當 podman 使用 systemd 啟動時,為什麼 conmon 在不同的 cgroup 中?
假設 podman 安裝在 linux 系統和名為 baz.service 的 systemd 單元上:
# /etc/systemd/system/baz.service [Service] ExecStart=/usr/bin/podman run --rm --tty --name baz alpine sh -c 'while true; do date; sleep 1; done' ExecStop=/usr/bin/podman stop baz
然後 baz.service 開始了:
# systemctl daemon-reload # systemctl start baz.service
然後,當我檢查單元的狀態時,我在 /system.slice/baz.service cgroup 中看不到
sh
orsleep
程序# systemctl status baz ● baz.service Loaded: loaded (/etc/systemd/system/baz.service; static; vendor preset: enabl Active: active (running) since Sat 2019-08-10 05:50:18 UTC; 14s ago Main PID: 16910 (podman) Tasks: 9 Memory: 7.3M CPU: 68ms CGroup: /system.slice/baz.service └─16910 /usr/bin/podman run --rm --tty --name baz alpine sh -c while # ...
我期待在我的 baz.service 狀態中看到 the
sh
和sleep
children,因為我聽說 redhat 的人說 podman 使用傳統的 fork-exec 模型。如果 podman 進行了 fork 和 exec,那麼我的
sh
和sleep
程序不會是 podman 的子程序並且與原始 podman 程序在同一個 cgroup 中嗎?我期待能夠使用 systemd 和 podman 來管理我的容器,而無需讓孩子轉到另一個父母並逃離我的 baz.service ssystemd 單元。
查看 I 的輸出
ps
可以看到,sh
實際上sleep
是不同程序的子程序,稱為conmon
. 我不確定 conmon 來自哪裡,或者它是如何啟動的,但 systemd 沒有擷取它。# ps -Heo user,pid,ppid,comm # ... root 17254 1 podman root 17331 1 conmon root 17345 17331 sh root 17380 17345 sleep
從輸出中可以清楚地看出我的 baz.service 單元沒有管理 conmon -> sh -> sleep 鏈。
- podman 與 docker 客戶端伺服器模型有何不同?
- podman 的 conmon 與 docker 的 containerd 有何不同?
也許它們都是容器執行時,而
dockerd
守護程序是人們想要擺脫的。所以也許 docker 是這樣的:
- dockerd 守護程序
- 碼頭工人cli
- containerd 容器執行時
podman 就像:
- 播客 cli
- conmon 容器執行時
所以也許 podman 使用了傳統的 fork exec 模型,但它不是分叉和執行的 podman cli,而是 conmon 程序。
我感到困惑。
背後的整個想法
podman
是使用超級強大的監督者(例如)擺脫集中式架構dockerd
,其中集中式守護程序是單點故障。甚至還有一個主題標籤——“ #nobigfatdaemons ”。如何避免集中容器管理?您刪除單個主守護程序(再次,
dockerd
)並獨立啟動容器(在一天結束時,容器只是程序,因此您不需要守護程序來生成它們)。但是,您仍然需要方法
- 收集容器的日誌 - 必須有人持有容器
stdout
;stderr
- 收集容器的退出程式碼 - 有人必須
wait(2)
使用容器的 PID 1;為此,每個 podman 容器仍由一個稱為
conmon
(來自“容器監視器”)的小守護程序監督。與 Docker 守護程序的區別在於,這個守護程序盡可能小(檢查原始碼的大小),並且它是按容器生成的。如果conmon
一個容器崩潰,系統的其餘部分不受影響。接下來,容器是如何產生的?
考慮到使用者可能想在後台執行容器,就像使用 Docker 一樣,
podman run
程序分叉兩次,然後才執行conmon
:$ strace -fe trace=fork,vfork,clone,execve -qq podman run alpine execve("/usr/bin/podman", ["podman", "run", "alpine"], 0x7ffeceb01518 /* 30 vars */) = 0 ... [pid 8480] clone(child_stack=0x7fac6bffeef0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=[8484], tls=0x7fac6bfff700, child_tidptr=0x7fac6bfff9d0) = 8484 ... [pid 8484] clone(child_stack=NULL, flags=CLONE_VM|CLONE_VFORK|SIGCHLD <unfinished ...> [pid 8491] execve("/usr/bin/conmon", ... <unfinished ...> [pid 8484] <... clone resumed>) = 8491
podman run
和之間的中間程序conmon
(即,conmon
在上面的範例中是 PID 8484 的直接父程序)將退出conmon
並由 重新父init
程序,從而成為自我管理的守護程序。在此之後,conmon
還分叉執行時(例如runc
),最後,執行時執行容器的入口點(例如/bin/sh
)。當容器執行時,
podman run
不再需要並且可能退出,但在您的情況下它保持線上,因為您沒有要求它與容器分離。接下來,
podman
利用 cgroups 來限制容器。這意味著它會為新容器創建新的 cgroup 並將程序移到那裡。根據 cgroups 的規則,程序可能一次只能是一個 cgroup 的成員,並且將程序添加到某個 cgroup 會將其從同一層次結構中的其他 cgroup(之前所在的位置)中刪除。所以,當容器啟動時,cgroups 的最終佈局如下:podman run
保留在baz.service
, created by的 cgroups 中systemd
,conmon
程序放在自己的 cgroups 中,容器化的程序放在自己的 cgroups 中:$ ps axf <...> 1660 ? Ssl 0:01 /usr/bin/podman run --rm --tty --name baz alpine sh -c while true; do date; sleep 1; done 1741 ? Ssl 0:00 /usr/bin/conmon -s -c 2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6 <...> 1753 pts/0 Ss+ 0:02 \_ sh -c while true; do date; sleep 1; done 13043 pts/0 S+ 0:00 \_ sleep 1 <...> $ cd /sys/fs/cgroup/memory/machine.slice $ ls -d1 libpod* libpod-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope libpod-conmon-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope $ cat libpod-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope/cgroup.procs 1753 13075 $ cat libpod-conmon-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope/cgroup.procs 1741
注意:上面的 PID 13075 實際上是一個
sleep 1
程序,在 PID 13043 死亡後產生。希望這可以幫助。