컨테이너는 Docker와 Kubernetes의 바탕이 되는 기술입니다. 이 기술은 리눅스 커널의 네임스페이스(namespace)와 리눅스 컨트롤 그룹(cgroup)을 이용해 자원을 격리하고 제어합니다. 컨테이너는 이 두 기능으로 단순한 이미지 실행 환경을 넘어서 리눅스의 강력한 자원 격리와 제한 기능을 활용합니다. 이 글에서는 리눅스 커널의 네임스페이스와 cgroup의 개념을 살펴보고, 두 기능의 사용법을 실습 예제와 함께 알아보겠습니다.
이 글은 AWS EC2의 Ubuntu 20.04 LTS 환경을 기준으로 작성했습니다.
컨테이너 격리의 핵심: 리눅스 커널의 네임스페이스
리눅스 커널의 네임스페이스는 커널 자원을 분할해 프로세스마다 격리된 시스템 환경을 제공하는 중요한 커널 기능입니다. 예를 들어, 하나의 건물 안에 여러 개의 독립된 방이 있는 것처럼, 네임스페이스로 컨테이너 내부의 프로세스가 호스트 시스템이나 다른 컨테이너 자원에 접근하지 못하도록 논리적 경계를 설정합니다. 이러한 격리 기능 덕분에 여러 컨테이너가 동일한 호스트에서 안전하고 효율적으로 실행될 수 있습니다.
주요 네임스페이스 종류
리눅스 커널은 다양한 네임스페이스를 제공하며, 각각 특정 자원을 격리합니다. 컨테이너 기술에서 주로 사용하는 8가지 네임스페이스는 다음과 같습니다.
- 마운트(mnt): 독립적인 파일 시스템 환경을 위한 마운트 지점 격리
- 프로세스 ID(pid): 컨테이너 내부의 프로세스를 독립적으로 관리하기 위한 PID 공간 분리
- 네트워크(net): IP 주소와 포트 등 독립적인 네트워크 스택 제공
- IPC: 프로세스 간 통신(IPC) 자원의 격리
- UTS: 호스트 이름과 도메인 이름의 격리
- 사용자(user): 사용자와 그룹 ID의 격리, 호스트 시스템에서 root 권한 제한
- cgroup: 자원 제어 그룹 계층 구조 격리
- 타임(time): 시스템 시간을 독립적으로 설정하도록 지원
unshare
명령어를 사용하면 원하는 네임스페이스를 생성해 지정한 프로그램을 격리된 환경에서 실행할 수 있습니다. 또 다양한 옵션으로 필요한 네임스페이스를 자유롭게 설정할 수 있습니다.
네임스페이스 격리 실습
간단한 예시로 PID 네임스페이스의 격리를 확인하겠습니다. 아래는 해당 명령어와 출력 예시입니다.
$ sudo unshare --pid --fork --mount-proc ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 10540 3128 pts/0 R+ 09:33 0:00 ps aux
위 명령어는 새로운 PID 네임스페이스를 생성하고 /proc
파일 시스템을 마운트한 뒤, ps aux
를 실행합니다. 명령어 아래 출력 예시에서 보듯, 이 새로운 네임스페이스 안에서는 격리된 프로세스 목록만 확인할 수 있으며, 이때 프로세스는 PID 1부터 시작하는 독립된 프로세스 트리를 갖습니다.
네트워크 격리의 핵심: 네트워크 네임스페이스와 가상 이더넷(veth) 쌍
네트워크 네임스페이스는 각 컨테이너에 독립적인 네트워크 환경을 제공하는 핵심 기능입니다. 각 네임스페이스는 자체 네트워크 인터페이스, IP 주소, 라우팅 테이블, 방화벽 규칙이 있습니다. 이로써 여러 컨테이너가 한 호스트 위에서 동시에 동작해도 서로 네트워크 설정에 간섭받지 않습니다.
가상 이더넷(veth: virtual ethernet) 쌍은 네임스페이스 간 통신을 구성할 때 사용됩니다. veth는 두 개의 가상 네트워크 인터페이스로, 한쪽에서 전송한 패킷이 곧바로 다른 쪽에 수신됩니다. 이를 이용하면 한쪽 인터페이스를 컨테이너의 네트워크 네임스페이스에, 나머지 한쪽을 호스트나 다른 컨테이너 네임스페이스에 연결해 컨테이너와 호스트 간 또는 컨테이너 간에 네트워크 통신을 설정할 수 있습니다.
리눅스 네트워크 네임스페이스 환경에서 컨테이너 간 통신 구조네트워크 네임스페이스 생성, veth 인터페이스 구성 실습
지금부터 네트워크 네임스페이스 두 개를 만들고, veth 쌍을 생성해 각각 연결하겠습니다. 네트워크 네임스페이스와 veth 인터페이스를 수동으로 구성해 리눅스 기반 컨테이너 네트워크가 격리된 환경을 만들고, 서로 통신하도록 연결되는 방식을 직접 확인할 수 있습니다.
-
네트워크 네임스페이스 생성: 독립적인 네트워크 환경을 테스트하기 위해 두 개의 네임스페이스(ns1, ns2)를 생성합니다.
sudo ip netns add ns1
sudo ip netns add ns2 -
veth 쌍 생성, 네임스페이스 연결: 네임스페이스 간에 연결할 veth 쌍을 만들고, 각 네임스페이스에 인터페이스를 할당합니다.
# ns1용
sudo ip link add veth-host1 type veth peer name veth-ns1
sudo ip link set veth-ns1 netns ns1
# ns2용
sudo ip link add veth-host2 type veth peer name veth-ns2
sudo ip link set veth-ns2 netns ns2 -
네트워크 설정: 각 네임스페이스 내부에서 veth 인터페이스를 활성화하고, IP 주소와 기본 라우트를 설정합니다.
sudo ip netns exec ns1 ip link set veth-ns1 up
sudo ip netns exec ns1 ip addr add 10.0.0.101/24 dev veth-ns1
sudo ip netns exec ns1 ip route add default via 10.0.0.1
sudo ip netns exec ns2 ip link set veth-ns2 up
sudo ip netns exec ns2 ip addr add 10.0.0.102/24 dev veth-ns2
sudo ip netns exec ns2 ip route add default via 10.0.0.1
이후 단계에서는 bridge-utils
를 사용해 브리지 네트워크를 구성하고, 실제 Docker 네트워크의 동작 방식을 함께 이해할 수 있습니다.
브리지 생성, veth 연결 실습
이어서 브리지를 생성하고, veth를 브리지에 연결하겠습니다. 브리지는 여러 네트워크 인터페이스를 하나의 네트워크 세그먼트로 묶는 가상 스위치 역할을 합니다. 컨테이너 간 통신을 위해 각 veth 인터페이스를 브리지에 연결해야 합니다.
-
브리지 생성, 활성화: 가상 브리지
br0
를 생성하고 활성화한 뒤, 호스트에서 사용할 IP 주소를 설정합니다.sudo ip link add br0 type bridge
sudo ip link set br0 up
sudo ip addr add 10.0.0.1/24 dev br0 -
veth를 브리지에 연결, 활성화: 호스트에 위치한 veth 인터페이스를 브리지에 연결하고, 동일한 네트워크 세그먼트에 포함합니다.
sudo ip link set veth-host1 master br0
sudo ip link set veth-host2 master br0
sudo ip link set veth-host1 up
sudo ip link set veth-host2 up
이제 두 네임스페이스가 br0
브리지로 동일한 네트워크에 연결됐습니다. 다음 단계에서는 ping
명령어로 통신이 가능한지 확인할 수 있습니다.
통신 테스트 실습
앞서 구성한 네트워크 구조의 정상 동작 여부를 확인하기 위해 ping 테스트를 진행하겠습니다.
-
ns1 → 브리지 (
10.0.0.1
):ns1
네임스페이스에서 브리지 IP로 ping을 실행해 통신 가능 여부를 확인합니다.sudo ip netns exec ns1 ping 10.0.0.1
-
ns1 → ns2 (
10.0.0.102
):ns1
에서ns2
로 직접 패킷을 전송합니다.sudo ip netns exec ns1 ping 10.0.0.102
-
호스트(브리지) → ns1 (
10.0.0.101
): 호스트(브리지가 위치한 네임스페이스)에서ns1
주소로 ping을 보내며 상호 통신 가능 여부를 확인합니다.ping 10.0.0.101
-
ns2 → ns1 (
10.0.0.101
):ns2
에서도ns1
로 ping을 시도해 양방향 통신 가능 여부를 확인합니다.sudo ip netns exec ns2 ping 10.0.0.101
모든 ping 응답이 정상적으로 돌아오면, 두 네임스페이스 간 통신과 호스트-네임스페이스 간 통신이 모두 올바르게 설정됐다는 의미입니다.
위 실습으로 네트워크 네임스페이스와 veth, 가상 브리지로 구성한 격리된 가상 네트워크 환경이 정상적으로 작동하는 걸 확인할 수 있습니다.
자원 관리의 핵심: 리눅스 컨트롤 그룹(cgroups)
리눅스 컨트롤 그룹(cgroups)은 프로세스 그룹의 자원 사용을 제어하고 제한하는 핵심 커널 기능입니다. cgroups로 CPU, 메모리, 디스크 I/O 등 시스템 자원을 효율적으로 제한·측정해 프로세스 그룹별로 격리할 수 있습니다. cgroups는 계층 구조를 기반으로, 부모 cgroup 아래에 여러 자식 cgroup을 생성해 각각 다른 자원 제한을 부여할 수 있습니다. 이 구조는 멀티 테넌트 환경에서 특정 프로세스의 자원 독점을 방지하고, 컨테이너별 자원 할당의 안정성 보장에 도움이 됩니다.