02_리눅스 컨테이너 기초와 작동 원리
1. 들어가며
컨테이너는 가상머신(VM)과 달리 하드웨어를 가상화하거나 별도의 Guest OS를 구동하지 않습니다. 대신 호스트 OS의 커널을 공유하며 프로세스를 논리적으로 격리하여 실행합니다.
최근 많은 기업은 마이크로서비스 워크로드를 애플리케이션 컨테이너로 운영합니다. 애플리케이션 컨테이너는 서비스 실행에 필요한 바이너리, 라이브러리, 런타임 의존성을 이미지로 패키징해 배포 일관성과 이식성을 높입니다.
"내 컴퓨터에서는 됐는데 왜 서버에선 안 돼?"와 같은 상황을 해결합니다.
하나의 호스트에서 수십 개의 컨테이너가 독립된 환경처럼 작동하게 하는 핵심 기술은 리눅스 커널에 이미 있는 세 가지 기능을 조합하여 구현됩니다. 이 글에서는 격리와 자원 통제 관점에서 핵심이 되는 Namespaces, cgroups, chroot/pivot_root를 중심으로 컨테이너가 어떻게 동작하는지 설명합니다.
2. Linux Namespaces — 가시성 격리
Linux Namespaces는 프로세스별로 독립된 시스템 환경에 있는 것처럼 가시성을 격리해주는 리눅스 커널 기능입니다. 컨테이너 안의 프로세스는 마치 자신만 존재하는 OS에서 실행되는 것처럼 동작하며, 호스트의 다른 컨테이너와 프로세스를 볼 수 없습니다.
쿠버네티스 환경에서 Pod 내부 구조를 이해하거나 네트워크를 트러블슈팅할 때 반드시 알아야 하는 핵심 개념입니다.
최초의 Namespace는 2002년 Linux 2.4.19 커널에 Mount Namespace로 도입되었으며, 현재 Linux에서는 여러 종류의 네임스페이스가 지원되고 있습니다:
- Process ID(pid): 프로세스 ID를 격리합니다. 컨테이너 내부의 최초 실행 프로세스는 PID 1을 할당받습니다. 호스트에서 12345번으로 도는 프로세스라도, 컨테이너 내부에서는 자신을 1번(init) 프로세스라고 인식하게 됩니다.
- Network(net): 네트워크 인터페이스, IP 주소, 라우팅 테이블, 포트 번호 등을 격리합니다. 각 컨테이너가 자신만의 독립적인 네트워크 인터페이스와 IP를 가질 수 있는 이유입니다.
- Mount Point(mnt): 파일 시스템 마운트 포인트를 격리합니다. 컨테이너 안에서 USB를 마운트해도 호스트나 다른 컨테이너에는 보이지 않습니다.
- Inter-Process Communication(ipc): 프로세스 간 통신(Shared Memory, Semaphore, Message Queue 등)을 격리하여, 다른 컨테이너의 프로세스 메모리에 접근하는 것을 막습니다.
- Unix Time Sharing System(uts): 호스트네임(Hostname)과 도메인 네임을 격리합니다. 실행 중인 호스트 이름과 독립적으로 컨테이너의 호스트네임(Hostname)과 도메인 네임을 변경할 수 있습니다.
- User(user): 사용자 및 그룹 ID를 격리합니다. 컨테이너 내부의
root사용자는 호스트에서는 권한이 없는 일반 사용자 계정에 매핑하여 권한 상승 공격을 방지합니다.
3. Linux cgroups — 자원 제어
cgroup은 프로세스가 사용할 수 있는 메모리, CPU, 블록 I/O 등의 자원 사용량을 할당 및 제한하는 역할을 합니다. Linux에서 컨테이너는 프로세스로서 실행되므로, cgroups를 사용하여 각 컨테이너에 사용되는 리소스를 제한할 수 있습니다.
Linux에서 직접 cgroup을 생성하고 관리할 수도 있지만, 컨테이너를 사용하는 경우 컨테이너 런타임이 이를 대신 처리합니다.
쿠버네티스 Pod 스펙의
resources.limits는 런타임에서 cgroup 제한으로 직접 연결됩니다.
- CPU: 코어 수 또는 사용 비율을 제한
- Memory: 최대 사용 가능한 메모리 크기를 제한
- Block I/O: 디스크 읽기/쓰기 속도를 제한
4. chroot / pivot_root — Root 디렉토리 변경
Namespaces로 프로세스를 격리하더라도, 파일시스템까지 격리되지 않으면 컨테이너 밖의 파일에 접근할 수 있습니다. chroot와 pivot_root는 개별 컨테이너 프로세스 간의 파일시스템 경계를 만듭니다.
chroot는 프로세스가 인식하는 루트 디렉토리(/)를 특정 디렉토리로 변경하는 시스템 콜입니다. chroot jail에 갇힌 프로세스는 자신에게 할당된 디렉토리 밖을 볼 수도, 접근할 수도 없습니다. 컨테이너 내부에 접근하면 호스트 OS의 전체 파일 시스템이 아닌 하위 집합만 표시됩니다. 이는 컨테이너 프로세스가 실행될 때 chroot 시스템콜을 통해 프로세스를 감옥(chroot jail)에 가두기 때문입니다. 감옥에 갇힌 프로세스는 자신에게 할당된 디렉토리 밖의 파일 시스템을 볼 수도, 접근할 수도 없게 됩니다.
pivot_root는 프로세스의 루트 파일시스템 자체를 다른 마운트로 전환합니다. 현대 컨테이너 런타임은 일반적으로 mount namespace와 pivot_root, bind mounts를 함께 사용해 컨테이너 전용 rootfs을 구성하여, chroot보다 더 안정적으로 호스트 파일 시스템과의 연결을 끊어버립니다.
5. 요약
컨테이너는 가상머신처럼 별도 OS를 띄우지 않으면서 리눅스 커널의 격리/제어 기능을 결합해 애플리케이션 실행 환경을 독립적으로 구성하는 방식입니다.
리눅스 컨테이너는 아래 3가지 기술을 중심으로 동작합니다:
| 기술 | 역할 | 설명 |
|---|---|---|
| Namespaces | 자원 격리 | 시스템 리소스를 논리적으로 그룹화하여 프로세스 간 가시성을 격리합니다. |
| cgroups | 자원 관리/제어 | 프로세스가 사용할 CPU, 메모리, I/O 같은 물리 자원 사용량을 제한합니다. |
| chroot/pivot_root | 루트 파일 시스템 변경 | 프로세스가 바라보는 최상위 루트 파일 시스템 범위를 분리합니다. |