痛点
Docker 在生产环境有几个绕不开的问题:
- dockerd 单点故障 — daemon 挂了,所有容器失联,
docker ps都执行不了,运维只能盲操作 - Root 权限依赖 — 默认要 root 或 docker 组,一旦容器逃逸直接拿到宿主机最高权限
- 与 systemd 割裂 — 容器生命周期靠 dockerd 管理,和系统服务管理体系两套逻辑,重启策略、日志收集都要单独配
Podman 是红帽主导的开源容器引擎,API 兼容 Docker,但架构完全不同 — 无 daemon、fork-exec 模型、原生支持 rootless 和 systemd 集成。以下用 3 个实操场景说明怎么迁移。
方案概览
| 对比项 | Docker | Podman |
|---|---|---|
| 架构 | C/S,依赖 dockerd | 无 daemon,直接调用 OCI runtime |
| 权限 | 默认需 root | 原生 rootless |
| systemd 集成 | 需第三方工具 | podman generate systemd 原生生成 |
| CLI 兼容 | — | alias docker=podman 直接替换 |
| 镜像格式 | OCI/Docker | OCI/Docker(完全兼容) |
实操步骤
场景一:无 daemon 运行容器(消除单点故障)
# CentOS/RHEL 9+ / Fedora
sudo dnf install -y podman
# Ubuntu 22.04+
sudo apt install -y podman
# 验证安装(注意没有任何 daemon 进程)
podman --version
# podman version 4.9.3
# 直接运行容器 — 无需启动任何后台服务
podman run -d --name nginx-test -p 8080:80 docker.io/library/nginx:alpine
# 验证容器运行
podman ps
curl -s http://localhost:8080 | head -5
# 关键区别:kill 掉 podman 进程,容器依然运行
# 因为容器进程由 conmon 托管,和 podman CLI 解耦
ps aux | grep conmon
场景二:Rootless 容器(非 root 用户运行生产服务)
# 用普通用户运行(无需 sudo、无需加入 docker 组)
# 前提:确保 /etc/subuid 和 /etc/subgid 已配置
grep $USER /etc/subuid
# ops:100000:65536
# 如果没有,手动添加:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
# 普通用户直接运行容器
podman run -d --name redis-rootless \
-p 6379:6379 \
docker.io/library/redis:7-alpine
# 验证:容器进程属于当前用户,非 root
ps aux | grep redis-server
# ops 12345 ... redis-server
# 端口绑定 >1024 无需特权;如需 80/443,用 sysctl 放开:
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
场景三:systemd 原生集成(容器即服务)
# 先创建容器
podman create --name monitoring-agent \
-v /proc:/host/proc:ro \
-p 9100:9100 \
docker.io/prom/node-exporter:latest \
--path.procfs=/host/proc
# 生成 systemd unit 文件
podman generate systemd --new --name monitoring-agent \
--restart-policy=always > ~/.config/systemd/user/container-monitoring-agent.service
# 启用并启动(用户级 systemd,无需 root)
systemctl --user daemon-reload
systemctl --user enable --now container-monitoring-agent.service
# 查看状态 — 和普通 systemd 服务一样管理
systemctl --user status container-monitoring-agent
# 设置 lingering,让用户服务在登出后持续运行
sudo loginctl enable-linger $USER
# 日志直接走 journald,统一收集
journalctl --user -u container-monitoring-agent -f
避坑指南
坑 1:镜像拉取默认不走 Docker Hub
Podman 默认搜索多个 registry,可能报 short-name resolution 错误。
# 解决:指定完整镜像路径
podman pull docker.io/library/nginx:alpine # ✅
podman pull nginx:alpine # ❌ 可能交互确认
# 或配置默认 registry
cat >> /etc/containers/registries.conf <<EOF
unqualified-search-registries = ["docker.io"]
EOF
坑 2:Rootless 模式下 volume 权限问题
容器内 UID 经过 user namespace 映射,挂载目录可能出现权限拒绝。
# 现象
podman run -v /data/app:/app myimage
# Permission denied
# 解决:用 :Z 标签让 Podman 自动修复 SELinux 上下文
podman run -v /data/app:/app:Z myimage
# 或使用 --userns=keep-id 保持 UID 映射一致
podman run --userns=keep-id -v /data/app:/app myimage
坑 3:Docker Compose 项目迁移
Podman 4.x+ 原生支持 podman compose,但部分 Compose 特性有差异。
# 安装 podman-compose(Python 实现)
pip install podman-compose
# 或使用 Docker Compose 本身(Podman 提供 socket 兼容)
systemctl --user enable --now podman.socket
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
docker-compose up -d # 直接使用原有 docker-compose.yml
总结
| 迁移场景 | 核心收益 | 迁移成本 |
|---|---|---|
| 无 daemon | 消除 dockerd 单点,容器独立存活 | 几乎为零,CLI 兼容 |
| Rootless | 容器逃逸也只拿到普通用户权限 | 需配 subuid/subgid,注意 volume 权限 |
| systemd 集成 | 容器即服务,统一生命周期和日志 | 一条命令生成 unit 文件 |
迁移建议:新部署直接用 Podman;存量 Docker 环境先在非核心服务试点,alias docker=podman 验证兼容性后再全量切换。对于 K8s 节点,containerd 仍是首选运行时,Podman 更适合单机或边缘节点的容器化场景。