LabHub
学习 学习路径 课程

Rootless Podman 运维

迁移时真正会绊住的地方

在 LabHub 中继续学习

一句话总结

从 docker 迁移到 podman,不是替换命令,而是更换运维模型:从守护进程转向 fork-exec,从 rootful 转向 rootless,从 daemon.json 转向 containers.conf 配置家族。

概念图: 更换运维模型 · 没有守护进程 · conmon · crun

为什么需要了解这些差异

设置 alias docker=podman 后,大多数命令可以原样运行,因此迁移看起来很容易。但真正开始搬迁时,一定会在几个地方遇到问题。提前了解这些问题,只需 30 分钟;毫无准备,则可能浪费半天。

它是如何工作的

架构差异

Docker:  docker CLI ──REST──▶ dockerd ──▶ containerd ──▶ runc ──▶ 컨테이너
Podman:  podman CLI ──fork/exec──▶ conmon ──▶ crun ──▶ 컨테이너

podman 没有守护进程podman run 会像普通进程一样直接创建容器。每个容器都有一个小型监控进程 conmon,负责管理标准输入输出与退出码;实际创建工作则由 OCI 运行时 crun 完成,它使用 C 编写,比 runc 更轻量。

这种架构的影响很大。

必然遇到的五个问题

1. restart: always 的行为不同。 由于没有守护进程,它并不表示“开机时自动启动”。podman 的正确做法是使用 Quadlet

# ~/.config/containers/systemd/web.container
[Unit]
Description=Web service

[Container]
Image=docker.io/library/nginx:1.27
PublishPort=8080:80
Volume=/srv/www:/usr/share/nginx/html:Z

[Service]
Restart=always

[Install]
WantedBy=default.target

放置 .container 文件后,systemd 会读取它并生成服务单元。podman generate systemd 已经成为旧式做法。

2. SELinux 卷标签。 在 RHEL/Fedora 上,如果挂载卷时出现 Permission denied,十有八九是 SELinux 导致的。应添加 -v ./data:/data:Z(专用)或 :z(共享)标签。从 Ubuntu 迁来的 compose 文件最容易踩中这个陷阱。

3. 非限定镜像名称。 podman pull nginx 的行为与 docker 不同。可以在 registries.conf 中加入 unqualified-search-registries = ["docker.io"],或者在 CI 中始终使用完整路径。

4. 存储位置。 rootless 镜像位于 ~/.local/share/containers/storage。如果沿用 docker system df 的思路去 /var/lib 下寻找,就什么也找不到。

5. 低于 1024 的端口。 rootless 模式无法绑定特权端口。

如何使用 compose

有两种方法。

面向生产环境时,还可以不用 compose,改选 podman kube play,直接运行 Kubernetes YAML。这样就能统一本地环境与集群使用的清单。

实际工作中会遇到的情况

可以逐步迁移。 两个引擎的存储彼此分离,所以能够在同一台机器上共存。按服务逐个迁移即可。

podman-docker 软件包。 这是一个把 docker 命令连接到 podman 的 shim 软件包,对脚本兼容很有帮助;但必须告知团队成员,“这台服务器上的 docker 实际是 podman”。 否则,照着 docker 文档运行的命令出现细微行为差异时,就很难找到原因。

下一个实验要做什么

加载镜像并运行容器,检查卷与用户映射,复现特权端口失败,并编写 Quadlet .container 文件。最后,把与 docker 的差异及其证据整理成表格。