pid 네임스페이스 구성으로 본 WSL 2 + docker-desktop
윈도우에서 docker-desktop을 설치하면 Mariner VM 위에 "docker-desktop" WSL 배포본이 올라가고, 윈도우의 docker-desktop 응용 프로그램은 WSL 배포본 위에서 컨테이너를 관리하는 식으로 동작합니다.
자, 그럼 docker-desktop이 생성하는 WSL 배포본 환경을 pid 네임스페이스 측면에서 살펴볼까요? ^^
우선, 이를 위해 docker-desktop 배포본에 접속한 다음,
c:\temp> wsl -d docker-desktop
# cd ~
# pwd
/root
# ps aux | grep [d]ocker
1 root 0:00 {init(docker-des} /init
19 root 0:08 wsl-bootstrap run --base-image /c/Program Files/Docker/Docker/resources/docker-desktop.iso --cli-iso /c/Program Files/Docker/Docker/resources/wsl/docker-wsl-cli.iso
104 root 1:01 /usr/bin/runc run --preserve-fds=3 01-docker
116 root 0:04 /usr/libexec/docker/docker-init /usr/bin/entrypoint.sh
186 root 58:22 /usr/local/bin/dockerd --config-file /run/config/docker/daemon.json --containerd /run/containerd/containerd.sock --pidfile /run/desktop/docker.pid --swarm-default-advertise-addr=192.168.65.3 --host-gateway-ip 192.168.65.254
543 root 38:06 /usr/bin/cri-dockerd --docker-endpoint unix:///run/guest-services/docker.sock --pod-infra-container-image registry.k8s.io/pause:3.9 --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/var/lib/cni-plugins/bin
pid 네임스페이스를 구하고,
# sleep 586000 &
[1] 208671
# ls -l /proc/208671/ns/pid
lrwxrwxrwx 1 root root 0 Oct 17 23:45 /proc/208671/ns/pid -> pid:[4026532496]
# cat /proc/208671/status | grep NSpid
NSpid: 208671
(wsl --debug-shell로 접속한) Mariner VM에서 4026532496 네임스페이스 관계를 추적하면 대충 이렇게 나옵니다.
// Mariner VM 내에서 실행
# lsns -t pid 4026532496
PID PPID USER COMMAND
1219 1201 root /init
1250 1219 root |-plan9 --control-socket 5 --log-level 4 --server-fd 6 --pipe
1313 1219 root |-/init
1314 1313 root | |-/init
1315 1314 root | | `-wsl-bootstrap run --base-image /c/Program Files/Docker/
1341 1315 root | | `-unshare -muinpf --propagation=unchanged --kill-child
1344 1313 root | `-/init
1353 1344 root | `-/usr/bin/vpnkit-bridge --pid-file=/run/vpnkit-bridge.pi
238593 1219 root `-/init
238594 238593 root `-/init
238595 238594 root `--sh
239682 238595 root `-sleep 586000
# cat /proc/239682/status | grep NSpid
NSpid: 239682 208751 208671
# cat /proc/1219/status | grep NSpid
NSpid: 1219 2 1
# ls -l /proc/1201/ns/pid
lrwxrwxrwx 1 root root 0 Oct 17 01:11 /proc/1201/ns/pid -> 'pid:[4026532484]'
# cat /proc/1201/status | grep NSpid
NSpid: 1201 1
그래서 이렇게 정리가 됩니다.
pid:[4026531836] Mariner VM에 속한 pid namespace
|- 239682 (sleep의 pid)
ㄴ pid:[4026532484] 중간 격리
|- 208751 (sleep의 pid)
ㄴ pid:[4026532496] docker-desktop 인스턴스에 속한 pid namespace
ㄴ 208671 (sleep의 pid)
자, 그럼 윈도우에서 docker run을 하나 해볼까요?
// docker build -t net8_ubuntu18_build -f https://raw.githubusercontent.com/stjeong/sample_docker_script/main/dockerfile.ubuntu18.net8 .
c:\temp> docker run -it --name net8 --rm net8_ubuntu18_build /bin/bash
# sleep 75982323 &
[1] 13
# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 18520 3432 pts/0 Ss 00:52 0:00 /bin/bash
root 13 0.0 0.0 4544 780 pts/0 S 00:53 0:00 sleep 75982323
root 14 0.0 0.0 34416 2808 pts/0 R+ 00:54 0:00 ps aux
# ls -l /proc/1/ns/pid
lrwxrwxrwx 1 root root 0 Oct 18 00:54 /proc/1/ns/pid -> 'pid:[4026532375]'
# ls -l /proc/13/ns/pid
lrwxrwxrwx 1 root root 0 Oct 18 00:54 /proc/13/ns/pid -> 'pid:[4026532375]'
docker-desktop WSL 환경에서 이 프로세스를 추적하면 (Mariner VM에서 WSL 배포본에 대해 그랬던 것처럼) 하위로 3개의 네임스페이스를 확인할 수 있고,
// docker-desktop WSL 환경에서 실행
# ps aux | grep [s]leep
220473 root 0:00 sleep 75982323
# ls -l /proc/220473/ns/pid
lrwxrwxrwx 1 root root 0 Oct 18 00:57 /proc/220473/ns/pid -> pid:[4026532375]
# cat /proc/220473/status | grep NSpid
NSpid: 220473 220313 13
중간 계층까지 알아내,
// Mariner VM 환경에서 실행
# lsns -t pid 4026532375
PID PPID USER COMMAND
251353 251333 root /bin/bash
251599 251353 root `-sleep 75982323
# ls -l /proc/251333/ns/pid
lrwxrwxrwx 1 root root 0 Oct 18 01:01 /proc/251333/ns/pid -> 'pid:[4026532516]'
# ls -l /proc/251353/ns/pid
lrwxrwxrwx 1 root root 0 Oct 18 01:01 /proc/251353/ns/pid -> 'pid:[4026532375]'
정리하면 이렇게 나옵니다.
pid:[4026532496] docker-desktop 인스턴스에 속한 pid namespace
|- 220473 (sleep의 pid)
ㄴ pid:[4026532516] 중간 격리 (아마도, LinuxKit 컨테이너)
|- 220313 (sleep의 pid)
ㄴ pid:[4026532375] docker run 컨테이너의 pid namespace
ㄴ 13 (sleep의 pid)
이것을 Mariner VM부터의 격리까지 합치면 docker run 컨테이너는 WSL 환경에서 대충 5개의 pid namespace 계층을 거치게 됩니다.
// Mariner VM 환경에서 실행
# cat /proc/251599/status | grep NSpid
NSpid: 251599 220553 220473 220313 13
# ls -l /proc/251599/ns/pid
lrwxrwxrwx 1 root root 0 Oct 18 01:01 /proc/251599/ns/pid -> 'pid:[4026532375]'
즉, 이런 구조로 활성화되는 것입니다.
pid:[4026531836] Mariner VM에 속한 pid namespace
|- 251599 (sleep의 pid)
ㄴ pid:[4026532484] 중간 격리
|- 220553 (sleep의 pid)
ㄴ pid:[4026532496] docker-desktop 인스턴스에 속한 pid namespace
|- 220473 (sleep의 pid)
ㄴ pid:[4026532516] 중간 격리 (아마도, LinuxKit 컨테이너)
|- 220313 (sleep의 pid)
ㄴ pid:[4026532375] docker run 컨테이너의 pid namespace
ㄴ 13 (sleep의 pid)
그럼, 여기서 docker inspect로 알아낸 State.Pid 값이 무엇을 의미하는지 살펴보겠습니다.
C:\temp> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
013853d881b7 net8_ubuntu18_build "/bin/bash" 38 minutes ago Up 38 minutes vibrant_kepler
C:\temp> docker inspect -f '{{.State.Pid}}' 013853d881b7
'220068'
220068 pid는 과연 어느 계층의 namespace에 속한 것일까요? 재미있게도, 예상했던 것과는 달리 docker-desktop WSL 환경에서는 찾을 수 없었습니다.
// docker-desktop WSL 환경에서 실행
# ps aux | grep [2]20068
그렇다면, 남은 후보는 docker-desktop WSL과 docker run 컨테이너 사이의 "pid:[4026532516] 중간 격리" 지점에서 생성한 pid로 보이는데요, 이에 대한 확인을 nsenter를 이용해 할 수 있습니다.
// Mariner VM 환경에서 실행
# lsns -t pid 4026532516
PID PPID USER COMMAND
1343 1341 root /init
1427 1343 root |-/init services
1459 1427 root | |-/usr/bin/devenv-server -socket /run/guest-services/devenv-
...[생략]...
16556 1445 root `-/bin/login -f -- root
16557 16556 root `--sh
# nsenter -t 1343 -a ps aux
PID USER TIME COMMAND
1 root 0:02 /init
18 root 11:02 /init services
...[생략]...
220048 root 0:03 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 013853d881b7d675fabe859125a53c154236bd00b34b
220068 root 0:00 /bin/bash
220313 root 0:00 sleep 75982323
231439 root 0:00 ps aux
나왔군요. ^^
참고로, 어차피 container 기술을 기반으로 하니 docker-desktop이 생성하는 컨테이너도 runc list 명령어로 열거할 수 있을 것 같은데요,
How to list docker containers using runc
; https://stackoverflow.com/questions/61738905/how-to-list-docker-containers-using-runc
docker-desktop의 경우 --root 경로가 이와는 다른 듯합니다.
sudo runc --root /run/docker/runtime-runc/moby list
해당 답글에 보면 containerd 데몬에서 사용하는 containerd.toml 파일에 그 경로가 나온다고 하는데요, 이것을 docker-desktop WSL 환경에서 다음과 같은 과정으로 확인할 수 있습니다.
// docker-desktop WSL 환경에서 실행
# ps aux | grep [/]usr/local/bin/containerd
141 root 0:20 /usr/local/bin/containerd --config /etc/containerd/containerd.toml
# nsenter -t 141 -a cat /etc/containerd/containerd.toml
version = 2
root = "/var/lib/desktop-containerd/daemon"
state = "/var/run/desktop-containerd/daemon"
disabled_plugins = [
"io.containerd.grpc.v1.cri",
"io.containerd.internal.v1.restart",
"io.containerd.snapshotter.v1.aufs",
"io.containerd.snapshotter.v1.btrfs",
"io.containerd.snapshotter.v1.devmapper",
"io.containerd.snapshotter.v1.zfs",
"io.containerd.tracing.processor.v1.otlp",
]
oom_score = -500
runtime_type = "io.containerd.runc.v2"
[plugins]
[plugins."io.containerd.runtime.v1.linux"]
runtime_root = "/var/lib/docker/runc"
[plugins."io.containerd.internal.v1.opt"]
path = "/usr/local/containerd.opt"
[proxy_plugins]
[proxy_plugins.stargz]
type = "snapshot"
address = "/run/containerd-stargz-grpc/containerd-stargz-grpc.sock"
그렇긴 한데, 그것을 지정해 실행해도 오류만 발생합니다.
// nsenter -t 141 -a find / -path /mnt -prune -o -path /run/desktop/mnt -prune -o -name runc
// /mnt
// /run/desktop/mnt
// /usr/bin/runc
# nsenter -t 141 -a /usr/bin/runc --root /var/run/desktop-containerd/daemon list
load container io.containerd.content.v1.content: container does not exist
load container io.containerd.metadata.v1.bolt: container does not exist
load container io.containerd.runtime.v1.linux: container does not exist
load container io.containerd.runtime.v2.task: container does not exist
load container io.containerd.snapshotter.v1.blockfile: container does not exist
load container io.containerd.snapshotter.v1.btrfs: container does not exist
load container io.containerd.snapshotter.v1.native: container does not exist
load container io.containerd.snapshotter.v1.overlayfs: container does not exist
load container tmpmounts: container does not exist
ID PID STATUS BUNDLE CREATED OWNER
일단 이 정도로만 알아보겠습니다. ^^
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]