podman rootless:不需要 root daemon 跑容器(更安全 + systemd 集成)

起因

我的服务器上 Docker daemon 用 root 跑。任何能跟 docker.sock 通信的用户
都等于 root(passing socket 到容器 = 容器逃逸)。多用户 / 共享开发机
上风险大。

podman 是 Red Hat 主导的 OCI 兼容容器引擎,几个区别:

  • no daemon:每个容器一个进程 fork 出来,systemd 直接 supervise
  • rootless first:默认非 root 用户跑
  • Docker CLI 兼容alias docker=podman 大多数命令直接 work
  • 支持 pod(K8s 那种多容器组)

# Debian 12+ / Ubuntu 22.04+
sudo apt install -y podman

# 给当前用户 subuid / subgid(rootless 必须)
# /etc/subuid + /etc/subgid 应当各有:
#   me:100000:65536
# 默认装好就有;没有就:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 me

# 配置 storage(rootless 在 ~/.local/share/containers/)
podman info | grep -A 3 storage

基础用法(跟 Docker 几乎一样)

podman pull nginx:alpine
podman run -d --name web -p 8080:80 nginx:alpine
podman ps
podman logs -f web
podman exec -it web sh
podman stop web
podman rm web

alias docker=podman 后大部分 docker 命令直接用。

rootless 网络

Rootless 容器不能 bind 80/443(< 1024 需 root)。
解决:

# 方案 A:bind 8080,前置 nginx 反代
podman run -d -p 8080:80 nginx

# 方案 B:开 unprivileged port range
sudo sysctl net.ipv4.ip_unprivileged_port_start=80
# 之后 rootless 也能 bind 80

方案 B 安全风险:任何用户能 bind 80。多人共享机器不建议。

podman generate systemd

杀手锏:把 container 转成 systemd unit:

podman run -d --name web -p 8080:80 nginx:alpine

# 生成 unit 文件
podman generate systemd --name web --files --new
# 输出:container-web.service

mkdir -p ~/.config/systemd/user/
mv container-web.service ~/.config/systemd/user/

systemctl --user daemon-reload
systemctl --user enable --now container-web.service

# 开机自启(必须 enable linger)
loginctl enable-linger $USER

之后 systemctl 全程管理 container:

systemctl --user status container-web
systemctl --user restart container-web
journalctl --user -u container-web -f

容器 = systemd service。一致性极佳。

Quadlet(podman 4.4+ 推荐)

更现代的方式:.container 文件直接被 systemd 当 unit:

~/.config/containers/systemd/web.container

[Unit]
Description=Web server

[Container]
Image=docker.io/library/nginx:alpine
PublishPort=8080:80
Volume=/home/me/www:/usr/share/nginx/html:Z
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user start web   # 注意是 web 不是 web.container

比 generate systemd 简洁得多。每改 image / volume / port 直接改 .container
文件 + daemon-reload。

Pod(多容器组)

K8s 风格的 Pod(共享 network + IPC):

podman pod create --name myapp -p 8080:80

podman run -d --pod myapp --name api myapi:latest
podman run -d --pod myapp --name worker myworker:latest

podman pod ls

api 和 worker 共享网络(同 localhost),共享 IPC namespace。
适合"sidecar" 模式。

K8s YAML 直接转 podman pod:

podman play kube k8s-pod.yaml

支持基础 K8s YAML。

auto-update

# 容器加 label
podman run -d --label io.containers.autoupdate=registry nginx:alpine

# 系统级 timer 自动检查 + 升级
systemctl --user enable --now podman-auto-update.timer

每天检查 registry 上 image 新版,有就 pull + restart 容器。Watchtower 等价。

docker-compose 兼容

sudo apt install -y podman-compose
# 或:pip install podman-compose

podman-compose up -d   # 大部分 docker-compose.yml 直接 work

或者用 podman compose(subcommand,跟 Docker Compose v2 接近)。

少数 docker-only feature(如 network_mode: bridge 的特定行为)
偶尔不兼容,看具体场景。

与 Docker 对比

Docker podman
daemon dockerd (root) 无 daemon(每容器自己进程)
rootless 实验性 + 有限 默认 + 一等公民
systemd 集成 极强(Quadlet)
Docker CLI 兼容 原生 极高
Compose docker compose podman-compose / podman compose
K8s 友好 Pod 概念无 原生 Pod
性能 略好(daemon overhead 摊销) 接近
生态成熟度 极高 高 + 增长中
RHEL / Fedora 默认

Red Hat 系(RHEL / CentOS Stream / Fedora)官方推 podman。
其它 distro 都装得上。

适用场景

podman 适合

  • 单机部署 + 喜欢 systemd 集成
  • 多用户开发机(rootless 安全)
  • 不需要 docker swarm
  • RHEL / Fedora 用户
  • K8s 学习(pod 概念友好)

保 Docker 适合

  • 重度用 docker swarm / docker compose 高级 feature
  • 团队工具链都基于 docker
  • 公司 SaaS 工具明确支持 Docker(如 GitHub Actions docker-container action)

我的实际迁移

家用服务器(10+ 容器:nginx / db / 监控 / 个人 app)从 docker 迁
podman + Quadlet:

# ~/.config/containers/systemd/postgres.container
[Container]
Image=postgres:16-alpine
PublishPort=127.0.0.1:5432:5432
Environment=POSTGRES_PASSWORD=...
Volume=postgres-data.volume:/var/lib/postgresql/data
HealthCmd=pg_isready -U postgres
HealthInterval=10s

[Install]
WantedBy=default.target

每个容器一个 .container 文件,git 管。
重启服务器后 systemd 自动按顺序起所有容器。

效果

  • 总内存:少 200 MB(无 dockerd 常驻)
  • 部署 / 重启容器跟 systemctl service 一致
  • 容器崩溃 / OOM / 异常退出:journal 里完整 log + restart 历史
  • rootless 让"容器漏洞 → 拿 host root" 这条路被堵
  • 一致 backup:备份 ~/.local/share/containers + ~/.config = 完整状态

踩过的坑

  1. rootless 网络默认 slirp4netns 慢:吞吐量大幅低于 root docker。
    高流量场景配 pasta (新版默认) 或者 root pod。

  2. rootless 不能跨用户访问 image:每个用户独立 storage。
    podman --root=/path/to/system-storage 可以共享但复杂。

  3. 某些 docker-only env var:如 DOCKER_HOST socket 路径。
    podman 用 unix socket 在 $XDG_RUNTIME_DIR/podman/podman.sock

  4. selinux 标签:volume mount 缺 :Z / :z 后缀容器内访问权限错。
    -v /path:/path:Z 让 SELinux 自动 relabel。

  5. GPU passthrough:rootless + GPU 不直接 work。--device /dev/nvidia*

  6. udev rule 配。生产推荐 root podman 跑 GPU 容器。
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。