Docker란 무엇인가: 이미지·컨테이너·VM 차이와 격리 구조

반응형

Docker를 처음 접하면 컨테이너를 “가벼운 가상 머신”이라고 설명하기 쉽다. 실행 환경을 격리한다는 공통점은 있지만 내부 구조는 다르다. 컨테이너는 게스트 운영체제 한 대가 아니라, 격리된 프로세스와 그 실행에 필요한 파일·설정의 묶음이다.

이 차이를 이해하면 이미지와 컨테이너의 관계, 실행 속도가 빠른 이유, 보안 경계까지 함께 정리된다.

이미지와 컨테이너는 무엇이 다른가

Docker 이미지는 애플리케이션 실행에 필요한 파일 시스템 계층과 설정 메타데이터를 담은 읽기 전용 템플릿이다. 단순히 하나의 압축 파일이라고 보기보다, 재사용 가능한 여러 읽기 전용 layer와 이미지 설정의 조합으로 보는 편이 정확하다.

컨테이너를 만들면 이미지 위에 쓰기 가능한 layer와 런타임 설정이 추가된다.

Dockerfile과 빌드 입력
        ↓ build
읽기 전용 이미지 layers + 설정
        ↓ create/run
쓰기 가능한 layer + 프로세스 + 네트워크/마운트 설정
        = 컨테이너

같은 이미지로 여러 컨테이너를 만들 수 있고, 각 컨테이너의 쓰기 layer는 서로 다르다. 컨테이너를 지우면 그 layer의 변경도 사라지므로 영속 데이터는 volume이나 외부 저장소로 분리한다.

동일한 이미지는 애플리케이션과 의존성의 차이를 줄여 주지만 모든 환경을 완전히 동일하게 만드는 것은 아니다. 호스트 커널, CPU 아키텍처, 외부 데이터베이스, 네트워크, 비밀 값과 런타임 설정은 여전히 결과에 영향을 준다.

컨테이너와 VM의 경계

구분 컨테이너 가상 머신
실행 단위 격리된 프로세스 게스트 운영체제와 그 안의 프로세스
커널 호스트의 커널을 공유 VM마다 게스트 커널 사용
이미지 구성 앱·런타임·라이브러리 중심 운영체제 전체 이미지 포함 가능
시작 보통 프로세스 시작에 가까움 게스트 OS 부팅 필요
격리 경계 커널의 격리 기능에 의존 하이퍼바이저와 별도 커널 경계

Linux 호스트의 Linux 컨테이너는 호스트 Linux 커널을 공유한다. macOS와 Windows의 Docker Desktop에서 Linux 컨테이너를 실행할 때는 내부 Linux VM이 필요하다. 따라서 “컨테이너에는 VM이 전혀 없다”는 설명도 환경에 따라 틀릴 수 있다.

컨테이너가 항상 VM보다 안전하거나, VM이 항상 더 느리다고 단정할 수도 없다. 워크로드와 격리 요구, 운영 방식에 따라 선택이 달라진다.

격리는 namespace와 cgroup에서 시작한다

Linux 컨테이너의 격리는 주로 커널 기능을 조합해 만든다.

  • namespace는 프로세스 ID, 네트워크 인터페이스, 마운트, 호스트 이름 등 프로세스가 보는 시스템 자원을 분리한다.
  • cgroup은 CPU, 메모리, I/O, 프로세스 수 등의 사용량을 측정하고 제한하거나 우선순위를 조정한다.
  • capability는 root 권한을 더 작은 단위로 나눠 필요한 권한만 부여할 수 있게 한다.
  • seccomp, AppArmor, SELinux 같은 정책은 허용되는 시스템 콜과 자원 접근을 더 좁힐 수 있다.

namespace가 있다고 파일과 네트워크가 자동으로 안전해지는 것은 아니다. 어떤 namespace와 mount, capability를 부여했는지에 따라 격리 범위가 달라진다. cgroup도 네트워크 보안 정책을 대신하지 않는다.

컨테이너는 한 프로세스만 실행해야 할까

컨테이너는 주 프로세스를 중심으로 생명주기가 관리된다. 그래서 한 컨테이너에 한 가지 서비스나 책임을 두는 방식이 로그, 확장, 장애 격리에 유리한 경우가 많다.

하지만 “프로세스가 정확히 하나여야 한다”는 Docker의 강제 규칙은 아니다. 웹 서버가 여러 worker 프로세스를 띄울 수 있고, 함께 관리해야 하는 보조 프로세스가 있을 수도 있다. 중요한 것은 관련 없는 여러 서비스를 한 컨테이너에 무리하게 묶지 않고, 종료 신호와 프로세스 회수, 헬스 체크를 명확히 관리하는 것이다.

여러 호스트에 컨테이너를 배치하고 장애 복구, 롤링 업데이트, 서비스 디스커버리를 처리하려면 Docker 실행만으로는 부족하다. Kubernetes 같은 오케스트레이터나 클라우드의 관리형 컨테이너 서비스가 그 역할을 맡는다.

최소한의 보안 기준

컨테이너는 호스트 커널을 공유하므로 이미지 안만 살펴서는 충분하지 않다. 개발 환경에서도 다음 기준을 습관으로 두는 편이 좋다.

  1. 검증된 최소 이미지에서 시작하고 이미지와 호스트를 꾸준히 패치한다.
  2. 가능하면 root가 아닌 사용자로 실행한다.
  3. --privileged를 피하고 불필요한 capability를 제거한다.
  4. 필요한 경로만 쓰기 가능하게 하고 나머지 파일 시스템은 읽기 전용을 검토한다.
  5. 비밀 값을 이미지 layer나 Dockerfile에 넣지 않는다.
  6. seccomp와 AppArmor·SELinux 등 플랫폼의 보안 기능을 끄지 않는다.
  7. CPU와 메모리 제한을 설정해 한 컨테이너의 과도한 사용이 호스트 전체로 번지지 않게 한다.

컨테이너 탈출 가능성, 과도한 권한, 취약한 이미지, 공개된 Docker 소켓은 모두 호스트까지 영향을 줄 수 있다. “격리됐다”는 말과 “신뢰할 수 있다”는 말은 같지 않다.

정리하면 Docker는 애플리케이션과 의존성을 이미지로 패키징하고, 호스트 커널의 격리 기능을 이용해 컨테이너 프로세스로 실행하는 도구와 생태계다. 다음 단계인 생성·시작·정지·삭제의 차이는 Docker 컨테이너 생명주기에서 이어서 볼 수 있다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글