iT邦幫忙

2022 iThome 鐵人賽

DAY 18
0
DevOps

那些關於 docker 你知道與不知道的事系列 第 18

Day 18: container 中 PID 1 process 的 parent 是誰呢?

  • 分享至 

  • xImage
  •  

到目前,我們知道了 Linux 上的 processes 是有父子關係的、討論了 zombie process 跟 orphan process,也知道了 PID 1 的 init process 在 linux 作業系統中的特殊角色。那讓我們複習一下 container 中的情況:

$ docker run -it --rm alpine ash

/ # sleep 3000 &
/ # ps -eaf -o pid,ppid,comm,args
PID   PPID  COMMAND          COMMAND
    1     0 ash              ash
    7     1 sleep            sleep 3000
    8     1 ps               ps -eaf -o pid,ppid,comm,args

這大家已經看過很多次了,兩個觀察點:

  1. PID 1 的 process 是 ash 這個,且其 PPID 為 0。ash 的這個 process 是啟動這個 container 的起始 process,由於是為這個 container 新建立的 PID namespace 中的第一個,所以他的 PID 會是 1。這邊也可以看到他的 PPID 會是 0,其解讀可以是因為 ash 這個 process 最一開始並不是在這個容器中被建立的,我們在 host 這裏,也就是 root namespace 裡去啟動這個 process,他的 parent process 會是在 host 中。

  2. sleepps 這兩個 process 的 PPID 都是 1,也就是他們都是從 ash process 去 fork 出來的,這有符合在 linux 世界中,PID 1 會是其他 process 的 parent process 或是祖先這件事。

讓我們回到 host 這邊來觀察一下,這次我們試試看用 docker top 這個指令:

ubuntu@ip-xxx:~$ docker container ps
CONTAINER ID   IMAGE     COMMAND   CREATED          STATUS          PORTS     NAMES
36d649e82a2b   alpine    "ash"     13 seconds ago   Up 11 seconds             xxx

ubuntu@ip-xxx:~$ docker top 36d649e82a2b -eaf -o pid,ppid,stat,comm
PID                 PPID                STAT                COMMAND
79020               78999               Ss+                 ash
79054               79020               S                   \_ sleep

首先我們先透過 docker container ps 找出個 container 的 id 是 36d649e82a2b,隨後在用 docker top 來查看,docker top 的說明如下:

ubuntu@ip-xxx:~$ docker top --help

Usage:  docker top CONTAINER [ps OPTIONS]

Display the running processes of a container

可以看到 docker top 是用來列出在 container 中執行的 processes 的,而且他可以使用 ps 指令的參數,注意到他這邊列出來的 PID 與 PPID 都是在 host 中的。我們另外再透過 host 中執行 ps 指令看看 PID 78999, 79020, 79054 這三個 processes 分別是什麼:

  • 也可以用 pstree 觀察喔
ubuntu@ip-xxx:~$ ps -eaf
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 Sep18 ?        00:00:17 /sbin/init
...略
root         490       1  0 Sep18 ?        00:26:13 /usr/bin/containerd
...略
root       78999       1  0 14:20 ?        00:00:00 /usr/bin/containerd-shim-runc-v2...
root       79020   78999  0 14:20 pts/0    00:00:00 ash
root       79054   79020  0 14:20 pts/0    00:00:00 sleep 3000
...略

整理一下觀察到的:

/sbin/init (1) 
    \---> containerd-shim-runc-v2 (78999) 
        \---> ash (79020) 
            \---> sleep (79054)

container 中 PID 1 的 process 是 host 中的 PID 79020,而 79020 的 parent process 是 containerd-shim-runc-v2(78999),而因為 containerd-shim-runc-v2 這個 process 並不存在於 container 的那個新的 PID namespace 中,所以在 contianer 裡,會看到 PID 1 的 process 其 PPID 為 0。

透過昨天的討論,我們已知 /sbin/init 是何方神聖了,那 containerd-shim-runc-v2 是什麼呢?我在 ps -eaf 列出的 processes 中還保留了一個 containerd,那這又跟我們現在要討論的有什麼關係呢?

先來看張古老的架構圖,之所以說古老,那是因為這是在 docker 1.11 版時的架構,來源請參考Docker 1.11: The first runtime built on containerd and based on OCI technology

https://ithelp.ithome.com.tw/upload/images/20221003/20151857vAPq8zX7RV.png

要討論這張圖,要先回到 Docker 本身的架構:Docker 本身用的是 client-server 的架構,Docker client (Docker CLI) 會負責跟 Docker daeomn 溝通,其溝通方式為 REST API,而這個 docker daemon 即為 dockerd,而這整個組合就是 docker engine。當 dockerd 收到指令後,會再將指令送往 containerdcontainerd 將較於 dockerd,他更符合 OCI 的標準(備註1)。本來 containerd 應該是要直接呼叫 runc (備註2) 來運行運行 container 的,不過在 Docker 中,containerdrunc 之間還有一個「墊片 (shim)」在。

這邊出現了很多詞彙是之前沒有討論過的,但其流程大概是這樣:

Docker CLI ==> dockerd ==> conatinerd ==> containerd-shim ==> runc ==> container 

根據我們這兩天的討論,既然是 runc 去啟動 container 的,那這個 runc process 應該是我們 container 中 PID 1 的 process 的 parent,但我們觀察起來並不是這樣的,在 ps 列出來的 process 清單中,也找不到 runc 這個 process。

這是因為 runc 在啟動完 container 後就會終止了。那這樣更奇怪了,在 runc 終止後,他的 child process 會變成 orphan,根據我們昨天的討論,這個孤兒行程應該要被 linux PID 1 的 process,也就是 init process 接收才對,為什麼會變成 containerd-shim 呢?

這些問題的答案,以及這些東西到底跟 container 中的 PID 1 有什麼關係,就讓我們明天繼續討論嚕,希望明天可以結束這部分啦。


備註1: 這邊要講下去,就得從 Docker, OCI, CNCF 與各廠商之間的恩怨情仇開始講起,那可能要 iThome 鐵人賽 365 天才能說得完了,簡單地說,OCI 是容器技術的標準,他定義了容器技術相關的一些規範,最主要的兩個是 Runtime 與 image,runtime 是 container 的運行標準,而 image 則是我們最一開始討論的那個 image 的標準。

備註2: containerd 會調用 runc 來啟動 container,runc 完全符合 OCI 的標準。


上一篇
Day 17: 殭屍與孤兒
下一篇
Day 19: Container 中 PID 1 的 process 會擔負起 init process 的責任嗎?
系列文
那些關於 docker 你知道與不知道的事32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言