rootless 容器的运维
目标
以 rootless 模式运行容器,亲自确认卷、用户映射和端口限制,并编写 Quadlet 单元文件。
为什么重要
从 docker 迁移到 podman 时,一定会遇到一些固定问题。restart: always 并不表示开机自动启动(因为没有守护进程),无法绑定 1024 以下的端口,卷中文件的所有者会显示为陌生的 UID,而在 SELinux 环境中,如果没有 :Z 标签,就会出现 Permission denied。提前了解这四点,迁移只需 30 分钟;不了解则要花半天。
而在 podman 中,开机自动启动的正确答案是 Quadlet。放置 .container 文件后,systemd 会读取它并生成服务单元。podman generate systemd 已经过时。
本实验在虚拟机中运行
rootless podman 会真正运行。 在 Pod 中,newuidmap 需要 CAP_SETUID 才能分配 subuid 范围,因此本实验过去是不完整的——容器无法真正启动,评分也只能检查文件。
在 VM 中可以直接使用 podman run,第 7 步的 Quadlet 也会真正转换为服务并启动。你可以使用 systemctl --user status labhub-web 亲自确认。首次启动需要一分多钟。
步骤
- 创建
/root/podrun目录,以podster权限加载/opt/images/labhub-alpine.tar,然后将镜像列表保存为/root/podrun/images.txt。 - 使用该镜像运行一次容器并执行
echo labhub-ok,将输出保存为/root/podrun/run.txt。 - 将
/home/podster/data挂载到容器的/data,在容器内创建/data/hello.txt,然后从主机确认该文件可见,并将文件路径和内容保存到/root/podrun/volume.txt。 - 将第 3 步所建文件的主机端所有者 UID作为一行数字写入
/root/podrun/owner.txt。 - 尝试发布特权端口并使其失败,将错误消息保存为
/root/podrun/port-fail.txt。然后改用 8090 端口重试,并将成功结果保存为/root/podrun/port-ok.txt。 - 以
labhub-web为名称在后台运行容器,并将podman ps的输出保存为/root/podrun/ps.txt。列表中必须显示该名称。 - 编写
/home/podster/.config/containers/systemd/labhub-web.container。必须包含[Unit]、[Container]、[Install]三个节;[Container]中必须有Image、PublishPort、Volume三个键,[Install]中必须有WantedBy。 - 将
/root/podrun/diff.txt创建为以下 6 行。DAEMON=none/STORAGE=<podster 의 graphRoot 절대 경로>/PRIVPORT=denied/RESTART=quadlet/GPUFLAG=--device/SELINUX_LABEL=:Z
参考
- 运行形式为
su - podster -c "podman run --rm localhost/labhub-alpine:1 sh -c 'echo labhub-ok'"。 - 使用
-v /home/podster/data:/data:Z挂载卷。 - 后台运行使用
-d --name labhub-web。请不要将其停止,保持容器运行。 - Quadlet 文件不必实际运行。只对语法和必需键进行评分。
- 本节点上的容器运行时可能受到限制。 如果禁止创建非特权 user namespace,rootless podman 会以
cannot re-exec process结束。即使如此,也要实际执行命令,并将其输出(包括错误)原样保存到指定文件中——评分器会检测环境,并根据这些记录和编写的配置进行判定。 - 常见错误 1:在第 3 步中以 root 所有权创建卷目录,导致 podster 无法写入。
- 常见错误 2:在第 5 步中只尝试从标准输出获取失败消息。错误会写入标准错误。
加载镜像
创建 /root/podrun 目录,以 podster 权限加载 /opt/images/labhub-alpine.tar,然后将镜像列表保存为 /root/podrun/images.txt。
由于没有互联网,请使用预先准备的归档。加载后在列表中确认。
运行容器
使用该镜像运行一次容器并执行 echo labhub-ok,将输出保存为 /root/podrun/run.txt。
一次性运行时,请添加退出后清理容器的选项。将输出保存到文件中。
挂载卷
将 /home/podster/data 挂载到容器的 /data,在容器内创建 /data/hello.txt,然后从主机确认该文件可见,并将文件路径和内容保存到 /root/podrun/volume.txt。
用冒号连接主机路径和容器路径。还应添加标签选项,以兼容 SELinux 环境。
确认用户映射
将第 3 步所建文件的主机端所有者 UID作为一行数字写入 /root/podrun/owner.txt。
从主机查看卷中创建的文件时,其所有者会显示为映射后的 UID。使用 keep-id 选项时,结果会不同。
重现特权端口失败
尝试发布特权端口并使其失败,将错误消息保存为 /root/podrun/port-fail.txt。然后改用 8090 端口重试,并将成功结果保存为 /root/podrun/port-ok.txt。
尝试发布 1024 以下的端口会失败。原样保存该错误消息,然后改用较高端口重试。
确认正在运行的容器
以 labhub-web 为名称在后台运行容器,并将 podman ps 的输出保存为 /root/podrun/ps.txt。列表中必须显示该名称。
指定名称后启动,管理起来更容易。有一个选项可指定输出格式。
编写 Quadlet 单元
编写 /home/podster/.config/containers/systemd/labhub-web.container。必须包含 [Unit]、[Container]、[Install] 三个节;[Container] 中必须有 Image、PublishPort、Volume 三个键,[Install] 中必须有 WantedBy。
这是扩展名为 .container 的文件。[Container] 节中必须包含 Image,也需要 [Install] 节。
总结与 docker 的差异
将 /root/podrun/diff.txt 创建为以下 6 行。
DAEMON=none / STORAGE=<podster 의 graphRoot 절대 경로> / PRIVPORT=denied / RESTART=quadlet / GPUFLAG=--device / SELINUX_LABEL=:Z
各项值必须是你在前面的步骤中实际确认过的值。存储路径请填写绝对路径。