리눅스 컨테이너 (1)

리눅스 컨테이너란?

 

리눅스 컨테이너(Linux Container)는 리눅스 커널이 제공하는 네임스페이스(namespace)와 cgroup(control groups) 등의 기능을 조합해 구현된 운영체제 수준의 가상화 기술입니다. 전통적인 하이퍼바이저 기반의 가상화가 하드웨어를 가상화하고 각 가상 머신(VM)이 자체 커널을 포함한 전체 운영체제를 구동하는 방식이라면, 컨테이너는 호스트 커널을 공유하면서도 사용자 공간(user space)은 독립적으로 분리된 실행 환경을 제공합니다.

 

기술적으로 보면, 컨테이너는 일종의 프로세스입니다. 하지만 이 프로세스는 외부의 다른 프로세스들과 PID, 파일 시스템, 네트워크, IPC, 사용자 정보 등의 리소스를 공유하지 않고, 마치 별도의 시스템처럼 동작하도록 구성됩니다. 이는 네임스페이스에 의해 가능한데, 각 네임스페이스는 특정 리소스의 가시성을 프로세스 단위로 제한합니다. 여기에 cgroup을 적용하면 CPU, 메모리, 디스크 I/O, 네트워크 대역폭 등 리소스 소비를 제한하거나 측정할 수 있습니다.

 

결과적으로 컨테이너는 리눅스 커널 위에서 실행되는 격리된 프로세스로, 독립성과 경량성, 이식성을 동시에 확보할 수 있는 실행 단위입니다.

 

컨테이너의 필요성

 

컨테이너의 핵심은 '격리(isolation)' 입니다. 격리는 단순히 다른 애플리케이션과 충돌을 피하기 위한 목적만 있는 것이 아니라, 보안, 성능, 안정성, 운영 효율성 전반에 영향을 미치는 중요한 속성입니다.

 

1. 보안 관점

컨테이너를 동일한 호스트에서 다수 실행할 경우, 서로 영향을 주지 않아야 합니다. 하나의 컨테이너에서 compromise(침해)가 발생하더라도, 다른 컨테이너나 호스트 전체로 침투하는 것을 막기 위해 리눅스는 namespace를 통해 시스템 컴포넌트를 논리적으로 격리하고, capability 시스템으로 커널 기능 접근 권한을 최소화합니다.

 

예를 들어, user namespace를 활용하면 컨테이너 내부에서는 루트 권한을 가지더라도, 실제 호스트에서는 비특권 사용자로 매핑되므로 잠재적인 공격 범위를 줄일 수 있습니다.

 

2. 리소스 관리

여러 개의 컨테이너가 단일 시스템 위에서 동작할 때, 하나의 컨테이너가 과도한 리소스를 사용할 경우 전체 시스템 성능에 영향을 줄 수 있습니다. 이를 방지하기 위해 cgroup을 사용해 리소스 소비를 제한하고 격리합니다. CPU 쿼터, 메모리 상한선 등을 설정함으로써 공정한 자원 분배와 성능 안정성을 확보할 수 있습니다.

 

3. 운영 환경 일관성

컨테이너는 애플리케이션 실행 환경 전체를 패키징할 수 있습니다. 파일 시스템 계층(OverlayFS 등)을 통해 애플리케이션, 의존성, 설정 등을 독립적으로 유지함으로써, 배포 대상 환경의 차이에 관계없이 "개발 환경 그대로의 실행 환경"을 보장합니다. 이 역시 다른 컨테이너와 파일 시스템을 공유하지 않도록 격리함으로써 가능해집니다.

 

 

결론적으로 컨테이너의 격리 메커니즘은 단순한 기능이 아니라, 멀티 테넌시 환경에서 보안을 유지하고, 시스템 자원을 효율적으로 할당하며, 안정적인 배포와 운영을 가능하게 만드는 필수 요소입니다. 이러한 격리는 가상 머신이 제공하는 것과 유사한 수준의 독립성을 제공하면서도, 훨씬 더 적은 오버헤드로 구현된다는 점에서 리눅스 컨테이너는 현대 인프라 구조의 핵심 컴포넌트로 자리잡고 있습니다.

* 멀티 테넌시(Multi-tenancy)는 하나의 시스템을 여러 사용자가 함께 사용하는 구조를 의미합니다.

 

컨테이너의 종류

 

컨테이너는 그 목적과 범위에 따라 애플리케이션 컨테이너시스템 컨테이너로 구분할 수 있습니다. 이 두 종류는 모두 리눅스 커널의 격리 기능을 활용하지만, 사용 목적과 구성 방식에 있어 분명한 차이를 가집니다.

 

애플리케이션 컨테이너 (Application Container)

애플리케이션 컨테이너는 하나의 애플리케이션 실행에 필요한 환경만을 최소 단위로 패키징한 컨테이너입니다. 일반적으로 특정 바이너리나 서비스 단위로 구성되며, 컨테이너 내부에는 애플리케이션과 그 의존성만 포함되고 운영체제의 전체 기능은 포함하지 않습니다.

 

이러한 구조는 컨테이너의 크기를 작게 만들고, 빠른 배포 및 확장이 가능하게 해줍니다. 또한 마이크로서비스 아키텍처와 같이 서비스 단위로 독립적인 배포와 스케일링이 요구되는 환경에 적합합니다.

 

대표적인 예로는 Docker, containerd, CRI-O 등이 있으며, 대부분 Kubernetes 환경에서 사용되는 컨테이너는 이 애플리케이션 컨테이너입니다.

 

시스템 컨테이너 (System Container)

시스템 컨테이너는 반대로 전체 운영체제 환경과 유사한 구조를 갖는 컨테이너입니다. 컨테이너 내부에는 *init 시스템이 존재할 수 있으며, 다양한 시스템 서비스(예: SSH, cron 등)도 함께 구동될 수 있습니다. 따라서 마치 가상 머신처럼 하나의 완전한 시스템 환경을 컨테이너 내부에 구축할 수 있습니다.

 

시스템 컨테이너는 일반적으로 기존 모놀리식 구조의 리프트 앤 시프트(Migration) 작업이나, 테스트 환경, CI/CD 파이프라인에서 전체 OS 수준의 격리가 필요한 경우에 유리합니다.

 

대표적인 기술로는 LXC(Linux Containers) 및 그 상위 관리 도구인 LXD가 있으며, 가상 머신과 컨테이너의 중간지점에 위치한 형태라 볼 수 있습니다.

* init 시스템이란 리눅스 시스템이 부팅될 때 가장 먼저 실행되는 PID 1번 프로세스입니다. 이 프로세스는 시스템 초기화를 담당하며 다양한 시스템 서비스를 실행하고 프로세스를 관리하는 핵심 역할을 합니다.
(애플리케이션 컨테이너에서는 PID 1번이 단일 애플리케이션입니다.)

 

 

 

프로세스 격리 기능

 

앞서 말한 것처럼, 컨테이너의 핵심은 격리입니다. 이제, 이 격리를 가능하게 해주는 리눅스 커널 기술들에 대해 살펴보겠습니다.

 

chroot

 

chroot 는 change root directory의 줄임말로 리눅스는 chroot 시스템 콜을 통해 프로세스의 루트 디렉터리를 변경할 수 있습니다. 이를 활용하면 특정 디렉터리를 루트(/)처럼 인식하게 만들 수 있으며, 컨테이너는 이러한 기능을 통해 자신만의 파일 시스템 루트를 가지게 됩니다.

 

보다 진보된 방식으로는 pivot_root를 사용하는 방법이 있으며, 이를 통해 완전한 파일 시스템 전환이 가능합니다. 이 격리를 통해 컨테이너 내부에서는 실제 호스트 파일 시스템의 존재를 알 수 없습니다.

 

namespace

 

리눅스의 네임스페이스는 프로세스가 인식할 수 있는 시스템 자원의 범위를 제한하는 기능입니다.

예를 들어, 하나의 컨테이너는 자신만의 프로세스 목록을 가지고 있고(PID namespace), 자신만의 네트워크 인터페이스를 사용하며(NET namespace), 독립된 마운트 포인트(MNT namespace)와 호스트명(UTS namespace)을 가질 수 있습니다.

 

네임스페이스는 총 8가지 유형이 존재하며, 컨테이너 환경에서는 이 중 pid, net, mnt, uts, ipc, user 네임스페이스가 주로 사용됩니다. 이로 인해 컨테이너는 다른 프로세스나 리소스를 인식하지 못하고, 마치 독립된 시스템처럼 동작하게 됩니다.

 

cgroup

 

cgroup은 control group의 줄임말로 리눅스 커널에서 제공하는 리소스 제어 기능입니다. 컨테이너마다 CPU, 메모리, 디스크 I/O, 네트워크 대역폭 등의 사용량을 제한하고 모니터링할 수 있게 해줍니다. 이를 통해 하나의 컨테이너가 과도한 자원을 사용하는 것을 방지하고, 시스템 전체의 안정성을 보장할 수 있습니다.

 

cgroup은 namespace와 함께 사용되어, 격리와 자원 통제를 동시에 제공하는 컨테이너 환경의 핵심 기술로 자리 잡고 있습니다.

 

capabilities

 

리눅스의 전통적인 권한 모델에서는 UID 0, 즉 root 사용자에게 모든 권한이 부여되었습니다. 그러나 이는 보안상 큰 리스크를 안고 있습니다. 이를 해결하기 위해 capabilities라는 개념이 도입되었으며, root 권한을 여러 개의 세분화된 권한 집합으로 분리할 수 있습니다.

 

컨테이너에서는 기본적으로 필요한 권한만 부여되고, 나머지는 제거되어 실행됩니다. 예를 들어, CAP_NET_ADMIN 권한이 없는 컨테이너는 네트워크 인터페이스를 조작할 수 없습니다. 이러한 제한을 통해 보안성을 크게 높일 수 있습니다.

 

union mount

 

컨테이너는 보통 읽기 전용 이미지 위에 쓰기 가능한 계층을 얹는 방식으로 파일 시스템을 구성합니다. 이를 가능하게 해주는 기술이 바로 union mount입니다.

 

대표적인 구현체로는 OverlayFS가 있으며, 여러 디렉터리를 하나의 통합된 파일 시스템처럼 마운트할 수 있습니다. 컨테이너는 공유된 읽기 전용 이미지(예: Ubuntu base image) 위에 개별 컨테이너의 쓰기 계층을 덧붙이기 때문에, 파일 변경이나 생성은 격리된 상태로 작동합니다.