容器技术发展至今,Docker 长期占据主导地位,其架构中的一个核心组件是以 root 权限运行的守护进程 dockerd。这个守护进程负责接收客户端命令、管理镜像、创建容器、维护运行状态,几乎所有操作都要经过它。Podman 的出现打破了这一模式,它不再依赖任何常驻后台进程,而是把容器管理功能直接集成到命令行工具中,通过 fork/exec 的方式启动容器进程。这种设计让 Podman 在安全性、资源占用和系统集成方面表现出明显优势,也使得容器管理变得更加灵活。

Docker 守护进程模式带来的固有问题
Docker 的典型工作流程是:用户执行 docker run 命令,docker CLI 将请求发送给本地的 dockerd 守护进程,dockerd 再通过 containerd 调用 runc 来创建容器。在这个链路中,dockerd 是所有容器操作的必经之路,而且它必须以 root 身份运行。这种集中式架构带来了几个明显的问题。
首先是单点故障。一旦 dockerd 进程崩溃或被误杀,宿主机上所有正在运行的容器都会受到影响,因为容器进程是 dockerd 的子进程,它们之间的父子关系决定了容器的生死与守护进程绑定。其次是权限膨胀。dockerd 拥有 root 权限,任何能够与它通信的用户实际上都具备了对宿主机的控制能力,这为容器逃逸攻击留下了更大的攻击面。第三是升级困难。升级 Docker 版本通常需要重启 dockerd,而这会导致所有容器短暂中断,对于生产环境来说代价很高。
Podman 的思路是去掉这个中间层。用户执行 podman run 时,Podman 直接与容器运行时(如 runc 或 crun)通信,由运行时创建容器进程。容器启动后,Podman 本身可以退出,容器进程由轻量级的监控程序 conmon 管理。这种模式与 Docker 的守护进程模式有本质区别,容器不再是某个后台服务的子进程,而是独立的系统进程,生命周期与 Podman 命令本身解耦。
Podman 的无守护进程架构与 OCI 运行时协作
Podman 遵循 OCI(Open Container Initiative)标准,这意味着它不绑定特定的运行时实现。用户可以通过 podman info 查看当前使用的运行时,常见的有 runc 和 crun,其中 crun 是一个用 C 语言编写的轻量级运行时,启动速度更快,内存占用更低。Podman 在执行容器操作时,会直接调用这些运行时的可执行文件,而不是通过某个常驻服务转发。
以启动一个容器为例,Podman 会先解析镜像、准备 rootfs 和配置,然后调用 runc create 来创建容器实例,再调用 runc start 启动它。容器进程运行后,Podman 会启动一个名为 conmon 的小程序来监控容器的标准输出、标准错误和退出状态。conmon 不是守护进程,它只负责单个容器的监控工作,容器退出后 conmon 也会随之退出。这种设计使得每个容器都有独立的监控进程,相互之间完全隔离,一个容器的异常不会影响其他容器。
# 查看 Podman 使用的 OCI 运行时
podman info --format '{{.Host.OCIRuntime.Name}}'
# 启动一个容器并查看进程树
podman run -d --name web nginx
ps -ef | grep -E 'conmon|nginx'容器状态和镜像元数据也不再存储在守护进程的数据库中,而是直接保存在文件系统里。rootful 模式下数据位于 /var/lib/containers,rootless 模式下位于 ~/.local/share/containers。这种存储方式让数据管理变得透明,用户可以像操作普通文件一样备份、迁移或删除容器数据,而不需要依赖某个后台服务提供 API。
Rootless 模式:普通用户安全运行容器
无守护进程架构带来的一个直接好处是 rootless 容器的实现变得更加自然。在 Docker 中,rootless 模式需要额外的配置和守护进程支持,而 Podman 从一开始就设计了完整的 rootless 支持。普通用户无需 sudo 权限就能运行容器,容器内的 root 用户通过 user namespace 映射到宿主机的普通用户,即使发生容器逃逸,攻击者也只能获得普通用户权限,而不是宿主机 root 权限。
实现 rootless 需要几个关键组件配合。用户命名空间通过 /etc/subuid 和 /etc/subgid 文件分配从属 UID 和 GID 范围,容器内的用户映射到这些范围。网络方面,rootless 容器无法直接创建 veth pair 或操作 iptables,因此 Podman 默认使用 slirp4netns 提供用户态网络栈,让容器通过 NAT 访问外部网络。存储方面,rootless 容器可以使用 fuse-overlayfs 来创建可写的联合文件系统,而无需 root 权限挂载 overlayfs。
# 查看当前用户的从属 ID 范围 cat /etc/subuid cat /etc/subgid # 以普通用户身份运行 rootless 容器 podman run -d -p 8080:80 --name myweb nginx podman ps curl localhost:8080
Rootless 模式让多租户环境下的容器隔离更加彻底。在同一台服务器上,多个普通用户各自运行自己的容器,互相之间无法访问对方的进程、文件或网络资源,也不需要管理员为每个用户分配单独的守护进程。这种模式非常适合 HPC 集群、共享开发机和 CI/CD 流水线等场景。
与 systemd 集成管理容器生命周期
由于 Podman 的容器本质上是独立进程,它们可以很方便地交给 systemd 来管理。systemd 是 Linux 系统中最常用的初始化系统和服务管理器,能够提供进程监控、自动重启、开机自启、日志收集等功能。Podman 提供了 podman generate systemd 命令,可以根据现有容器自动生成 systemd 单元文件,让容器像系统服务一样运行。
使用这个功能时,需要先创建一个容器,然后用生成命令输出单元文件。生成的单元文件可以放到 ~/.config/systemd/user 目录下(用于 rootless 容器)或者 /etc/systemd/system 目录下(用于 rootful 容器)。之后通过 systemctl --user enable 或 systemctl enable 启用服务,容器就会在系统启动时自动运行,并且在容器异常退出时由 systemd 自动重启。
# 创建容器 podman create --name myapp -p 8080:80 nginx # 生成 systemd 单元文件 podman generate systemd --name myapp --files # 查看生成的单元文件内容 cat container-myapp.service
生成的单元文件通常包含 ExecStart 和 ExecStop 指令,分别对应 podman start 和 podman stop 命令。这样 systemd 并不需要理解容器内部的细节,它只需把 Podman 当作一个普通的命令来调用。这种方式比 Docker 的 restart policy 更加灵活,因为可以利用 systemd 的依赖管理、定时任务、资源限制等丰富特性。例如,你可以让容器在网络就绪后再启动,或者限制容器服务的 CPU 和内存使用量。
对于已经习惯了 systemd 管理传统服务的运维人员来说,Podman 的这种集成方式大大降低了容器化的学习成本。你不再需要学习另一个守护进程的配置语法,而是用熟悉的 systemd 单元文件来管理容器。这也是 Podman 在 RHEL、CentOS、Fedora 等发行版中越来越受欢迎的重要原因之一。
综合来看,Podman 通过去除守护进程这一层,把容器管理回归到 Unix 哲学:一个工具只做一件事,并且做好。容器进程独立运行,权限最小化,状态透明存储,并且能与 systemd 无缝协作。虽然 Docker 的守护进程模式在集群管理和远程 API 方面仍有优势,但对于单机容器管理和追求安全、简洁的场景,Podman 的无守护进程架构无疑提供了一种更优的选择。