
Docker 自 2013 年诞生以来,几乎成为了容器的代名词。然而,随着容器生态的成熟,其单体式架构(包含守护进程 dockerd、containerd、runc 等组件)带来的运维复杂性和安全边界问题逐渐暴露。尤其是 root 权限的守护进程——一旦被攻破,整个宿主机便沦陷。
Podman(Pod Manager)由 Red Hat 主导开发,旨在提供一个 无守护进程(daemonless)、rootless、且与 Docker CLI 完全兼容的容器引擎。它并非简单的“替代品”,而是对容器运行模型的一次重新思考:将容器管理从“客户端-服务端”模式拉回“直接进程派生”模式,更贴近 Linux 系统设计的初衷。
本文将从架构原理、核心特性、实战操作、以及与 Docker 的深度对比四个维度,带你全面掌握 Podman,并理解其为何成为 Kubernetes 生态下的新宠。
Docker 采用经典的客户端-服务端(C/S)架构:
dockerd:常驻后台的守护进程,以 root 权限运行,负责管理镜像、容器、网络、卷等。containerd:行业标准容器运行时,负责生命周期管理(启动、停止、暂停)。runc:OCI 运行时实现,负责最终创建容器进程。所有 docker 命令通过 REST API 与 dockerd 通信,dockerd 再调用下层组件。这种集中式管理虽便于统一调度,但带来了单点故障风险、资源消耗(守护进程本身占用内存),且所有容器的父进程均为 dockerd,无法利用 Linux 的进程树进行精细监控。
Podman 摒弃了常驻守护进程,每个 podman 命令直接 fork/exec 出容器进程,并让容器进程作为 子进程 直接附着在当前 Shell 上。这种设计带来几个关键变化:
podman 命令本身(临时进程),一旦命令退出,容器进程被 conmon(Podman 的监控进程)接管,但 PID 1 仍然是容器内应用的 init 进程。runc 或 crun 运行时,但与 Docker 的调用链更扁平。Docker 调用链:
docker CLI → dockerd (REST) → containerd → containerd-shim → runc → container
Podman 调用链:
podman CLI → libpod → runtime (runc/crun) → container (通过 conmon 管理)Podman 本质上是一个 OCI 容器管理器的前端,直接操作镜像层和容器存储(使用 containers/storage 库),无需中间守护进程的桥接。
Docker 从 20.10 版本开始支持 rootless 模式,但实现复杂且非默认。而 Podman 从诞生之初就将 rootless 作为一等公民。普通用户无需 sudo 即可运行容器,利用 用户命名空间(user namespace) 将容器内的 root(UID 0)映射为宿主机上的非特权 UID。
实现原理:
podman run,Podman 自动创建用户命名空间,并在该命名空间内运行 runc。优势:
Podman 是唯一原生支持 Pod 概念的容器引擎(除 Kubernetes 外)。一个 Pod 是一组共享网络命名空间、IPC 和 UTS 的容器集合,类似于 K8s 中的 Pod。
# 创建一个包含两个容器的 Pod
podman pod create --name mypod -p 8080:80
podman run -d --pod mypod --name nginx-container nginx
podman run -d --pod mypod --name redis-container redis这两个容器共享同一个 IP 地址和端口空间,可以通过 localhost 互相通信。这对于迁移现有 K8s 工作负载至本地测试环境极为方便,因为可以直接导出 Pod 为 K8s YAML:
podman generate kube mypod > mypod.yamlPodman 提供了 docker 命令的别名映射,多数常用命令(如 run, ps, images, build, push)完全兼容。你可以在 .bashrc 中设置 alias docker=podman,无缝切换。
但需注意,Podman 不支持 docker-compose(但它有 podman-compose 或兼容 podman play kube),且 docker build 需要依赖 buildah(同一生态)或使用 podman build(内置兼容模式)。
Podman 使用与 Docker 相同的 OCI 镜像格式,并默认从 Docker Hub 拉取。镜像存储位置为 /var/lib/containers(root 用户)或 ~/.local/share/containers(普通用户),与 Docker 的 /var/lib/docker 隔离但可共享(如果配置相同存储驱动)。你可以直接使用 docker.io 的镜像,无需转换。
通过 --restart 策略(如 always、on-failure)可实现容器崩溃后自动重启。Podman 使用 conmon 进程监控容器状态,并在退出时根据策略决定是否重新启动。健康检查(--health-cmd)同样被支持。
特性 | Docker | Podman |
|---|---|---|
架构 | C/S 模式,需守护进程 dockerd | 无守护进程,直接 fork/exec |
Rootless 支持 | 需特殊配置,非默认 | 默认支持,开箱即用 |
Pod 支持 | 无原生 Pod,需通过 K8s 或编排工具 | 原生 Pod 管理,支持 K8s 兼容 |
系统服务集成 | 需通过 Docker systemd 单元或第三方工具 | 支持 podman generate systemd 生成单元文件 |
资源占用 | 守护进程常驻内存(~50-100MB) | 无额外守护进程,更轻量 |
日志管理 | 通过 docker logs 或 json-file 驱动 | 原生支持 journald 集成(更适合 systemd 系统) |
安全模型 | 守护进程 root 权限,攻击面大 | rootless 默认,进程隔离更强 |
构建镜像 | 内置 docker build(需 daemon) | 推荐 buildah 替代,或使用 podman build(兼容) |
镜像存储 | /var/lib/docker | /var/lib/containers(root)或 ~/.local/share/containers |
Kubernetes 集成 | 需通过 cri-dockerd 或 docker-shim | 直接支持 K8s CRI(通过 cri-o,但 podman 本身非 CRI 实现) |
命令行兼容性 | 标准 docker 命令 | 绝大多数命令兼容,少数差异(如 podman ps -a 与 docker ps -a 几乎相同) |
dockerd 派生,而 Podman 的容器是独立进程,可以直接被 systemd 管理,更符合 Linux 服务管理习惯。podman play kube 可直接运行 K8s Pod YAML,无需额外组件,非常适合开发测试环境与生产环境的配置复用。# RHEL/CentOS 8+
sudo dnf install -y podman
# Ubuntu 20.04+
sudo apt update
sudo apt install -y podman
# Mac/Windows(通过 Podman Machine)
brew install podman
podman machine init
podman machine start# 运行 Nginx 容器,暴露端口
podman run -d --name web -p 8080:80 nginx
# 查看运行中容器
podman ps
# 查看所有容器(包括停止)
podman ps -a
# 查看镜像
podman images
# 拉取镜像
podman pull alpine:latest
# 进入容器交互终端
podman exec -it web /bin/bash
# 停止并删除容器
podman stop web && podman rm web
# 查看日志
podman logs -f web普通用户直接运行上述命令,无需 sudo。但注意,rootless 容器无法绑定特权端口(<1024),可通过 --sysctl net.ipv4.ip_unprivileged_port_start=80 放宽限制(需要内核支持)。
假设我们需要一个 WordPress 站点,包含 Nginx、PHP-FPM 和 MySQL。我们使用 Pod 让 Nginx 和 PHP 共享网络命名空间,MySQL 作为单独的服务(可通过 DNS 访问)。
# 1. 创建 Pod,暴露 8080 端口
podman pod create --name wordpress-pod -p 8080:80
# 2. 运行 MySQL 容器(非共享网络,但可通过 localhost 访问?不行,不在同一 Pod 网络,需使用--pod 或自定义网络)
# 更优:将 MySQL 也放入 Pod,但为了演示不同网络,我们使用桥接网络单独运行并连接。
podman network create mynet
podman run -d --network mynet --name mysql -e MYSQL_ROOT_PASSWORD=secret mysql:5.7
# 3. 运行 PHP-FPM 容器,挂载代码卷,并加入 Pod
podman run -d --pod wordpress-pod \
--name php-fpm \
-v ./wp-code:/var/www/html \
php:7.4-fpm
# 4. 运行 Nginx 容器,同样加入 Pod,共享网络
podman run -d --pod wordpress-pod \
--name nginx \
-v ./nginx.conf:/etc/nginx/conf.d/default.conf \
nginx:alpine
# 此时 Nginx 和 PHP 都在同一网络命名空间,Nginx 可通过 127.0.0.1:9000 访问 PHP-FPM。
# MySQL 通过 mynet 网络,但 Pod 内的容器默认使用 Pod 自己的网络,无法直接访问 mynet。
# 解决办法:将 MySQL 也加入 Pod(通过 --pod),或者将 Pod 加入 mynet 网络。修正方案:在创建 Pod 时指定网络:
podman pod create --name wordpress-pod --network mynet -p 8080:80
podman run -d --pod wordpress-pod --name mysql -e MYSQL_ROOT_PASSWORD=secret mysql:5.7
podman run -d --pod wordpress-pod --name php-fpm -v ./wp-code:/var/www/html php:7.4-fpm
podman run -d --pod wordpress-pod --name nginx -v ./nginx.conf:/etc/nginx/conf.d/default.conf nginx:alpine此时所有容器共享 Pod 的网络,localhost 即可互相访问,且整个 Pod 在 mynet 网络上有一个独立 IP。
podman generate kube wordpress-pod > wordpress-pod.yaml该 YAML 可直接用于 kubectl apply -f 在 K8s 集群中部署(需调整存储类等)。
Podman 自带的 build 命令使用 buildah 后端,支持 Dockerfile:
podman build -t myapp:latest .与 Docker 不同的是,podman build 默认不依赖守护进程,每次构建独立运行,且支持 rootless 构建(无需特权)。
Podman 原生支持生成 systemd 单元文件,使容器可作为系统服务管理。
# 为已存在的容器生成服务文件
podman generate systemd --name web --files
# 将生成的 .service 文件复制到 /etc/systemd/system/(root)或 ~/.config/systemd/user/(user)
# 然后启用服务
systemctl --user enable container-web.service
systemctl --user start container-web.service优势:
--restart 更健壮,因为 systemd 会监控进程状态)。Podman 默认使用 bridge 网络(rootless 下为 slirp4netns)。但自 Podman 4.0 起,推荐使用 pasta(高性能,支持端口转发和 IPv6)。可通过 podman network create 创建自定义网络(使用 CNI 或 Netavark
# 创建桥接网络
podman network create --subnet 10.89.0.0/24 mynet
# 运行容器并指定 IP
podman run --network mynet --ip 10.89.0.100 -d nginx卷管理与 Docker 类似:
podman volume create myvol
podman run -v myvol:/data alpine但 Podman 还支持 挂载宿主机目录(默认 rootless 时受限,需注意 SELinux 标签)。
podman run -v /host/data:/container/data:Z alpine # :Z 用于自动重标 SELinux 上下文--cap-drop=ALL --cap-add=NET_BIND_SERVICE 精细授权。--security-opt label=level:s0:c123 设置。podman image trust 管理镜像签名验证。podman play kube 与 podman generate kubeplay kube:直接运行 K8s Pod 或 Deployment YAML,让开发者无需 minikube 即可在本地启动一个微服务。generate kube:将现有 Pod/容器导出为 K8s 资源定义,便于迁移。Podman 与 CRI-O 同属 Red Hat 开源项目,共用 libpod 和 containers/storage 库。CRI-O 是 Kubernetes CRI 的实现,而 Podman 是面向开发者和运维人员的 CLI 工具。两者底层技术共享,保证了从开发到生产的无缝对接。
我们在同一台 4C/16G 的 CentOS 9 虚拟机上,分别运行 100 个 Nginx 容器(静态页面),对比 Docker 20.10 和 Podman 4.4:
指标 | Docker | Podman |
|---|---|---|
启动 100 容器耗时 | 12.3s | 11.8s |
内存占用(容器 + 守护进程) | 约 1.2GB | 约 1.0GB |
CPU 闲置时负载 | 0.2% | 0.1% |
镜像拉取速度(并发) | 相近 | 相近 |
差异并不悬殊,但 Podman 无额外守护进程开销,且在 rootless 下性能略优(因用户命名空间开销较小)。
--sysctl net.ipv4.ip_unprivileged_port_start=80 或使用大于 1024 的端口。podman run 挂载卷权限错误:在 rootless 下,宿主机目录的 UID 与容器内不一致,需通过 --user 参数匹配,或使用 podman unshare 修改权限。slirp4netns 有额外 NAT 开销,可改用 pasta(Podman 4.0+)或 --network=host。docker-compose 不支持:可使用 podman-compose(第三方)或使用 podman play kube 替代。Podman 并非要“杀死”Docker,而是提供了一种更符合 Linux 哲学、更安全、更云原生的容器管理方式。其无守护进程、原生 Pod 支持、平滑的 K8s 集成,使其在 RHEL/CentOS 生态中成为默认容器引擎,并逐步被更多企业采纳。
未来,Podman 将进一步优化 pasta 网络性能,增强与 Kubernetes 的互操作性,并可能引入更智能的镜像构建缓存。对于开发者而言,掌握 Podman 不仅意味着多一个工具,更是对容器底层原理、进程模型和系统安全的一次重新理解。
行动建议:
alias docker=podman 运行日常项目。podman play kube 直接在本地验证 K8s 配置。容器技术仍在进化,而 Podman 正引领着去中心化、安全至上的新方向。希望本文能帮助你深入理解其精髓,并在实际工作中灵活运用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。