昨天我們「似乎」證明了 container 中的 PID 1 有負起 init process 的責任,會接收子孫輩的 orphan process。
我們來換個方式玩玩看,我準備了兩個以 ubuntu:20.04
為 base image 的 Dockerfile:
bash:exec
From ubuntu:20.04
CMD ["sleep", "1000"]
bash:shell
From ubuntu:20.04
CMD sleep 1000
實驗1: 啟動 bash:exec
ubuntu@ip-xxx:~/shellmode-bash$ docker run -d --rm bash:exec
7e7aae9a459587171a3b6473bcdfca9a92cf938bc74bcde7e52bda62a0de1b7a
ubuntu@ip-xxx:~/shellmode-bash$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7e7aae9a4595 bash:exec "sleep 1000" 56 seconds ago Up 55 seconds epic_payne
ubuntu@ip-xxx:~/shellmode-bash$ docker exec -it 7e7aae9 bash
root@7e7aae9a4595:/# sleep 2000
啟動 bash:exec
後,可以透過 docker ps
觀察到啟動的命令是 sleep 1000
,然後用 docker exec
進入這個 container,並且啟動 sleep
process。
在 host 的觀察:
ubuntu@ip-xxx:~/shellmode-bash$ ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 91849 1 0 16:48 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 7e7aae9a459587
root 91871 91849 0 16:48 ? 00:00:00 sleep 1000
ubuntu 91899 82740 0 16:49 pts/2 00:00:00 docker exec -it 7e7aae9 bash
root 91919 91849 0 16:49 pts/0 00:00:00 bash
root 91927 91919 0 16:49 pts/0 00:00:00 sleep 2000
ubuntu 91936 82095 0 16:50 pts/1 00:00:00 ps -eaf
整理一下:
containerd-shim-runc-v2 (91849)
\_ sleep 1000 (91871)
\_ bash (91919)
\_ sleep 2000 (91927)
一樣來殺掉中間那個 bash (91919):
ubuntu@ip-xxx:~/shellmode-bash$ sudo kill -9 91919
ubuntu@ip-xxx:~/shellmode-bash$ ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 91849 1 0 16:48 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 7e7aae9a459587
root 91871 91849 0 16:48 ? 00:00:00 sleep 1000
root 91927 91871 0 16:49 ? 00:00:00 [sleep] <defunct>
登愣,接收是接收了,但卻變成 zombie
了!
containerd-shim-runc-v2 (91849)
\_ sleep 1000 (91871)
\_ sleep 2000 (91927) <defunct>
實驗2: 啟動 bash:shell
過程一模一樣,這邊就不加贅述,我們就只看不一樣的地方:
ubuntu@ip-xxx:~/shellmode-bash$ docker run -d --rm bash:shell
ed3be7fcee2c697657eeaba9ce30502ec1116de361d9483a3305bb7ae4334855
ubuntu@ip-xxx:~/shellmode-bash$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed3be7fcee2c bash:shell "/bin/sh -c 'sleep 1…" 2 seconds ago Up 2 seconds cranky_hoover
這邊有個不太一樣的地方,可以看到這次啟動 container 的命令變成了 /bin/sh -c 'sleep 1…
,不是 直接執行 sleep
喔,而是用 /bin/sh
來執行 sleep
。
接著一樣用 docker exec
進入 container,但這次先在 container 裡觀察一下 processes:
ubuntu@ip-xxx:~/shellmode-bash$ docker exec -it ed3be7fcee2c bash
root@ed3be7fcee2c:/# ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 16:54 ? 00:00:00 /bin/sh -c sleep 1000
root 7 1 0 16:54 ? 00:00:00 sleep 1000
root 8 0 0 16:56 pts/0 00:00:00 bash
root 28 8 0 16:58 pts/0 00:00:00 ps -eaf
可以觀察到 container 裡 PID 1 的 process 不是 sleep
喔,而是 /bin/sh
。接著執行 sleep
,在 host 我們用 ps
觀察一下:
ubuntu@ip-xxx:~/shellmode-bash$ ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 92040 1 0 16:54 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id ed3be7fcee2c697657eeaba9ce30502e
root 92060 92040 0 16:54 ? 00:00:00 /bin/sh -c sleep 1000
root 92087 92060 0 16:54 ? 00:00:00 sleep 1000
ubuntu 92102 82740 0 16:56 pts/2 00:00:00 docker exec -it ed3be7fcee2c bash
root 92121 92040 0 16:56 pts/0 00:00:00 bash
root 92172 92121 0 17:00 pts/0 00:00:00 sleep 2000
ubuntu 92174 82095 0 17:00 pts/1 00:00:00 ps -eaf
整理一下:
containerd-shim-runc-v2 (92040)
\_ /bin/sh (92060)
\_ sleep 1000 (92087)
\_ bash (92121)
\_ sleep 2000 (92172)
來殺掉中間這個 bash (92121):
ubuntu@ip-xxx:~/shellmode-bash$ sudo kill -9 92121
ubuntu@ip-xxx:~/shellmode-bash$ ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 92040 1 0 16:54 ? 00:00:00 /usr/bin/containerd-shim-runc-v2 -namespace moby -id ed3be7fcee2c697657eeaba9ce30502e
root 92060 92040 0 16:54 ? 00:00:00 /bin/sh -c sleep 1000
root 92087 92060 0 16:54 ? 00:00:00 sleep 1000
變成這樣了:
containerd-shim-runc-v2 (92040)
\_ /bin/sh (92060)
\_ sleep 1000 (92087)
我們殺掉的是 bash(92121),但連 sleep 2000 (92172) 也都被回收了。
來個小小的結論,sleep 1000
看來是無法當一個負責任的 PID 1,雖然他還是會接收孤兒,但並不會好好地回收殭屍。在實驗 2 中,實際上的 PID 1 並不是 sleep
,而是 /bin/sh
,看來 /bin/sh
是有好好地處理殭屍的。
不過不過,我這邊突然改用 ubuntu:20.04
進行實驗,不知道大家有沒有覺得很奇怪?之前不是一直都用 alpine
嗎?如果用 alpine
做一樣的實驗,結果會不一樣嗎?