Microsoft MVP성태의 닷넷 이야기
Linux: 90. pid 네임스페이스 구성으로 본 WSL 2 + docker-desktop [링크 복사], [링크+제목 복사],
조회: 4984
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 

(시리즈 글이 7개 있습니다.)
개발 환경 구성: 713. "WSL --debug-shell"로 살펴보는 WSL 2 VM의 리눅스 환경
; https://www.sysnet.pe.kr/2/0/13650

개발 환경 구성: 715. Windows - WSL 2 환경의 Docker Desktop 네트워크
; https://www.sysnet.pe.kr/2/0/13659

Linux: 88. WSL 2 리눅스 배포본 내에서의 pid 네임스페이스 구성
; https://www.sysnet.pe.kr/2/0/13771

Linux: 89. pid 네임스페이스 구성으로 본 WSL 2 배포본의 계층 관계
; https://www.sysnet.pe.kr/2/0/13772

Linux: 90. pid 네임스페이스 구성으로 본 WSL 2 + docker-desktop
; https://www.sysnet.pe.kr/2/0/13773

Linux: 91. Container 환경에서 출력하는 eBPF bpf_get_current_pid_tgid의 pid가 존재하지 않는 이유
; https://www.sysnet.pe.kr/2/0/13774

개발 환경 구성: 729. WSL 2 - Mariner VM 커널 이미지 업데이트 방법
; https://www.sysnet.pe.kr/2/0/13779




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

일단 이 정도로만 알아보겠습니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 10/19/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 91  92  93  94  95  96  97  98  99  100  101  102  103  [104]  105  ...
NoWriterDateCnt.TitleFile(s)
11325정성태10/14/201719383.NET Framework: 689. CLR 4.0 환경에서 DLL 모듈의 로드 주소(Base address) 알아내는 방법
11324정성태10/13/201720970디버깅 기술: 101. windbg - "*** WARNING: Unable to verify checksum for" 경고 없애는 방법
11322정성태10/13/201718370디버깅 기술: 100. windbg - .NET 4.0 응용 프로그램의 Main 메서드에 Breakpoint 걸기
11321정성태10/11/201719896.NET Framework: 688. NGen 모듈과 .NET Profiler
11320정성태10/11/201720705.NET Framework: 687. COR_PRF_USE_PROFILE_IMAGES 옵션과 NGen의 "profiler-enhanced images" [1]
11319정성태10/11/201728270.NET Framework: 686. C# - string 배열을 담은 구조체를 직렬화하는 방법
11318정성태10/7/201721033VS.NET IDE: 122. 비주얼 스튜디오에서 관리자 권한을 요구하는 C# 콘솔 프로그램 제작 [1]
11317정성태10/4/201726163VC++: 120. std::copy 등의 함수 사용 시 _SCL_SECURE_NO_WARNINGS 에러 발생
11316정성태9/30/201724215디버깅 기술: 99. (닷넷) 프로세스(EXE)에 디버거가 연결되어 있는지 아는 방법 [4]
11315정성태9/29/201740251기타: 68. "시작하세요! C# 6.0 프로그래밍: 기본 문법부터 실전 예제까지" 구매하신 분들을 위한 C# 7.0/7.1 추가 문법 PDF [8]
11314정성태9/28/201722073디버깅 기술: 98. windbg - 덤프 파일로부터 닷넷 버전 확인하는 방법
11313정성태9/25/201719337디버깅 기술: 97. windbg - 메모리 덤프로부터 DateTime 형식의 값을 알아내는 방법파일 다운로드1
11312정성태9/25/201722489.NET Framework: 685. C# - 구조체(값 형식)의 필드를 리플렉션을 이용해 값을 바꾸는 방법파일 다운로드1
11311정성태9/20/201716853.NET Framework: 684. System.Diagnostics.Process 객체의 명시적인 해제 권장
11310정성태9/19/201720313.NET Framework: 683. WPF의 Window 객체를 생성했는데 GC 수집 대상이 안 되는 이유 [3]
11309정성태9/13/201718422개발 환경 구성: 335. Octave의 명령 창에서 실행한 결과를 복사하는 방법
11308정성태9/13/201719479VS.NET IDE: 121. 비주얼 스튜디오에서 일부 텍스트 파일을 무조건 메모장으로만 여는 문제파일 다운로드1
11307정성태9/13/201722060오류 유형: 421. System.Runtime.InteropServices.SEHException - 0x80004005
11306정성태9/12/201720156.NET Framework: 682. 아웃룩 사용자를 위한 중국어 스팸 필터 Add-in
11305정성태9/12/201721621개발 환경 구성: 334. 기존 프로젝트를 Visual Studio를 이용해 Github의 신규 생성된 repo에 올리는 방법 [1]
11304정성태9/11/201718703개발 환경 구성: 333. 3ds Max를 Hyper-V VM에서 실행하는 방법
11303정성태9/11/201721992개발 환경 구성: 332. Inno Setup 파일의 관리자 권한을 제거하는 방법
11302정성태9/11/201718279개발 환경 구성: 331. SQL Server Express를 위한 방화벽 설정
11301정성태9/11/201717146오류 유형: 420. SQL Server Express 연결 오류 - A network-related or instance-specific error occurred while establishing a connection to SQL Server.
11300정성태9/10/201721039.NET Framework: 681. dotnet.exe - run, exec, build, restore, publish 차이점 [3]
11299정성태9/9/201719733개발 환경 구성: 330. Hyper-V VM의 Internal Network를 Private 유형으로 만드는 방법
... 91  92  93  94  95  96  97  98  99  100  101  102  103  [104]  105  ...