September 23, 2025

[Container] Container 기본

[Container] Container 기본

Container

하드웨어를 가상화하는 전통적인 하이퍼바이저 가상화 대신 커널을 공유하여 운영체제 수준에서 격리를 진행합니다. 둘 다 사용해 보면 VM 에 비해 확실히 적은 리소스를 사용하며 빠르죠. 기본적으로 커널을 사용하므로 리눅스에 기반을 하고 있고 Namespacecgroups 을 사용합니다.

Namespace

네임스페이스는 격리 환경 기술입니다. 많은 종류의 네임스페이스가 있고, 여러 종류의 네임스페이스를 조합해서 컨테이너로 사용합니다.

  1. Mount (mnt)
    1. 파일시스템을 격리하여 독립된 루트(/) 파일시스템으로 호스트 시스템에는 영향을 주지 않게 합니다.
  2. PID
    1. 프로세스 ID 를 격리하여 외부의 프로세스를 보지 않습니다.
  3. Network
    1. 고유 veth, ip, port, route 등으로 각 컨테이너는 같은 포트를 사용할 수 있습니다.
  4. UTS
    1. hostname 을 격리합니다.
  5. IPC
    1. 프로세스간 통신을 격리하여 공유메모리나 세마포어를 격리합니다.
  6. User
    1. User/Group ID 를 격리합니다. 컨테이너 안에서의 root 여도 호스트에서는 권한 없는 사용자에 매핑하여 피해를 최소화합니다.
  7. cgroup
    1. control group 을 격리하여 리소스를 제한합니다.
  8. time
    1. 시스템 시간을 격리합니다.

lsns 로 현재 사용중인 모든 네임스페이스를 확인할 수 있습니다.

NPROCS(Number of Processes) 는 namespace 에서 실행되고 있는 프로세스의 개수입니다.
PID 는 해당 네임스페이스를 생성하거나 실행중인 프로세스 ID 입니다.

💡
현재 셸의 네임스페이스는 상속받은 상태입니다.
lsns 의 권한이 없어 user:chdr 인 값만 보이고 있습니다.
ls -l /proc/self/ns/sudo ls -l /proc/1/ns/ 의 값을 비교해보면 상속받았다는 것을 알 수 있습니다.

root 에서 lsns 로 살펴보면 PID 1로부터 시작하는 것을 알 수 있습니다.

보면 전용 네임스페이스에서 데몬들이 실행되고 있는데, 최소 권한으로 보안과 안정성을 가질 수 있기에 격리해서 사용합니다.

  1. systemd-udevd (user driver deamon)
    1. user driver 를 관리하는 deamon 입니다.
  2. systemd-resolved
    1. DNS 이름 해석을 관리하는 daemon 입니다.
  3. systemd-timesyncd
    1. 시스템 시간을 NTP 와 주기적으로 맞추는 역할을 하는 daemon 입니다.
  4. systemd-logind
    1. 사용자 로그인 및 세션 관리자 daemon 입니다.
      노트북 덮개, 절전 모드 등 하드웨어 접근 권한도 제어합니다.
  5. rsyslogd
    1. 시스템 로그를 기록하는 daemon 입니다.

Unshare

unshare 커맨드는 사용자가 새로운 네임스페이스에서 지정된 프로그램을 실행할 수 있게 합니다. 위에 나열된 격리할 수 있는 옵션들을 사용하면 해당 네임스페이스로 격리됩니다.

--net, --uts, --ipc, --user, --cgroup, --mount, --pid, --mount-proc, --fork

💡
pid, mount-proc 을 같이 사용해야 합니다.
pid 를 격리해도 mount-proc 을 하지 않으면 root 의 프로세스가 보입니다.
이는 ps, top 이 proc 을 읽어서 작동하기 때문인데, 반대로 mount-proc 만 해도 pid 격리가 안되어 root의 프로세스가 보입니다.
💡
fork 는 unshare 커맨드에서 자식 프로세스를 만들어 init 으로 작동시킵니다.
/bin/bash 와 보통 프로세스는 init 처럼 고아 프로세스를 정리하는 방법이 없습니다. 이런 시스템 콜이 없으면 fork 자체를 막아 오류가 발생합니다.
bash: fork: Cannot allocate memory

컨테이너 만들기

unshare 를 이용해서 contaienr 를 만들어 봅니다. bash 와 다른 패키지들을 미리 구성하기 위해 debootstrap 을 사용합니다.

sudo apt-get install -y util-linux debootstrap

mkdir my-container
sudo debootstrap stable ./my-container http://deb.debian.org/debian
sudo unshare --pid --net --uts --ipc --mount --mount-proc --fork chroot ./my-container /bin/bash 

unshare 로 네임스페이스를 만들고 chroot 로 원하는 폴더를 루트 디렉토리로 만들고, /bin/bash 를 수행합니다.

💡
mount 를 옵션을 주면 네임스페이스 안에서 디렉토리를 덮어씌울 수 있습니다. moint -t tmpfs none /tmp
기본적으로 호스트의 파일시스템에 접근 할 수 있기에 chroot를 포함해 여러 안전장치를 더합니다.
User 격리로 권한을 없애고, 필요한 시스템 콜을 제거하고, 프로세서의 시스템 콜을 차단하기도 합니다.

컨테이너 네트워크

unshare 에서 network 격리로 생성한 네임스페이스에는 루프백만 있습니다.
호스트나 외부와 통신하기 위해서는 다른 네트워크 설정이 필요합니다.

브릿지를 이용해서 컨테이너끼리 통신하고 마스킹과 포워딩으로 인터넷에 접속 가능하게 합니다.
sudo apt-get install bridge-utils iproute2

veth 연결

veth 쌍을 이용해서 host 와 연결하면 host 와 통신할 수 있습니다.

# 컨테이너 실행 & PID 가져오기
sudo unshare --pid --net --uts --ipc --mount --mount-proc --fork chroot ./my-container /bin/bash
CONTAINER_PID=$(ps aux | grep "[c]hroot ./my-container /bin/bash" | awk '{print $2}' | sort -n | tail -n 1)

# veth 쌍 생성 & 컨테이너 네임스페이스로 이동
sudo ip link add veth-host type veth peer name veth-container
sudo ip link set veth-container netns $CONTAINER_PID

# 호스트 네트워크 설정
sudo ip addr add 10.0.0.1/24 dev veth-host
sudo ip link set veth-host up

# 컨테이너 네트워크 설정
sudo nsenter -t $CONTAINER_PID -n ip addr add 10.0.0.2/24 dev veth-container
sudo nsenter -t $CONTAINER_PID -n ip link set veth-container up

# 루프백 활성화, default 게이트웨이 설정
sudo nsenter -t $CONTAINER_PID -n ip link set lo up
sudo nsenter -t $CONTAINER_PID -n ip route add default via 10.0.0.1

이제 ping 10.0.0.1 등으로 호스트와의 통신을 확인할 수 있습니다.
veth를 쌍으로 생성하여 container 가 꺼지면 호스트의 veth 도 삭제됩니다.

Bridge

호스트와의 통신은 했지만 다른 컨테이너와 그리고 인터넷으로의 통신은 되지 않았습니다.

브릿지를 통해 다른 컨테이너와 통신하고 NAT(MASQUERADE) 으로 인터넷에 접속할 수 있습니다.

브릿지 생성

sudo ip link add br0 type bridge           # br0 라는 브리지 생성
sudo ip addr add 172.17.0.1/16 dev br0     # 브리지에 IP 주소 할당
sudo ip link set br0 up                    # 브리지 활성
ip addr show br0                           # up 상태 확인
brctl show                                 # 브리지 상태 확인

컨테이너 간 통신

veth 를 각 컨테이너에 생성 후 호스트 veth 를 브릿지에 연결하면 됩니다.
브릿지를 스위치처럼 사용하여 통신을 하게 합니다.

Comments