Systemd

當 podman 使用 systemd 啟動時,為什麼 conmon 在不同的 cgroup 中?

  • May 6, 2021

假設 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 中看不到shorsleep程序

# 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 狀態中看到 theshsleepchildren,因為我聽說 redhat 的人說 podman 使用傳統的 fork-exec 模型。

如果 podman 進行了 fork 和 exec,那麼我的shsleep程序不會是 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)並獨立啟動容器(在一天結束時,容器只是程序,因此您不需要守護程序來生成它們)。

但是,您仍然需要方法

  • 收集容器的日誌 - 必須有人持有容器stdoutstderr
  • 收集容器的退出程式碼 - 有人必須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 中systemdconmon程序放在自己的 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 死亡後產生。

希望這可以幫助。

引用自:https://unix.stackexchange.com/questions/534843