컨테이너 내부 원리 · 네임스페이스 — 무엇을 볼 수 있는가 · 실습
네임스페이스를 눈으로 확인하기
이 실습은 진짜 VM 에서 돕니다
이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이
따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라
진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가
되고 docker exec 도 docker logs 도 그대로 동작합니다.
예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라
컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어
보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.
알아 둘 것이 둘 있습니다.
- 처음 뜨는 데 1분 남짓 걸립니다. VM 이 부팅하고 도커를 설치하기
- 브라우저 미리보기가 없습니다. VM 으로 들어오는 연결은 채점 포트
때문입니다. 파드 실습(보통 40초)보다 느립니다.
하나만 열려 있습니다. 웹 서버를 띄웠다면 VM 안에서 curl 로 확인하세요.
목표
네임스페이스 7종 중 pid, uts, mnt, net, user 다섯 가지를 직접 재어 보고, 컨테이너가
'상자'가 아니라 시야가 제한된 평범한 프로세스라는 것을 inode 번호로 증명합니다.
마지막에는 컨테이너 둘이 네트워크 네임스페이스를 공유하는 쿠버네티스 파드 구조를
도커만으로 재현합니다.
왜 중요한가
컨테이너 트러블슈팅이 막히는 지점은 대부분 "이건 분리된 건가 아닌가"를 몰라서
생깁니다. 포트가 왜 충돌하는지, 컨테이너 안에서 본 파일이 호스트 어디에 있는지,
사이드카가 왜 localhost 로 앱에 붙는지 — 답은 전부 "그 자원이 어느 네임스페이스에
속하는가" 한 줄입니다. 네임스페이스에는 inode 번호가 붙고 /proc/<pid>/ns/* 로
읽을 수 있으므로, 이 질문은 추측이 아니라 측정으로 답할 수 있습니다.
같은 번호면 같은 네임스페이스, 다른 번호면 다른 네임스페이스. 그게 전부입니다.
단계
1. /root/int1 디렉터리를 만들고, 호스트에서 /proc/self/ns/pid 링크가 가리키는 값을 /root/int1/host-pid-ns.txt 에 저장합니다. 파일 내용은 pid:[숫자] 한 줄이어야 합니다.
2. alpine:3.20 이미지로 dk-ns 라는 이름의 컨테이너를 --hostname labhub-uts 를 붙여 sleep infinity 로 백그라운드 실행하고, 그 컨테이너 안에서 읽은 /proc/self/ns/pid 값을 /root/int1/ctr-pid-ns.txt 에 저장합니다. 1단계 값과 달라야 합니다.
3. dk-ns 안에서 프로세스 목록을 PID 열이 보이는 형식으로 뽑아 /root/int1/pid1.txt 에 저장합니다 (docker exec dk-ns ps -o pid,comm). PID 1 이 sleep — 그 컨테이너를 띄울 때 준 명령 — 이어야 합니다. systemd 도 init 도 아니라는 것이 요점입니다. 이 VM 에서 그냥 ps -e 를 하면 PID 1 은 systemd 라서 대조가 됩니다.
4. dk-ns 의 호스트명을 /root/int1/uts.txt 에 저장합니다. 내용은 labhub-uts 한 줄이어야 합니다.
5. 이 상자의 /etc/os-release 와 alpine 이미지 쪽 /etc/os-release 를 한 파일에 이어서 /root/int1/mnt-proof.txt 에 저장합니다. 파일 안에 호스트 쪽 ubuntu 와 컨테이너 쪽 alpine 이 둘 다 보여야 합니다.docker run --rm alpine:3.20 cat /etc/os-release 와 이 VM 의 같은 파일을 한 파일에 이어 붙이면 됩니다. 함께 uname -r 도 찍어 보세요 — 커널은 같고 파일 트리만 다릅니다. 마운트 네임스페이스가 하는 일이 결국 프로세스의 / 를 다른 트리로 바꿔치는 것뿐이라는 사실이 그 두 줄에 들어 있습니다.
6. dk-ns 안에서 읽은 /proc/net/dev 를 /root/int1/ctr-net.txt 에 저장합니다. lo: 줄이 있어야 하고, 호스트의 같은 파일과 내용이 달라야 합니다 — 인터페이스 구성과 송수신 통계는 네임스페이스마다 따로 관리됩니다.
7. dk-ns 의 네트워크 네임스페이스에 합류하는 dk-sidecar 컨테이너를 백그라운드로 띄우고, dk-ns 안에서 읽은 /proc/self/ns/net 을 /root/int1/ns-a.txt 에, dk-sidecar 안에서 읽은 같은 값을 /root/int1/ns-b.txt 에 저장합니다. 두 값은 net:[숫자] 형식이면서 서로 같아야 합니다.
8. 호스트의 /proc/self/uid_map 을 /root/int1/host-uidmap.txt 에, dk-ns 안의 /proc/self/uid_map 을 /root/int1/ctr-uidmap.txt 에 저장합니다. 두 매핑 내용이 서로 달라야 합니다.
참고
- 네임스페이스 식별자는
readlink /proc/self/ns/pid처럼 링크를 따라가야 나옵니다.cat으로는 읽히지 않습니다. - 컨테이너 안에서 무언가를 읽을 때는
docker exec <이름> <명령>을 쓰고, 그 출력을 호스트 쪽 경로로 리다이렉션합니다. - 남의 네트워크 네임스페이스에 합류하는 옵션은
--network container:<이름>입니다. - 이어붙이기는
>>, 덮어쓰기는>입니다. 5단계에서 둘을 헷갈리면 앞의 내용이 사라집니다. - 흔한 실수 1: 2·6·8단계를 호스트 셸에서 실행하면 전부 호스트 값이 저장돼 "같습니다"로 실패합니다.
- 흔한 실수 2: 컨테이너를
--rm으로 띄우면 다음 단계에서dk-ns가 사라져 채점이 실패합니다. - 흔한 실수 3: 4단계에서
hostname출력에 개행 말고 다른 문자가 섞이면 안 됩니다.labhub-uts만 남아야 합니다.
단계 8개
- 호스트의 PID 네임스페이스 식별자 읽기
- 컨테이너의 PID 네임스페이스와 비교
- 컨테이너 안의 PID 1 확인
- UTS 네임스페이스로 분리된 호스트명
- 마운트 네임스페이스 — 같은 커널, 다른 파일 트리
- 네트워크 네임스페이스의 인터페이스 목록
- 두 컨테이너가 네임스페이스를 공유하게 하기
- 유저 네임스페이스의 UID 매핑