콘텐츠로 이동

컨테이너 안티패턴 이해하기

개요

OS의 종류와 관계 없이, 오래된 애플리케이션을 클라우드 네이티브로 전환하려면 코드 수준에서부터 재검토를 철저히 진행해야 합니다. 그렇지 않을 경우, 다음의 문제점이 발생할 수 있습니다.

  • 복잡한 구성으로 인한 운영 난이도 상승, 시스템 안정성 저하: 레거시 애플리케이션이 정상적으로 작동할 수 있도록 호환성을 맞추는 과정에서 복잡한 구성이 필연적으로 들어갑니다. 이로 인해 운영 난이도가 상승하고 시스템 안정성이 저하되는 결과가 나타날 수 있습니다.
  • 단위 시간 당 처리할 수 있는 요청량 감소로 인한 수익 저하: 동일한 기능을 처리하기 위하여 소요되는 CPU 사이클 수, 소요되는 메모리 용량을 줄이기 위해서는 구현을 최적화하는 과정이 꼭 필요합니다. 안티패턴은 최적화를 위해서 일반적으로 해소해야 할 문제입니다.
  • 시스템 확장성 저하: 안티패턴 중에는 시스템 확장을 저해하는 요소가 포함될 수 있습니다. 시스템 확장을 원활하게 수용할 수 있도록 하기 위해서는 특정 머신의 상태에 의존하지 않는 코드 구현이 필요합니다.

이 문서에서는 위의 문제점에 연결될 수 있는 주된 안티패턴들을 살펴보고, 어떻게 수정하는 것이 바람직한지 살펴보도록 하겠습니다.

OS 종류와 무관한 일반적인 안티패턴

구성과 코드의 혼합

  • 문제: 구성 정보(예: 데이터베이스 연결 문자열, API 키 등)가 코드 내부에 하드코딩되어 있는 경우. 이는 컨테이너 환경에서 애플리케이션을 이식하거나 환경 간에 이동할 때 문제가 됩니다.
  • 해결: 환경 변수, 구성 파일, 시크릿 관리 시스템 등을 사용하여 구성 정보를 코드 외부에서 관리하도록 변경합니다.

비상태성의 부재

  • 문제: 웹 애플리케이션의 상태(예: 세션 정보)가 로컬 파일 시스템이나 인메모리에 저장되는 경우, 컨테이너가 재시작될 때 상태 정보를 잃게 됩니다.
  • 해결: 세션 정보와 같은 상태를 외부 저장소(예: 데이터베이스, 캐시 서비스 등)에 저장하여 애플리케이션을 상태 비저장(stateless)으로 만듭니다.

로깅과 모니터링의 부적절한 처리

  • 문제: 로그를 파일 시스템에 직접 쓰는 등의 방식은 컨테이너 환경에서 로그 관리를 어렵게 만듭니다.
  • 해결: 표준 출력(stdout) 및 표준 에러(stderr) 스트림을 사용하여 로그를 출력하고, 컨테이너 오케스트레이션 도구(예: Kubernetes) 또는 외부 로그 수집 도구를 통해 로그를 수집하고 관리합니다.

단일 컨테이너 내의 여러 책임

  • 문제: 단일 애플리케이션 컨테이너가 데이터베이스, 웹 서버, 배치 작업 등 여러 책임을 갖는 경우, 이는 컨테이너의 모듈성과 재사용성을 저해합니다.
  • 해결: 각 컨테이너가 단일 책임을 갖도록 애플리케이션을 분리하고, 각각을 독립적으로 배포 및 확장할 수 있도록 합니다.

비효율적인 종속성 관리

  • 문제: 필요하지 않은 라이브러리나 구식 라이브러리가 포함되어 있어, 이미지 크기가 커지고 보안 취약점이 생길 수 있습니다.
  • 해결: 애플리케이션의 종속성을 정기적으로 검토하고 최신 상태로 유지합니다. 가능하다면, 멀티 스테이지 빌드를 사용하여 최종 이미지 크기를 줄입니다.

하드코딩된 네트워크 설정

  • 문제: 애플리케이션이 특정 IP 주소나 포트에 직접적으로 의존하는 경우, 이는 컨테이너 환경에서 네트워킹 구성을 유연하게 변경하는 것을 방해합니다.
  • 해결: 서비스 발견 메커니즘을 사용하고, 네트워크 구성을 동적으로 관리할 수 있도록 합니다.

Windows 애플리케이션 구현 상의 안티패턴

경로 구분자의 차이

  • 문제: Windows는 파일 경로에 백슬래시(\)를 사용하는 반면, 리눅스와 다른 UNIX 기반 시스템은 슬래시(/)를 사용합니다. 경로 구분자가 하드코딩된 경우, 이는 크로스 플랫폼 이슈를 일으킬 수 있습니다.
  • 해결: 언어나 프레임워크가 제공하는 경로 조립 함수(예: Path.Combine — .NET, os.path.join — Python)를 사용하여 운영 체제 독립적인 방식으로 경로를 조립합니다.

대소문자 구분

  • 문제: Windows 파일 시스템은 대소문자를 구분하지 않지만, 리눅스는 구분합니다. 이로 인해, Windows에서는 문제가 없던 파일 접근 코드가 리눅스에서는 파일을 찾지 못하는 문제가 발생할 수 있습니다.
  • 해결: 파일과 디렉토리 이름을 일관되게 사용하고, 가능한 경우 모든 파일 접근을 대소문자 구분 없이 처리하도록 코드를 검토합니다.

시스템 호출 및 명령어의 차이

  • 문제: 특정 시스템 호출이나 명령어(예: dir vs ls, copy vs cp)는 운영 체제마다 다릅니다. 이러한 명령어를 직접적으로 코드 내에서 사용하는 경우, 크로스 플랫폼 호환성 문제가 발생할 수 있습니다.
  • 해결: 표준 라이브러리나 크로스 플랫폼을 지원하는 외부 라이브러리를 사용하거나, 필요한 경우 운영 체제를 확인하고 조건부로 다른 명령어를 실행하는 로직을 구현합니다.

레지스트리 의존성

  • 문제: Windows 애플리케이션이 시스템 레지스트리에 의존하는 경우, 이러한 의존성은 리눅스 시스템에서는 직접적인 대응이 없습니다.
  • 해결: 애플리케이션의 구성 정보를 레지스트리 대신 외부 파일이나 환경 변수 등으로 관리하도록 변경합니다.

인증 및 권한 관리의 차이

  • 문제: Windows 기반의 인증 방식(예: Active Directory)는 리눅스 시스템에서는 다르게 작동하거나, 직접적인 대응이 없을 수 있습니다.
  • 해결: 표준화된 인증 방식(예: OAuth, OpenID Connect)을 사용하거나, 크로스 플랫폼을 지원하는 인증 솔루션을 도입합니다.

특정 API나 라이브러리의 운영 체제 종속성

  • 문제: 특정 운영 체제에만 존재하는 API나 라이브러리에 의존하는 경우, 이식성이 제한됩니다.
  • 해결: 가능한 한 표준 API나 크로스 플랫폼을 지원하는 라이브러리를 사용합니다. 필요한 경우, 운영 체제에 따라 다른 구현을 제공하는 추상화 레이어를 구현합니다.