名前空間を目で確かめる
한국어 원문으로 표시합니다.
이 실습은 진짜 VM 에서 돕니다
이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이
따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라
진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가
되고 docker exec 도 docker logs 도 그대로 동작합니다.
예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라 컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어 보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.
알아 둘 것이 둘 있습니다.
- 처음 뜨는 데 1분 남짓 걸립니다. VM 이 부팅하고 도커를 설치하기 때문입니다. 파드 실습(보통 40초)보다 느립니다.
- 브라우저 미리보기가 없습니다. VM 으로 들어오는 연결은 채점 포트
하나만 열려 있습니다. 웹 서버를 띄웠다면 VM 안에서
curl로 확인하세요.
목표
네임스페이스 7종 중 pid, uts, mnt, net, user 다섯 가지를 직접 재어 보고, 컨테이너가 '상자'가 아니라 시야가 제한된 평범한 프로세스라는 것을 inode 번호로 증명합니다. 마지막에는 컨테이너 둘이 네트워크 네임스페이스를 공유하는 쿠버네티스 파드 구조를 도커만으로 재현합니다.
왜 중요한가
컨테이너 트러블슈팅이 막히는 지점은 대부분 "이건 분리된 건가 아닌가"를 몰라서
생깁니다. 포트가 왜 충돌하는지, 컨테이너 안에서 본 파일이 호스트 어디에 있는지,
사이드카가 왜 localhost 로 앱에 붙는지 — 답은 전부 "그 자원이 어느 네임스페이스에
속하는가" 한 줄입니다. 네임스페이스에는 inode 번호가 붙고 /proc/<pid>/ns/* 로
읽을 수 있으므로, 이 질문은 추측이 아니라 측정으로 답할 수 있습니다.
같은 번호면 같은 네임스페이스, 다른 번호면 다른 네임스페이스. 그게 전부입니다.
단계
/root/int1디렉터리를 만들고, 호스트에서/proc/self/ns/pid링크가 가리키는 값을/root/int1/host-pid-ns.txt에 저장합니다. 파일 내용은pid:[숫자]한 줄이어야 합니다.alpine:3.20이미지로dk-ns라는 이름의 컨테이너를--hostname labhub-uts를 붙여sleep infinity로 백그라운드 실행하고, 그 컨테이너 안에서 읽은/proc/self/ns/pid값을/root/int1/ctr-pid-ns.txt에 저장합니다. 1단계 값과 달라야 합니다.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 라서 대조가 됩니다.dk-ns의 호스트명을/root/int1/uts.txt에 저장합니다. 내용은labhub-uts한 줄이어야 합니다.- 이 상자의
/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도 찍어 보세요 — 커널은 같고 파일 트리만 다릅니다. 마운트 네임스페이스가 하는 일이 결국 프로세스의/를 다른 트리로 바꿔치는 것뿐이라는 사실이 그 두 줄에 들어 있습니다. dk-ns안에서 읽은/proc/net/dev를/root/int1/ctr-net.txt에 저장합니다.lo:줄이 있어야 하고, 호스트의 같은 파일과 내용이 달라야 합니다 — 인터페이스 구성과 송수신 통계는 네임스페이스마다 따로 관리됩니다.dk-ns의 네트워크 네임스페이스에 합류하는dk-sidecar컨테이너를 백그라운드로 띄우고,dk-ns안에서 읽은/proc/self/ns/net을/root/int1/ns-a.txt에,dk-sidecar안에서 읽은 같은 값을/root/int1/ns-b.txt에 저장합니다. 두 값은net:[숫자]형식이면서 서로 같아야 합니다.- 호스트의
/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만 남아야 합니다.
호스트의 PID 네임스페이스 식별자 읽기
/root/int1 디렉터리를 만들고, 호스트에서 /proc/self/ns/pid 링크가 가리키는 값을 /root/int1/host-pid-ns.txt 에 저장합니다. 파일 내용은 pid:[숫자] 한 줄이어야 합니다.
네임스페이스 식별자는 /proc/self/ns/ 아래 심볼릭 링크로 노출됩니다. 링크가 가리키는 문자열 자체가 답이므로 cat 이 아니라 링크를 따라가는 명령을 써야 합니다. 파일에는 pid:[숫자] 한 줄만 남아야 합니다.
컨테이너의 PID 네임스페이스와 비교
alpine:3.20 이미지로 dk-ns 라는 이름의 컨테이너를 --hostname labhub-uts 를 붙여 sleep infinity 로 백그라운드 실행하고, 그 컨테이너 안에서 읽은 /proc/self/ns/pid 값을 /root/int1/ctr-pid-ns.txt 에 저장합니다. 1단계 값과 달라야 합니다.
같은 경로를 컨테이너 안에서 읽어야 다른 번호가 나옵니다. 호스트 셸에서 읽으면 1단계와 똑같은 값이 나와 실패합니다. 컨테이너는 계속 떠 있어야 다음 단계에서 계속 쓸 수 있으니 곧바로 종료되지 않는 명령으로 띄우세요.
컨테이너 안의 PID 1 확인
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 라서 대조가 됩니다.
프로세스 목록을 뽑되 PID 열이 함께 보이는 형식이어야 합니다. 컨테이너의 주 명령이 곧 1번 프로세스입니다. 호스트에서 뽑으면 1번이 다른 프로세스로 나옵니다.
UTS 네임스페이스로 분리된 호스트명
dk-ns 의 호스트명을 /root/int1/uts.txt 에 저장합니다. 내용은 labhub-uts 한 줄이어야 합니다.
호스트명을 바꾸는 것이 아니라, 컨테이너를 만들 때 붙여 준 호스트명이 UTS 네임스페이스 덕분에 호스트와 따로 논다는 것을 확인하는 단계입니다. 컨테이너 안에서 호스트명을 물어보고 그 값만 저장하세요.
마운트 네임스페이스 — 같은 커널, 다른 파일 트리
이 상자의 /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 도 찍어 보세요 — 커널은 같고 파일 트리만 다릅니다. 마운트 네임스페이스가 하는 일이 결국 프로세스의 / 를 다른 트리로 바꿔치는 것뿐이라는 사실이 그 두 줄에 들어 있습니다.
한쪽만 담으면 비교가 되지 않습니다. 호스트에서 읽은 것과 컨테이너 안에서 읽은 것을 같은 파일에 이어서 남기세요. 이어붙일 때는 덮어쓰기가 아니라 추가 리다이렉션을 씁니다.
네트워크 네임스페이스의 인터페이스 목록
dk-ns 안에서 읽은 /proc/net/dev 를 /root/int1/ctr-net.txt 에 저장합니다. lo: 줄이 있어야 하고, 호스트의 같은 파일과 내용이 달라야 합니다 — 인터페이스 구성과 송수신 통계는 네임스페이스마다 따로 관리됩니다.
인터페이스 목록은 /proc/net/dev 로도 읽을 수 있습니다(이 환경에서는 ip/ifconfig 보다 확실합니다). 컨테이너 안에서 읽으면 lo 와 컨테이너 인터페이스 정도만 보이고 호스트보다 줄 수가 적어야 합니다.
두 컨테이너가 네임스페이스를 공유하게 하기
dk-ns 의 네트워크 네임스페이스에 합류하는 dk-sidecar 컨테이너를 백그라운드로 띄우고, dk-ns 안에서 읽은 /proc/self/ns/net 을 /root/int1/ns-a.txt 에, dk-sidecar 안에서 읽은 같은 값을 /root/int1/ns-b.txt 에 저장합니다. 두 값은 net:[숫자] 형식이면서 서로 같아야 합니다.
새 네트워크 네임스페이스를 만드는 대신 남의 것에 합류시키는 옵션이 있습니다. 쿠버네티스 파드에서 pause 컨테이너가 하는 일과 같습니다. 합류가 성공했다면 두 컨테이너의 net inode 번호가 완전히 같아야 합니다.
유저 네임스페이스의 UID 매핑
호스트의 /proc/self/uid_map 을 /root/int1/host-uidmap.txt 에, dk-ns 안의 /proc/self/uid_map 을 /root/int1/ctr-uidmap.txt 에 저장합니다. 두 매핑 내용이 서로 달라야 합니다.
/proc/self/uid_map 은 컨테이너안UID 호스트UID 개수 세 열입니다. 이 환경은 rootless 라서 호스트 쪽 셸과 컨테이너 안쪽의 매핑이 다르게 나옵니다 — 읽기 자료의 실측표(도커 기본값에서는 user 네임스페이스가 같았다)와 왜 다른지 생각해 보세요.