假设 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 状态中看到sh
and 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
: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 中:注意:上面的 PID 13075 实际上是一个
sleep 1
进程,在 PID 13043 死亡后产生。希望这可以帮助。
podman with
--cgroups split
将以更系统友好的方式创建 cgroup。(类似于 systemd-nspawn 的做法,使用“服务”cgroup,以及用于主管和容器进程的子 cgroup)示例 - 我的 rwhod 容器:
作为这个日志讨论的一部分,我是专门为此目的将该方法添加到 podman 的一部分。https://github.com/containers/podman/issues/6400