TL;DR
- 컨테이너는 리눅스 커널의 독립된 기능이 아니라 네임스페이스(Namespaces) 등 여러 리눅스 도구를 조합해 별도 머신처럼 보이게 하는 환경임.
- Docker 이미지는 파일시스템 레이어로 구성되며, 여러 이미지 레이어의 내용이 겹쳐져 최종 파일시스템 뷰를 만듦.
- 컨테이너가 분리해야 하는 핵심 요소는 네트워크 스택, 파일시스템, 프로세스임.
unshare,pivot_root, 마운트,veth장치를 조합하면 별도의 프로세스·파일시스템·네트워크 환경을 직접 구성할 수 있음.- 직접 만든 환경은 Docker와 유사하지만, 변경 사항을 기본 이미지와 분리하는 오버레이 파일시스템이 빠져 있음.
Docker 이미지는 무엇인가?
- 예시 이미지로 가볍고 보안·단순성·자원 효율성을 고려해 설계된
musl및BusyBox기반 리눅스 배포판인 Alpine Linux 3.12.0을 사용함. docker pull alpine:3.12.0으로 이미지를 내려받고docker save로alpine-3.12.0.tar파일에 저장함.- 저장된 이미지 아카이브에는 컨테이너 속성을 설명하는
manifest.json, 이미지의 SHA-256 값이 담긴 JSON 파일, 이미지 레이어 디렉터리,repositories등이 포함됨. - 레이어의
layer.tar를 살펴보면bin,usr,var등을 포함하는 완전한 리눅스 파일시스템 구조가 확인됨. - 이미지 레이어는 최소한
Dockerfile을 만드는 과정의 개별 단계를 나타내며, 여러 이미지의 내용은 겹쳐져 최종 파일시스템 뷰를 구성함. - 오버레이 파일시스템(overlay filesystem)의 세부 원리는 더 살펴볼 수 있으나, 여기서는 레이어가 이미지 구성 단계별 파일시스템이라는 점이 핵심임.
컨테이너란 무엇인가?
- 리눅스 커널에는 ‘컨테이너’라는 개념 자체가 없으며, 여러 리눅스 도구를 함께 적용한 결과가 컨테이너처럼 보이는 환경임.
- 컨테이너는 여러 개가 하나의 커널을 공유하더라도 별도의 머신이나 가상 머신처럼 보여야 함.
- 최소한 네트워크 스택, 파일시스템, 프로세스가 각각 격리돼야 함.
- 리눅스는 네임스페이스(Namespaces)를 통해 이런 격리를 제공하며, 간단한 셸 명령으로 격리 환경을 직접 구성할 수 있음.
가상 머신 설정
- 이후 명령은 GCP에서 실행되는 Debian 머신에서 수행하며,
gcloud compute instances create로 Debian 10 인스턴스containers-demystified를us-west1-a영역에 만들고 SSH로 접속함. - 환경의 커널 버전은
4.19.0-9-cloud-amd64임. - Docker 설치를 위해 HTTPS 전송·인증서·
curl·GnuPG·소프트웨어 저장소 관리 도구를 설치하고, Docker의 Debian 저장소와 GPG 키를 추가함. - 패키지 목록을 갱신한 뒤
docker-ce,docker-ce-cli,containerd.io를 설치하고,docker그룹을 생성해 현재 사용자를 그룹에 추가함.
처음부터 컨테이너 만들기
- Alpine 이미지 레이어의 파일을
container/디렉터리에 풀면bin,dev,etc,home,root,tmp,usr,var등이 있는 파일시스템이 만들어짐. unshare --fork --mount bash로 마운트 네임스페이스를 만들고,container/를 자체 바인드 마운트한 뒤pivot_root로 새 루트 파일시스템으로 전환함.- 기존 루트를
/_old에서 언마운트하면 프로세스가 격리된 파일시스템에서 벗어나지 못하도록 할 수 있음.pivot_root는 호출 프로세스의 루트 파일시스템을 지정한 새 루트로 바꾸고 기존 루트를 두 번째 경로에 둠. - 새 루트에서
/proc파일시스템을 다시 마운트해 프로세스 목록을 확인하면, 마운트 격리만으로는 호스트의 프로세스가 여전히 보임. unshare에--pid를 추가하면 별도의 프로세스 네임스페이스가 생기며, 프로세스 목록에는 네임스페이스 안의bash와ps만 표시됨.- 네트워크 장치를 확인하면 루프백 장치뿐 아니라
docker0와 물리 네트워크에 연결된eth0도 보여 네트워크 격리가 아직 되지 않은 상태임. --uts와--net을 추가해 UTS 및 네트워크 네임스페이스를 만들고, 호스트에서 컨테이너 프로세스의 PID를 이용해veth쌍을 생성함.- 호스트 쪽
veth장치를 올리고docker0브리지에 연결함. - 새 네임스페이스에서 파일시스템 전환과
/proc마운트를 수행한 뒤 호스트 이름을container로 설정함. - 루프백과 컨테이너 쪽
veth장치를 올리고172.17.42.3/16주소와172.17.0.1기본 게이트웨이를 설정함. - 이 설정에서
ping 8.8.8.8은 동작할 수 있지만,/etc/resolv.conf가 없어 DNS는 동작하지 않음. - 이 네트워크 구성은 Docker 브리지 설정을 활용하는 방식임. 클라우드 인스턴스에서 브리지를 직접 설정하는 일은 간단하지 않고, 물리 네트워크 장치를 일시적으로 옮기면 인스턴스의 라우팅이 끊길 수 있음.
- Docker는 NAT를 구성하기 위해 다양한
iptables규칙을 사용함.
다음 단계
- 지금까지 만든 환경은 Docker와 같은 도구가 하는 작업에 상당히 가까움.
- 빠진 핵심 요소는 오버레이 파일시스템에서 컨테이너를 시작해 새
pivot_root안의 변경 사항이 기본 이미지에 영향을 주지 않도록 하는 구성임.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요