给定 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
或进程sleep
# 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
和sleep
子项,因为我听 Redhat 的人说 podman 使用传统的 fork-exec 模型。
如果 podman 执行了 fork 和 exec,那么我的sh
和sleep
进程不是 podman 的子进程,并且与原始 podman 进程位于同一个 cgroup 中吗?
我期望能够使用 systemd 和 podman 来管理我的容器,而无需孩子们转到不同的父母并逃离我的 baz.service ssystemd 单元。
查看 的输出,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 守护进程
- docker 命令行
- Containerd 容器运行时
podman 就像:
- podman 命令行界面
- 公共容器运行时
因此,也许 podman 使用传统的 fork exec 模型,但不是 podman cli 进行 fork 和 exec,而是通用进程。
我感到很困惑。
答案1
背后的整个想法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
使用 cgroup 来限制容器。这意味着它为新容器创建新的 cgroup 并将进程移至其中。根据 cgroup 的规则,进程一次只能是一个 cgroup 的成员,将进程添加到某个 cgroup 会将其从同一层次结构中的其他 cgroup(之前所在的位置)中删除。所以,当容器启动时,cgroups的最终布局如下所示:保留在由创建的podman run
cgroups中,进程放置在自己的cgroup中,容器化进程放置在自己的cgroup中:baz.service
systemd
conmon
$ 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死亡后产生的。
希望这可以帮助。
答案2
podman with--cgroups split
将以更加适合 systemd 的方式创建 cgroup。 (类似于 systemd-nspawn 的做法,使用“服务”cgroup 以及用于主管和容器进程的子 cgroup)
示例 - 我的 rwhod 容器:
CGroup: /machine.slice/rwhod.service
├─container
│ ├─ 998 /dev/init -- /container/tool/run
│ ├─1040 /usr/bin/python3 -u /container/tool/run
│ └─1706 /usr/sbin/rwhod -i mgmt0 -S -D
└─supervisor
└─995 /opt/podman/libexec/podman/conmon --api-version 1 -c ddf3e27960378fd57b2ebd15d7beb7474506f612e7329acb014c5f89cd652562 >
作为这个日志讨论的一部分,我专门为此目的将该方法添加到了 podman。https://github.com/containers/podman/issues/6400