DevOps/Linux

Linux 환경에서 간단한 컨테이너 직접 만들어 보기

IT록흐 2025. 6. 12. 21:58
반응형

 

 

Linux에서 컨테이너를 만들어 보자. 

컨테이너의 핵심은 '격리(isolation)'이다.  여기서 2가지 격리를 구현해야 한다. 

 

1) 파일시스템 격리 -> chroot

2) 프로세스 격리 -> unshare ( namespace )

 

 

파일시스템 격리

 

파일시스템 격리는 chroot로 구현된다. 

 

chroot는 root의 경로가 변경된 또 다른 쉘(shell)을 띄우는 것이다. 터미널로 접속하면 우리는 정해진 위치를 root로 하여 쉘(shell) 프로그램을 구동하여 터미널에 접속한다. chroot는 또 다른 경로를 root로 하여 쉘을 구동한다. 쉘이 바라보는 루트는 달라지지만 쉘이 커널로 전송하는 경로는 동일하다. 그러므로 달라진 루트 위치에 bin폴더, proc 폴더, lib 폴더를 복사 및 생성하면 마치 서버 안에 또 다른 서버가 생성된 것처럼 보이게 만들 수 있다. 

 

#/bin/bash

# root로 지정할 디렉토리 생성
mkdir -p /home/namespace/box

# binary 파일 및 lib 복사하기
mkdir -p /home/namespace/box/bin
mkdir -p /home/namespace/box/lib
mkdir -p /home/namespace/box/lib64

cp -v /usr/bin/kill /home/namespace/box/bin/
cp -v /usr/bin/ps /home/namespace/box/bin/
cp -v /bin/bash /home/namespace/box/bin/
cp -v /bin/ls /home/namespace/box/bin/
cp -v /usr/sbin/ip /home/namespace/box/bin/
cp -r /lib/* /home/namespace/box/lib/
cp -r /lib64/* /home/namespace/box/lib64/


# binary 파일이 접근 할 proc 디렉토리 생성하기
# proc 디렉토리는 커널 내부 정보를 파일형태로 제공 
mkdir -p /home/namespace/box/proc
mount -t proc proc /home/namespace/box/proc


# root 경로 지정하여 bash쉘 실행하기
chroot /home/namespace/box /bin/bash

 

 

위 쉘을 실행하면, 컨테이너에 필요한 binary 파일이 생성되고 binary 파일들이 필요한 커널 내부 정보를 proc 디렉토리를 통해 제공할 수 있다. 그리고 chroot를 통해 지정된 경로를 root로 인식하는 bash쉘을 실행할 수 있다. 

 

하지만 문제가 있다. 

 

파일시스템은 격리되었지만 격리라고 볼 수가 없다. 왜냐하면 호스트 프로세스가 컨테이너 프로세스를 실행하였기 때문에 호스트와 컨테이너는 부모-자식 관계로 동일한 네임스페이스를 공유한다. 동일한 네임스페이스를 공유하기에, 커널이 proc 디렉토리로 호스트와 동일한 프로세스 정보를 공유하여 자식도 부모처럼 같은 프로세스 정보를 볼 수 있다. 다시말하여 자식인 컨테이너가 호스트에서 동작 중인 프로세스에 영향을 줄 수 있다. 심지어 kill 도 할 수 있는 것이다. 

 

 

프로세스 격리 ( unshare )

 

자식이 부모의 프로세스 정보를 볼 수 없도록 네임스페이스를 생성해서 격리해야 한다. 

 

#/bin/bash

mkdir -p /home/namespace/box
mkdir -p /home/namespace/box/bin
mkdir -p /home/namespace/box/lib
mkdir -p /home/namespace/box/lib64

cp -v /usr/bin/kill /home/namespace/box/bin/
cp -v /usr/bin/ps /home/namespace/box/bin/
cp -v /bin/bash /home/namespace/box/bin/
cp -v /bin/ls /home/namespace/box/bin/
cp -v /usr/sbin/ip /home/namespace/box/bin/
cp -r /lib/* /home/namespace/box/lib/
cp -r /lib64/* /home/namespace/box/lib64/

# proc 디렉토리 생성하기
mkdir -p /home/namespace/box/proc

# unshare 명령어로 네임스페이스 따로 생성해서 chroot 프로세스 실행하기 
unshare -p -n -f --mount-proc=/home/namespace/box/proc chroot /home/namespace/box /bin/bash

 

 

unshare 명령어를 사용하면 chroot로 실행되는 /bin/bash는 호스트랑은 다른 네임스페이스를 공유하게 된다. 

 

-p : pid 네임스페이스 격리 

-n : 네트워크 인터페이스 격리

-f : 자식프로세스 생성 후 네임스페이스 격리하기

--mount-proc : 호스트와 분리된 네임스페스 전용 /proc 제공하기 

 

bash-5.2# ps -ef 
UID          PID    PPID  C STIME TTY          TIME CMD
0              1       0  0 12:09 ?        00:00:00 /bin/bash
0              2       1 99 12:09 ?        00:00:00 ps -ef
bash-5.2# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
    link/tunnel6 :: brd :: permaddr ba76:c2cd:ea6d::

 

 

그럼 위와 같이, chroot로 실행된 /bin/bash가 1번 프로세스 PID가 되고 네트워크도 격리된 가상의 네트워크가 형성된다. 이로써 컨테이너와 호스트의 프로세스가 격리된 환경이 구현되었다. 

 

격리되었다고 해도 자식이 부모를 못 볼 뿐이지 부모는 자식을 볼 수 있다. 프로세스는 트리 방식의 계층구조로 실행되기 때문에 자식 전용 네임스페이스도 부모(호스트) 단에서 확인이 가능하다. 

 

root@kind-control-plane:/proc/1019# cat status
Name:	coredns
//중략.... 
NStgid:	1019	1
NSpid:	1019	1
NSpgid:	1019	1
NSsid:	1019	1

 

 

coredns는 kubernetes에서 dns를 담당하는 파드이다. coreDNS 프로세스는 컨테이너 안에서 프로세스 PID는 1번이지만 호스트 노드에서 1019이다. 이렇듯 부모인 호스트는 자식인 컨테이너 안의 네임스페이스도 확인이 가능하다.

 

리눅스 커널은 프로세스가 데이터 처리에 필요한 논리적인 리소스들을 격리하는 네임스페이스 기능을 제공하여 프로세스 간 보안 기능을 구현한다. 

 

PID namespace 프로세스 ID 공간 (논리적 PID) 데이터를 처리하는 프로세스 격리
NET namespace 네트워크 인터페이스, 라우팅 테이블 등 데이터가 들어오는 입구 격리
MNT namespace 마운트된 파일시스템 트리 데이터가 오가는 디렉토리 격리
IPC namespace 공유 메모리, 세마포어, 메시지큐 등 데이터를 공유하는 메모리 격리
USER namespace UID/GID 매핑 데이터에 접근하는 유저 격리
 
 
데이터가 들어오는 입구, 데이터 저장 경로, 공유 메모리, 접근하는 유저 등등 데이터를 처리하는데 필요한 개념을 네임스페이스라는 공간으로 가두어 격리시키고 다른 네임스페이스에 영향을 주지 못하도록 격리시킨다. 
 

 

 

chroot로 파일시스템을 격리하고

unshare로 네임스페이스를 생성하여 프로세스를 격리하면 

기본적이고 간단한 컨테이너를 직접 구현할 수 있다. 

 

 

 

참고자료

https://product.kyobobook.co.kr/detail/S000203332747

 

코어 쿠버네티스 | 제이 비아스 - 교보문고

코어 쿠버네티스 | 쿠버네티스의 핵심 사항을 이해하려면 반드시 이 책을 읽어 보자!현실에서 쿠버네티스의 배포는 힘든 작업입니다. 작은 구성 오류나 설계 문제도 시스템을 망칠 수 있습니다.

product.kyobobook.co.kr

 

반응형