跳到主要内容

故障排除

Podman 常见问题和解决方案列表


1 多种问题 - 验证版本

针对Podman报告的大量问题中,很多已经被项目的更新版本所修复。在报告问题之前,请使用podman version命令验证您正在运行的版本,并将其与Podman的README顶部记录的最新版本进行比较。

如果它们不同,请将您的PODMAN版本更新到最新可能的版本,并在报告问题之前重新尝试您的命令。


2 无法使用卷挂载,出现权限被拒绝

podman run -v ~/mycontent:/content fedora touch /content/file
touch: cannot touch '/content/file': Permission denied

解决方案

这有时是由SELinux引起的,有时是由用户命名空间引起的。

像SELinux这样的标记系统要求为挂载到容器中的卷内容设置适当的标签。如果没有标签,安全系统可能会阻止在容器内部运行的进程使用内容。默认情况下,Podman不会更改操作系统设置的标签。

要在容器上下文中更改标签,可以在卷挂载时添加两个后缀:z:Z之一。这些后缀告诉Podman重新标记共享卷上的文件对象。z选项告诉Podman两个容器共享卷内容。因此,Podman使用共享内容标签标记内容。共享卷标签允许所有容器读写内容。Z选项告诉Podman使用私有非共享标签标记内容。只有当前容器可以使用私有卷。

podman run -v ~/mycontent:/content:Z fedora touch /content/file

确保内容对容器是私有的。不要重新标记系统目录和内容。 重新标记系统内容可能会导致您机器上的其他受限服务失败。对于这些类型的容器,我们建议禁用SELinux隔离。选项--security-opt label=disable将禁用容器的SELinux隔离。

podman run --security-opt label=disable -v ~:/home/user fedora touch /home/user/file

然而,如果容器镜像以特定的非root用户身份运行,比如Jupyter Notebook镜像(以“jovyan”用户身份运行)和Postgres镜像(以“postgres”用户身份运行),解决方案是修复用户命名空间。这包括像Jupyter Notebook镜像和Postgres镜像这样的容器镜像。在任何情况下,使用--userns开关来映射用户命名空间,大多数情况下使用keep-id选项。

podman run -v "$PWD":/home/jovyan/work --userns=keep-id jupyter/scipy-notebook

3 找不到镜像或裸键不能包含':'

执行podman pullpodman build命令时,如果无法拉取一个“通用”镜像,很可能是/etc/containers/registries.conf文件未安装或配置错误。

示例

sudo podman build -f Dockerfile
STEP 1: FROM alpine
error building: error creating build container: no such image "alpine" in registry: image not known

或者

sudo podman pull fedora
error pulling image "fedora": unable to pull fedora: error getting default registries to try: Near line 9 (last key parsed ''): Bare keys cannot contain ':'.

解决方案

  • 当从 Podman 向注册中心发送命令(如 buildcommitpullpush)时,默认会启用 TLS 验证。如果这些命令没有使用加密,可能会出现此错误。
  • 如果注册中心不需要加密,那么除非使用 --tls-verify 选项关闭 TLS 验证,否则 Podman 命令(如 buildcommitpullpush)将会失败。注意:强烈建议不要在与注册中心通信时不使用 TLS 验证。

要解决这个问题,您可以在 Podman 命令中使用 --tls-verify=false 选项来禁用 TLS 验证。例如:

sudo podman push --tls-verify=false alpine docker://localhost:5000/myalpine:latest

然而,请牢记,这样做会削弱通信的安全性,因为它不再通过加密的 HTTPS 连接进行通信。在生产环境中,始终应该使用加密来保护数据传输的机密性和完整性。

此外,如果您正在尝试与本地未加密的 Docker 注册表通信,您可能需要确保注册表配置为监听 HTTP 而不是 HTTPS,并在 Podman 命令中相应地引用它。如果您确实需要这样做,请确保您的网络环境是安全的,并且只有受信任的系统可以访问这个未加密的注册表。

总之,尽管关闭 TLS 验证可以解决某些通信问题,但这不是一个安全的做法,应当尽量避免。如果可能,请确保您的注册中心和 Podman 通信都使用了加密的 HTTPS 连接。

  • 通过将 tls-verify 选项设置为 false 来关闭 TLS 验证。
  • 例如:podman push --tls-verify=false alpine docker://localhost:5000/myalpine:latest

对于全局解决方案,用户[1]可以创建文件 /etc/containers/registries.conf.d/registry-NAME.conf(将 NAME 替换为此注册表的名称),其中包含以下内容(将 FULLY.QUALIFIED.NAME.OF.REGISTRY 替换为此注册表的地址):

[[registry]]
location = "FULLY.QUALIFIED.NAME.OF.REGISTRY"
insecure = true

[1] 如果您使用的是 Mac/Windows,那么在向 registry-conf 文件添加不安全条目之前,您应该执行 podman machine ssh 命令登录到 podman 机器。

这是一种不安全的方法,应谨慎使用。


5 无特权容器无法 ping 主机

当从无特权容器中使用 ping 命令时,可能会因为缺少权限而失败。

症状

podman run --rm fedora ping -W10 -c1 redhat.com
PING redhat.com (209.132.183.105): 56 数据字节

--- redhat.com ping 统计信息 ---
1 个数据包已传输,0 个数据包接收到,100% 数据包丢失

解决方案

很可能需要在主机上启用无特权 ping。 确保用户的 UID 属于 /proc/sys/net/ipv4/ping_group_range 文件中定义的范围。

要更改其值,可以使用类似以下命令:

 sysctl -w "net.ipv4.ping_group_range=0 2000000"

要使更改持久化,您需要在 /etc/sysctl.d 中添加一个文件,其中包含 net.ipv4.ping_group_range=0 $MAX_UID

请注意:更改此设置可能会带来安全风险,因为它允许更多用户执行网络 ping 命令。因此,在调整这些设置时请务必谨慎。

6 当 Dockerfile 包含 useradd 命令时构建会挂起

如果 Dockerfile 中包含类似 RUN useradd -u 99999000 -g users newuser 的命令,构建过程可能会挂起。

症状

如果在 Dockerfile 中使用具有大 UID/GID 的 useradd 命令,它将创建一个大的稀疏文件 /var/log/lastlog。这可能导致构建过程永远挂起。Go 语言不支持稀疏文件,这可能会导致在容器镜像中创建一些巨大的文件。

解决方案

如果 Dockerfile 中的条目看起来像这样:RUN useradd -u 99999000 -g users newuser,则可以通过添加 --no-log-init 参数来修改它,变为:RUN useradd --no-log-init -u 99999000 -g users newuser。这个选项告诉 useradd 停止创建 lastlog 文件。

7 运行 Podman 命令时权限被拒绝

当无特权 Podman 尝试在非 exec 主目录中执行容器时,会引发权限错误。

症状

如果您在挂载为 noexec 的主目录上运行 Podman 或 Buildah,它们将失败并显示如下消息:

podman run centos:7
standard_init_linux.go:203: exec user process caused "permission denied"

解决方案

由于系统管理员将您的主目录设置为 noexec,因此您将无法从主目录中的存储执行容器。您可以通过手动指定一个不在 noexec 挂载上的容器存储路径来解决这个问题。只需将文件 /etc/containers/storage.conf 复制到 ~/.config/containers/(如果必要,则创建该目录)。指定一个不在 noexec 挂载点上的 graphroot 目录,并且您对该目录具有读/写权限。您还需要将其他字段修改为可写目录。

8 在 Podman 容器中运行 systemd 时权限被拒绝

当在启用了 SELinux 的机器上的容器内部以 PID 1 的身份运行 systemd 时,它需要写入 cgroup 文件系统。

症状

当 systemd 尝试写入 cgroup 文件系统时,会收到权限拒绝的错误,同时 AVC 消息会开始在系统的 audit.log 文件或日志中显示。

解决方案

Podman 的新版本(2.0 或更高版本)支持使用不同的 SELinux 标签来运行基于 init 的容器,这允许容器进程访问 cgroup 文件系统。此功能需要 container-selinux-2.132 或更高版本。

在 Podman 2.0 之前,SELinux 布尔值 container_manage_cgroup 允许容器进程写入 cgroup 文件系统。在启用了 SELinux 的系统上,打开这个布尔值,以允许 systemd 在容器中正确运行。仅对运行 Podman 旧版本的系统执行此操作。

# setsebool -P container_manage_cgroup true

9 运行无特权 Podman 命令时缺少 newuidmap

无特权 Podman 需要安装 newuidmap 和 newgidmap 程序。

症状

如果您以无特权用户身份运行 Podman 或 Buildah,可能会收到一个关于缺少 newuidmap 可执行文件的错误。

podman run -ti fedora sh
rootless 模式与多个 ID 所需的命令:exec: "newuidmap":在 $PATH 中未找到可执行文件

解决方案

要解决这个问题,您需要安装 uidmap 包,它通常包含 newuidmapnewgidmap。根据您使用的 Linux 发行版,安装命令会有所不同。例如,在基于 Fedora 的系统上,您可以使用以下命令:

sudo dnf install shadow-utils

或者,在基于 Debian 的系统上:

sudo apt-get install uidmap

安装完成后,请确保 newuidmapnewgidmap 可执行文件位于无特权用户的 $PATH 环境变量中指定的目录中,以便 Podman 能够找到它们。在某些情况下,您可能还需要设置适当的 SELinux 策略,以允许无特权用户执行这些程序。

解决方案

安装包含这些可执行文件的 shadow-utils 版本。请注意,对于 RHEL 和 CentOS 7,至少需要安装 7.7 版本以支持这些功能。

10 rootless 设置用户:无效参数

无特权 Podman 要求运行它的用户在 /etc/subuid 和 /etc/subgid 文件中拥有一个 UID 范围。

症状

用户(通过 --user 参数或镜像的默认配置)在命名空间中没有被映射。

podman run --rm -ti --user 1000000 alpine echo hi
错误:容器创建失败:container_linux.go:344: 启动容器进程时出错 "setup user: invalid argument"

解决方案

更新 /etc/subuid 和 /etc/subgid 文件,为每个用户添加字段,如下所示:

cat /etc/subuid
johndoe:100000:65536
test:165536:65536

这些文件的格式是 用户名:起始UID:UID范围

  • 用户名,与 /etc/passwdgetpwent 中列出的用户名相同。
  • 分配给用户的初始 UID。
  • 分配给用户的 UID 范围的大小。

这意味着 johndoe 被分配了 UID 100000-165535,以及他在 /etc/passwd 文件中的标准 UID。

您应该确保每个用户都有唯一的 UID 范围,因为重叠的 UID 可能会让一个用户攻击另一个用户。此外,请确保您分配的 UID 范围能够覆盖容器所需的所有 UID。例如,如果容器有一个 UID 为 10000 的用户,请确保您至少有 10000 个子 UID;如果容器需要以 UID 为 1000000 的用户身份运行,请确保您至少有 1000000 个子 UID。

您还可以使用 usermod 程序为用户分配 UID。

如果您更新了 /etc/subuid/etc/subgid 文件,您需要停止所有正在运行的容器并终止暂停进程。这可以通过 system migrate 命令自动完成,该命令也可以用于停止所有容器并终止暂停进程。

# usermod --add-subuids 200000-201000 --add-subgids 200000-201000 johndoe
# grep johndoe /etc/subuid /etc/subgid
/etc/subuid:johndoe:200000:1001
/etc/subgid:johndoe:200000:1001

11 更改 Graphroot 的位置导致权限被拒绝

当我在 storage.conf 文件中更改 graphroot 存储位置时,下次运行 Podman 时,我收到类似这样的错误:

# podman run -p 5000:5000 -it centos bash

bash: error while loading shared libraries: /lib64/libc.so.6: cannot apply additional memory protection after relocation: Permission denied

例如,管理员设置了一个备用磁盘并将其挂载在 /src/containers,然后将 storage.conf 指向此目录。

症状

SELinux 阻止容器使用任意位置作为 overlay 存储。这些目录需要使用与 /var/lib/containers/storage 下内容相同的标签进行标记。

解决方案

通过设置一个等价记录来告诉 SELinux 新的容器存储位置。这将告诉 SELinux 对新路径下的内容进行标记,就好像它们存储在 /var/lib/containers/storage 下一样。

# semanage fcontext -a -e /var/lib/containers /srv/containers
# restorecon -R -v /srv/containers

上面的 semanage 命令告诉 SELinux 设置 /srv/containers 的默认标签以匹配 /var/lib/containersrestorecon 命令告诉 SELinux 将标签应用到实际的内容上。

现在,在这些目录中创建的所有新内容都将自动拥有正确的标签。

12 匿名镜像拉取失败,出现“无效的用户名/密码”错误

拉取不需要身份验证的匿名镜像时,可能会出现“无效的用户名/密码”错误。

症状

如果你拉取一个匿名镜像,即不需要凭据的镜像,但是你在身份验证文件中为目标容器注册表建立了不再有效的凭据,那么你可能会收到“无效的用户名/密码”错误。

podman run -it --rm docker://docker.io/library/alpine:latest ls
尝试拉取 docker://docker.io/library/alpine:latest...ERRO[0000] 拉取镜像引用 //alpine:latest 时出错:确定 docker://alpine:latest 的清单 MIME 类型时出错:无法检索认证令牌:无效的用户名/密码
失败
错误:无法拉取 docker://docker.io/library/alpine:latest:无法拉取镜像:确定 docker://alpine:latest 的清单 MIME 类型时出错:无法检索认证令牌:无效的用户名/密码

如果身份验证文件被“手动”修改,或者本地建立了凭据,然后在容器注册表中更新了密码,就可能出现这种情况。

解决方案

根据使用哪个容器工具建立的凭据,使用 podman logoutdocker logout 从身份验证文件中删除凭据。

13 在容器内部运行 Podman 导致容器崩溃和状态不一致

在容器内部运行 Podman 并转发一些但不是所有必需的主机目录,可能会导致容器行为不一致。

症状

在通过 Podman 创建容器并将存储目录从主机挂载到容器内,然后在容器内部运行 Podman 后,所有容器的状态都显示为“已配置”或“已创建”,即使它们原本是运行或停止状态。

解决方案

在容器内部运行 Podman 时,建议至少将 /var/lib/containers/storage/ 作为卷进行挂载。 通常,你不会挂载主机版本的目录,但如果你希望与主机共享容器,可以这样做。 然而,如果你确实挂载了主机的 /var/lib/containers/storage,你也必须挂载主机的 /run/libpod/run/containers/storage 目录。 如果不这样做,将导致容器内的 Podman 检测到临时文件已被清除,从而假设系统已重启。 这可能导致 Podman 重置容器状态并丢失对正在运行的容器的跟踪。

对于在容器内部从主机运行容器,我们还推荐使用 Podman 远程客户端,它只需要将单个套接字挂载到容器中。

14 在 NFS 上使用无根用户执行 'podman build' 时遇到 EPERM 错误:

NFS 强制在服务器端对不同 UID 进行文件创建,并且不理解用户命名空间,而无根 Podman 需要它。 当容器内的 root 进程(如 YUM)尝试以不同 UID 创建文件时,NFS 服务器会拒绝创建操作。 当存储位于 NFS 上时,文件锁也会成为问题。其他分布式文件系统(例如:Lustre、Spectrum Scale、General Parallel File System (GPFS))在以无根模式运行时也不受支持,因为这些文件系统不了解用户命名空间。

症状

podman build .
ERRO[0014] 应用层时出错:ApplyLayer 退出状态 1 标准输出: 标准错误:打开 /root/.bash_logout:权限被拒绝
创建构建容器时出错:提交完成的镜像时出错:使用 blob "sha256:a02a4930cb5d36f3290eb84f4bfa30668ef2e9fe3a1fb73ec015fc58b9958b17" 添加层时出错:ApplyLayer 退出状态 1 标准输出: 标准错误:打开 /root/.bash_logout:权限被拒绝

解决方案

选择以下方案之一:

  • 在不同的目录(非 NFS 共享目录)中设置 containers/storage。
  • 在本地文件系统上创建一个目录。
  • 编辑 ~/.config/containers/containers.conf 并将 volume_path 选项指向该本地目录。(如果 ~/.config/containers/containers.conf 不存在,请复制 /usr/share/containers/containers.conf
  • 否则,只需以 root 用户身份运行 Podman,使用 sudo podman

15 在使用 OverlayFS 时,无根用户 'podman build' 失败

Overlay 文件系统(OverlayFS)在提取镜像时需要调用 mknod 命令来创建空白文件(whiteout 文件)。 但是,无根用户没有执行这种操作的权限来使用 mknod

15 使用 OverlayFS 时无根用户 'podman build' 失败的症状和解决方案:

症状

podman build --storage-driver overlay .
STEP 1: FROM docker.io/ubuntu:xenial
获取镜像源签名
复制 blob edf72af6d627 完成
复制 blob 3e4f86211d23 完成
复制 blob 8d3eac894db4 完成
复制 blob f7277927d38a 完成
复制配置 5e13f8dd4c 完成
写入镜像目标
存储签名
错误:创建构建容器时出错:提交完成的镜像时出错:添加带有 blob "sha256:8d3eac894db4dc4154377ad28643dfe6625ff0e54bcfa63e0d04921f1a8ef7f8" 的层时出错:处理 tar 文件时出错(退出状态 1):操作不允许
podman build .
ERRO[0014] 应用层时出错:ApplyLayer 退出状态 1 标准输出: 标准错误:打开 /root/.bash_logout:权限被拒绝
创建构建容器时出错:提交完成的镜像时出错:使用 blob "sha256:a02a4930cb5d36f3290eb84f4bfa30668ef2e9fe3a1fb73ec015fc58b9958b17" 添加层时出错:ApplyLayer 退出状态 1 标准输出: 标准错误:打开 /root/.bash_logout:权限被拒绝

解决方案

选择以下方案之一:

  • 以特权用户身份完成构建操作。
  • 安装并配置 fuse-overlayfs。
  • 安装适用于您 Linux 发行版的 fuse-overlayfs 包。
  • 在您的 ~/.config/containers/storage.conf 文件的 [storage.options] 部分下添加 mount_program = "/usr/bin/fuse-overlayfs"

16 基于 RHEL 7 和 CentOS 7 的 init 镜像不支持 cgroup v2

RHEL 7 和 CentOS 7 中附带的 systemd 版本不支持 cgroup v2。对 cgroup v2 的支持需要 systemd 版本 230 或更高版本, 而 RHEL 7 或 CentOS 7 从未发布或支持这些版本。

16 RHEL 7 和 CentOS 7 基础的 init 镜像不支持 cgroup v2

RHEL 7 和 CentOS 7 系统中附带的 systemd 版本不支持 cgroup v2。cgroup v2 的支持需要 systemd 版本 230 或更高版本, 而 RHEL 7 或 CentOS 7 从未发布或支持这些版本。因此,如果你尝试在这些系统上运行基于 cgroup v2 的容器或 init 镜像,可能会遇到问题。

17 rootless 容器在用户会话退出后也会退出

你需要通过 loginctl 设置 lingering 模式,以防止用户会话完成后用户进程被杀死。

症状

用户登出后,所有容器也会退出。

解决方案

# loginctl enable-linger $UID

18 podman run 失败,出现 "bpf create: permission denied error"

当 BIOS 中启用了 Secure Boot 时,Kernel Lockdown 补丁会拒绝 eBPF 程序。[Matthew Garrett 的文章](https://mjg59.dreamwidth.org/50577.html 描述了 Lockdown 和 Secure Boot 之间的关系,而 [Jan-Philip Gehrcke 的文章](https://gehrcke.de/2019/09/running-an-ebpf-program-may-require-lifting-the-kernel-lockdown/ 则将这一关系与 eBPF 联系起来。[RH bug 1768125](https://bugzilla.redhat.com/show_bug.cgi?id=1768125 包含了一些额外的细节。

症状

尝试运行 podman 时出现以下错误:

Error: bpf create: Operation not permitted: OCI runtime permission denied error

解决方案

一个解决方法是在 BIOS 中禁用 Secure Boot。

19 创建 libpod 运行时出错:命名空间中可能没有足够的 ID 可用

无法拉取镜像

解决方案

这个错误通常是因为用户所在的用户命名空间中的 UID 或 GID 不足。在 rootless 模式下运行 Podman 时,Podman 会尝试在用户命名空间中为用户容器分配唯一的 UID 和 GID。如果命名空间中没有足够的 ID,就会出现这个错误。

你可以尝试以下解决方案:

  • 增加用户命名空间的 ID 范围:编辑 /etc/subuid/etc/subgid 文件,为用户分配更多的子 UID 和子 GID。
  • 清理不再使用的容器和镜像:通过删除不再需要的容器和镜像来释放一些 ID。
  • 检查是否有其他进程占用了 ID:有时候,其他进程可能已经占用了用户命名空间中的 ID。确保没有其他进程与 Podman 竞争这些 ID。

如果以上方法都不能解决问题,你可能需要进一步检查你的系统配置或寻求更专业的帮助。

20 无法在 rootless 容器中访问传入的设备或文件

作为非 root 用户,你可能拥有对某个设备或文件的组访问权限,并希望使用 --device=...--volume=... 将其传入 rootless 容器中。

症状

在容器内部的任何访问都会被拒绝,显示“Permission denied”。

解决方案

运行时使用 setgroups(2),因此进程会失去非 root 用户拥有的所有附加组。使用 --group-add keep-groups 标志可以将用户的辅助组访问权限传递给容器。目前仅 crun OCI 运行时支持此功能。

21 以分离模式运行的 rootless 容器在注销时关闭

当以 rootless 用户身份使用类似 podman run --detach httpd 的命令运行容器时,容器会在注销时被关闭,并不会保持运行。

注意:这部分与第 17 节重复,因此应该被删除以避免冗余。在第 17 节中已经提供了关于如何在注销时保持 rootless 容器运行的解决方案。

22 容器的默认分离键与 shell 历史导航冲突

Podman 默认使用 ctrl-p,ctrl-q 来从正在运行的容器中分离。而 bash 和 zsh shell 默认使用 ctrl-p 来显示上一条命令。这会导致在容器内部运行 shell 时出现问题。

症状

使用默认的分离键组合 ctrl-p,ctrl-q 时,shell 历史导航(在 bash 和 zsh 中测试)使用 ctrl-p 访问上一条命令时不会显示这条命令或任何内容。这是因为 conmon 正在等待额外的字符,以查看用户是否希望从容器中分离。在命令后添加额外的字符会导致该命令连同额外字符一起显示。如果用户第二次按下 ctrl-p,shell 将显示倒数第二条命令。

解决方案

解决此问题的方法是更改默认的 detach_keys。例如,为了将默认值更改为 ctrl-q,ctrl-q,可以使用 --detach-keys 选项。

podman run -ti --detach-keys ctrl-q,ctrl-q fedora sh

为了使这一更改成为所有容器的默认设置,用户可以修改 containers.conf 文件。这可以在用户的主目录中完成,只需在用户的 containers.conf 文件中添加以下行:

[engine]
detach_keys = "ctrl-q,ctrl-q"

这将更改所有新启动容器的默认分离键。已经运行的容器不会受到影响,除非它们被重新启动。请注意,修改 containers.conf 文件可能需要适当的权限,并且更改将影响所有使用 Podman 的用户。

此外,如果你正在使用特定的配置文件或环境变量来运行 Podman,确保这些设置也反映了所需的 detach_keys 更改。

始终建议在进行此类配置更改时备份原始文件,以便在需要时可以恢复默认设置。

cat >> ~/.config/containers/containers.conf << _eof
[engine]
detach_keys="ctrl-q,ctrl-q"
_eof

为了影响root运行容器和所有用户,修改系统/etc/containers/containers.conf 中的宽默认值。

23 带有公开端口的容器无法在 pod 中运行

使用 --publish-p 选项公开了端口的容器无法在 pod 中运行。

症状

podman pod create --name srcview -p 127.0.0.1:3434:3434 -p 127.0.0.1:7080:7080 -p 127.0.0.1:3370:3370
4b2f4611fa2cbd60b3899b936368c2b3f4f0f68bc8e6593416e0ab8ecb0a3f1d

podman run --pod srcview --name src-expose -p 3434:3434 -v "${PWD}:/var/opt/localrepo":Z,ro sourcegraph/src-expose:latest serve /var/opt/localrepo
Error: 无法在现有的容器网络命名空间中设置端口绑定

解决方案

这是一个已知的限制。如果容器将在 pod 中运行,则无需为 pod 中的容器公开端口。端口只需要由 pod 本身公开。Pod 网络堆栈就像主机上的网络堆栈一样——pod 中有多个容器和容器内的程序,它们都共享一个单一的接口、IP 地址和关联的端口。如果一个容器绑定到一个端口,那么在它使用时,pod 中的其他容器就不能使用该端口。Pod 中的容器还可以通过在 pod 中将一个容器绑定到 localhost,然后另一个容器连接到该端口来在 localhost 上进行通信。

在症状部分中的示例中,去掉 -p 3434:3434 允许 podman run 命令完成,并且作为 pod 一部分的容器仍然可以访问该端口。例如:

 podman run --pod srcview --name src-expose -v "${PWD}:/var/opt/localrepo":Z,ro sourcegraph/src-expose:latest serve /var/opt/localrepo

这样,src-expose 容器就可以在 srcview pod 中运行,而无需显式地为其公开端口。相反,所有在 pod 中公开的端口(在本例中为 127.0.0.1:3434:3434127.0.0.1:7080:7080127.0.0.1:3370:3370)都可以通过 pod 访问,而 pod 中的容器可以使用 localhost 地址相互通信。

24 Podman 容器镜像在运行时出现 fuse: device not found 错误

一些容器镜像在运行时需要 fuse 内核模块在内核中加载,以便使用 fuse 文件系统。

症状

尝试运行位于 quay.io/podman、quay.io/containers、registry.access.redhat.com/ubi8 或其他位置的容器镜像时,有时会返回错误:

错误:无法卸载 /var/lib/containers/storage/overlay/30c058cdadc888177361dd14a7ed7edab441c58525b341df321f07bc11440e68/merged:参数无效
错误:挂载容器 "1ae176ca72b3da7c70af31db7434bcf6f94b07dbc0328bc7e4e8fc9579d0dc2e" 时出错:挂载构建容器 "1ae176ca72b3da7c70af31db7434bcf6f94b07dbc0328bc7e4e8fc9579d0dc2e"
时出错:创建 overlay 挂载到 /var/lib/containers/storage/overlay/30c058cdadc888177361dd14a7ed7edab441c58525b341df321f07bc11440e68/merged
时出错:使用挂载程序 /usr/bin/fuse-overlayfs:fuse:未找到设备,请先尝试 'modprobe fuse'
fuse-overlayfs:无法挂载:没有这样的设备
: 退出状态 1
错误:退出状态 1

解决方案

如果在运行容器镜像时遇到 fuse: device not found 错误,那么很可能是 fuse 内核模块尚未在您的主机系统上加载。使用 modprobe fuse 命令加载模块,然后再运行容器镜像。要在启动时自动启用此功能,您可以向 /etc/modules.load.d 添加一个配置文件。查看 man modules-load.d 以获取更多详细信息。

25 podman run --rootfs link/to//read/only/dir 无法工作

如果在只读文件系统上遇到“OCI 运行时错误”或错误“{image} 不是一个绝对路径或是一个符号链接”时,这通常是这个问题的标志。有关更多详细信息,请查看此问题

症状

无根 Podman 需要文件系统中存在某些文件才能运行。Podman 会在 rootfs 中创建 /etc/resolv.conf/etc/hosts 和其他文件描述符,以便在其上挂载卷。

解决方案

先以读写模式运行容器一次,Podman 将在 rootfs 上生成所有文件描述符,之后您就可以使用只读 rootfs 运行了。

podman run --rm --rootfs /path/to/rootfs true

上面的命令将创建运行容器所需的所有缺失的目录。

之后,它就可以以只读模式被多个容器同时使用了:

podman run --read-only --rootfs /path/to/rootfs ....

另一个选项是使用 Overlay Rootfs Mount:

podman run --rootfs /path/to/rootfs:O ....

当容器执行完毕时,对挂载点的修改会被销毁,这与卸载一个 tmpfs 挂载点类似。

26 设置资源限制的容器运行失败,出现权限错误

在某些基于 systemd 的系统上,非 root 用户没有资源限制委托权限。这会导致设置资源限制时失败。

症状

使用资源限制选项运行容器时,会出现类似以下错误的失败情况:

--cpus--cpu-period--cpu-quota--cpu-shares

错误:OCI 运行时错误:crun:请求的 cgroup 控制器 `cpu` 不可用

--cpuset-cpus--cpuset-mems

错误:OCI 运行时错误:crun:请求的 cgroup 控制器 `cpuset` 不可用

这表示当前用户没有启用资源限制委托。

解决方案

您可以通过运行以下命令来验证是否启用了资源限制委托:

cat "/sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers"

示例输出可能是:

memory pids

在上面的示例中,没有列出 cpucpuset,这意味着当前用户没有权限设置 CPU 或 CPUSET 限制。

如果您想为所有用户启用 CPU 或 CPUSET 限制委托,可以创建文件 /etc/systemd/system/user@.service.d/delegate.conf,内容如下:

[Service]
Delegate=memory pids cpu cpuset

注销并重新登录后,您应该就有权限设置 CPU 和 CPUSET 限制了。

27 执行容器进程 '/bin/sh' 时发生错误:执行格式错误。(或其他非bin/sh的二进制文件)

这通常发生在尝试从一个与你当前运行环境架构不同的镜像中运行容器时。

例如,如果远程仓库仅包含并发送给你linux/arm64 OS/ARCH 的镜像,但你却在linux/amd64架构上运行(就像在openMF中发生的那样,由于timbru31)。

28 错误:创建sshClient失败:与堡垒主机ssh://user@host:22/run/user/.../podman/podman.sock的连接失败。

ssh:握手失败:ssh:无法认证,尝试了以下方法[none publickey],没有剩余的支持方法。在某些情况下,如果客户端和运行podman守护进程的机器不在同一台机器上,客户端使用的密钥可能使用了主机不支持的加密算法。这表明SSH配置存在问题。在修复之前,使用预共享密钥通过SSH与podman通信将是不可能的。

症状

/etc/crypto-policies/back-ends/openssh.config中接受的加密算法并不是用来创建转移到主机进行SSH认证的公钥/私钥对时使用的算法。

你可以通过尝试从客户端使用podman-remote info连接到主机,同时在主机上运行journalctl -f并查找错误userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedAlgorithms [preauth]来确认这一点。

解决方案

使用支持的算法(例如ecdsa)创建一个新密钥:

ssh-keygen -t ecdsa -f ~/.ssh/podman

然后将新的id复制过去:

ssh-copy-id -i ~/.ssh/podman.pub user@host

然后重新添加连接(如果需要,先删除旧的):

podman-remote system connection add myuser --identity ~/.ssh/podman ssh://user@host/run/user/1000/podman/podman.sock

现在,这应该能够工作:

podman-remote info

29 在RHEL中,Podman v2.2.1至v3.0.1版本无根CNI网络失败。

在RHEL上使用Podman v2.2.1至v3.0.1版本时,尝试在无根容器中使用网络时会遇到失败。这个错误在其他Linux发行版上不会发生。

症状

使用CNI网络创建了一个无根容器,但podman run命令返回了一个错误,指出必须构建镜像。

解决方案

为了在RHEL的无根容器中使用CNI网络,必须为CNI-in-slirp4netns创建一个Infra容器镜像。构建Infra容器镜像的说明可以在v2.2.1版本的这里找到,对于v3.0.1版本可以在这里找到。

30 重新加载firewalld后,与容器相关的防火墙规则丢失

在执行firewall-cmd --reloadsystemctl restart firewalld后,无法访问容器网络。运行podman network reload将修复此问题,但需要手动执行。

症状

当重新加载防火墙时,由podman创建的防火墙规则会丢失。

解决方案

@ranjithrajaram已经创建了一个systemd-hook来解决这个问题。

1 对于"firewall-cmd --reload",创建一个具有以下内容的systemd单元文件:

[Unit]
Description=firewalld reload hook - run a hook script on firewalld reload
Wants=dbus.service
After=dbus.service

[Service]
Type=simple
ExecStart=/bin/bash -c '/bin/busctl monitor --system --match "interface=org.fedoraproject.FirewallD1,member=Reloaded" --match "interface=org.fedoraproject.FirewallD1,member=PropertiesChanged" | while read -r line ; do podman network reload --all ; done'

[Install]
WantedBy=default.target

2 对于“systemctl restart firewalld”,创建一个具有以下内容的systemd单元文件:

[Unit]
Description=podman网络重载
Wants=firewalld.service
After=firewalld.service
PartOf=firewalld.service

[Service]
Type=simple
RemainAfterExit=yes
ExecStart=/usr/bin/podman network reload --all

[Install]
WantedBy=default.target

然而,如果你在RHEL 8上使用busctl monitor,你将无法获得机器可读的输出。这是因为它没有@yrro所提到的busctl -j选项。

对于RHEL 8,你可以使用以下一行bash脚本。

[Unit]
Description=在firewalld启动或重新加载后重新执行podman NAT规则
Wants=dbus.service
After=dbus.service
Requires=firewalld.service

[Service]
Type=simple
ExecStart=/bin/bash -c "dbus-monitor --profile --system 'type=signal,sender=org.freedesktop.DBus,path=/org/freedesktop/DBus,interface=org.freedesktop.DBus,member=NameAcquired,arg0=org.fedoraproject.FirewallD1' 'type=signal,path=/org/fedoraproject/FirewallD1,interface=org.fedoraproject.FirewallD1,member=Reloaded' | sed -u '/^#/d' | while read -r type timestamp serial sender destination path interface member _junk; do if [[ $type = '#'* ]]; then continue; elif [[ $interface = org.freedesktop.DBus && $member = NameAcquired ]]; then echo 'firewalld已启动'; podman network reload --all; elif [[ $interface = org.fedoraproject.FirewallD1 && $member = Reloaded ]]; then echo 'firewalld已重新加载'; podman network reload --all; fi; done"
Restart=Always

[Install]
WantedBy=default.target

解释: 这段文字描述了如何为systemctl restart firewalld命令创建一个systemd单元文件,以便在重启firewalld服务时重新加载podman的网络配置。此外,还提供了一个针对RHEL 8的bash脚本,该脚本通过dbus-monitor来监听firewalld服务的启动和重新加载事件,并在这些事件发生时执行podman network reload --all命令。由于RHEL 8的busctl命令不支持-j选项,因此无法使用busctl monitor来获取机器可读的输出,所以这里使用了dbus-monitor作为替代方案。

busctl monitor在RHEL 8中几乎可以使用,除了它在启动时总是输出两个错误的事件。 其中一个是(在其唯一的机器可读格式中)与firewalld启动时获得的NameOwnerChanged信号无法区分。 这意味着当这个单元启动时,你会得到一个额外的podman network reload --all命令执行。

除此之外,你可以使用以下包含Python 3代码的systemd服务。

[Unit]
Description=在firewalld启动或重新加载后重新执行podman NAT规则
Wants=dbus.service
Requires=firewalld.service
After=dbus.service

[Service]
Type=simple
ExecStart=/usr/bin/python /path/to/python/code/podman-redo-nat.py
Restart=always

[Install]
WantedBy=default.target

使用systemctl restart firewalld时,此代码会重新加载podman网络两次。

import dbus
from gi.repository import GLib
from dbus.mainloop.glib import DBusGMainLoop
import subprocess
import sys

# 代码中的返回值让我有点困惑
# 不确定是否需要它们。

def reload_podman_network():
try:
subprocess.run(["podman", "network", "reload", "--all"], timeout=90)
# 这部分我不确定
sys.stdout.write("podman network reload done\n")
sys.stdout.flush()
except subprocess.TimeoutExpired as t:
sys.stderr.write(f"Podman reload failed due to Timeout {t}")
except subprocess.CalledProcessError as e:
sys.stderr.write(f"Podman reload failed due to {e}")
except Exception as e:
sys.stderr.write(f"Podman reload failed with an Unhandled Exception {e}")

return False

def signal_handler(*args, **kwargs):
if kwargs.get('member' == "Reloaded":
reload_podman_network()
elif kwargs.get('member' == "NameOwnerChanged":
reload_podman_network()
else:
return None
return None

解释

此段文字首先解释了busctl monitor在RHEL 8系统中的一个问题,即在启动时它会输出两个错误的事件,其中一个与firewalld启动时发出的信号类似,这会导致在systemd服务启动时错误地执行一次podman network reload --all命令。

接着,它提供了一个使用Python 3编写的systemd服务示例,该服务将在firewalld启动或重新加载后重新加载podman的NAT规则。Python脚本定义了一个reload_podman_network函数,用于执行podman network reload --all命令,并处理可能出现的各种异常。同时,它还定义了一个signal_handler函数,用于处理DBus信号,并在接收到ReloadedNameOwnerChanged信号时调用reload_podman_network函数。

在Python代码中,注释提到了关于返回值的一些困惑,表示可能并不需要这些返回值。此外,sys.stdout.writesys.stdout.flush的用途是向标准输出写入信息,表明podman网络重载已完成,但这可能并不是必须的,取决于systemd服务的具体需求。

def signal_listener():
try:
# 定义主循环
DBusGMainLoop(set_as_default=True)
loop = GLib.MainLoop()
system_bus = dbus.SystemBus()

# 监听systemctl restart firewalld事件,通过添加过滤器实现,这会导致podman网络被重载两次
system_bus.add_signal_receiver(signal_handler,
dbus_interface='org.freedesktop.DBus',
arg0='org.fedoraproject.FirewallD1',
member_keyword='member')

# 监听firewall-cmd --reload命令
system_bus.add_signal_receiver(signal_handler,
dbus_interface='org.fedoraproject.FirewallD1',
signal_name='Reloaded',
member_keyword='member')

# 运行主循环
loop.run()
except KeyboardInterrupt:
# 捕获键盘中断,退出循环并正常退出程序
loop.quit()
sys.exit(0)
except Exception as e:
# 捕获其他异常,退出循环,输出错误信息并异常退出程序
loop.quit()
sys.stderr.write(f"发生错误:{e}")
sys.exit(1)

if __name__ == "__main__":
signal_listener()

31 Podman 运行失败,提示 ERRO[0000] 错误:XDG_RUNTIME_DIR 目录 "/run/user/0" 不属于当前用户 0。或错误:创建临时目录:mkdir /run/user/1000: 权限被拒绝

在尝试运行 podman 命令时遇到失败,伴随有警告 XDG_RUNTIME_DIR is pointing to a path which is not writable. Most likely podman will fail.

症状

以无根模式(rootless)调用容器时,为用户配置了 cgroupv2 作为cgroup配置,但用户缺少或拥有无效的 systemd 会话

示例情况:

# 使用 su 命令以 user1 用户身份执行 podman images
# su user1 -c 'podman images'
ERRO[0000] XDG_RUNTIME_DIR directory "/run/user/0" is not owned by the current user
# 使用 su 命令以登录 shell 的方式执行 podman images
# su - user1 -c 'podman images'
Error: creating tmpdir: mkdir /run/user/1000: permission denied

解释

该段代码定义了一个名为 signal_listener 的函数,该函数用于监听DBus信号,并在接收到特定信号时调用 signal_handler 函数。函数首先定义了DBus的主循环,然后连接到系统总线。通过两次调用 add_signal_receiver,分别监听 systemctl restart firewalld 事件和 firewall-cmd --reload 命令。如果程序接收到键盘中断(Ctrl+C),则退出循环并正常退出程序;如果发生其他异常,则捕获异常,退出循环,输出错误信息,并以错误码1退出程序。

此外,该段还描述了 podman 命令运行时可能出现的两种错误情况。当以无根模式运行容器时,如果用户的 XDG_RUNTIME_DIR 环境变量指向的路径不可写,或者尝试创建临时目录时权限不足,podman 命令会失败。这通常是因为用户没有有效的systemd会话,或者cgroup配置为 cgroupv2 但相关配置不正确。通过提供具体的错误信息和示例命令,用户可以更好地理解问题的原因和如何解决。

解决方案

您需要移除占用问题端口的悬挂gv-proxy进程。例如,如果错误消息中提到的端口是127.0.0.1:7777,您可以使用命令kill -9 $(lsof -i:7777)来识别并移除阻止您在默认端口上启动新虚拟机的悬挂进程。请注意,这里使用的lsof命令可能是一个拼写错误,通常应该使用lsoft或者更常见的lsof可能是lsoflsof -t -i:7777(假设您想获取进程ID而不是完整的进程列表)。如果lsof不是您的系统中可用的命令,您可以使用netstatss命令结合grepawk来找到占用该端口的进程ID,然后使用kill命令来终止它。

正确的命令可能类似于:

kill -9 $(netstat -tulnp | grep 7777 | awk '{print $7}' | cut -d'/' -f1)

或者如果您使用的是ss命令:

kill -9 $(ss -tuln | grep 7777 | awk '{print $4}' | cut -d':' -f1)

这些命令会列出所有监听的网络端口,然后通过grep找到端口7777的相关行,awkcut用于提取进程ID,最后kill -9用于强制终止该进程。

请确保在执行kill命令之前,您已经确认了确实需要终止该进程,因为强制终止可能会导致数据丢失或其他不可预知的问题。如果可能,尝试使用不带-9选项的kill命令来正常终止进程,如果不起作用,再使用-9选项强制终止。

在解决了端口占用问题之后,您应该能够成功地使用podman machine init初始化新的客户端虚拟机。

33 容器内的sshd进程无法运行。

症状

容器内运行的sshd进程因为错误“Error writing /proc/self/loginuid”而失败。

解决方案

如果/proc/self/loginuid文件已经被初始化,则需要CAP_AUDIT_CONTROL能力来覆盖它。

这通常发生在从用户会话中运行Podman时,因为/proc/self/loginuid文件已经被初始化。解决方案是从系统服务中运行Podman,要么使用Podman服务,然后使用podman -remote来启动容器,要么简单地运行类似systemd-run podman run ...的命令。在这种情况下,容器将只需要CAP_AUDIT_WRITE能力。

34 容器创建的文件不是由用户的常规UID所拥有

在使用无根Podman运行容器后,非root用户看到的是一个数字UID和GID,而不是用户名和组名。

症状

当在主机上使用ls -l命令列出通过--volume /some/dir传递给podman run命令的目录中的文件权限时,显示的是UID和GID而不是相应的用户名和组名。显示的UID和GID数字是主机系统上用户的从属UID和GID范围中的。

例如:

mkdir dir1
chmod 777 dir1
podman run --rm -v ./dir1:/dir1:Z \
--user 2003:2003 \
docker.io/library/ubuntu bash -c "touch /dir1/a; chmod 600 /dir1/a"
ls -l dir1/a
-rw-------. 1 102002 102002 0 Jan 19 19:35 dir1/a
less dir1/a
less: dir1/a: Permission denied

翻译解释:

33 容器内的sshd进程运行失败

症状

在容器内运行sshd进程时,遇到了“Error writing /proc/self/loginuid”错误。

解决方案

如果/proc/self/loginuid文件已经被初始化,那么需要CAP_AUDIT_CONTROL权限来覆盖它。

这种情况通常发生在从用户会话中运行Podman时,因为/proc/self/loginuid文件已经预先设置。解决方案是从系统服务中运行Podman,比如使用Podman服务,然后通过podman -remote来启动容器,或者简单地通过运行类似systemd-run podman run ...的命令来启动。在这种情况下,容器只需要CAP_AUDIT_WRITE权限。

34 容器创建的文件不属于用户的常规UID

在使用无根Podman运行容器后,非root用户看到文件的所有者是数字UID和GID,而不是用户名和组名。

症状

当在主机上使用ls -l命令查看通过--volume /some/dir参数传递给podman run命令的目录中的文件权限时,发现显示的是UID和GID,而不是对应的用户名和组名。这些显示的UID和GID数字对应于主机系统上用户的从属UID和GID范围。

例如:

mkdir dir1
chmod 777 dir1
podman run --rm -v ./dir1:/dir1:Z \
--user 2003:2003 \
docker.io/library/ubuntu bash -c "touch /dir1/a; chmod 600 /dir1/a"
ls -l dir1/a
-rw-------. 1 102002 102002 0 Jan 19 19:35 dir1/a
less dir1/a
less: dir1/a: 权限不够

在这个例子中,文件dir1/a的所有者和组ID显示为102002,而不是预期的用户名和组名。同时,尝试使用less命令查看文件时,会收到权限不够的错误消息,因为当前用户可能没有足够的权限来读取这个文件。

解决方案

如果想要读取、更改所有者(chown)或删除这样的文件,可以进入用户命名空间。不要直接运行诸如less dir1/arm dir1/a的命令,而需要在命令行前加上podman unshare,即podman unshare less dir1/apodman unshare rm dir1/a。要更改文件dir1/a的所有者为常规用户的UID和GID,可以运行podman unshare chown 0:0 dir1/a。在用户命名空间中,文件的所有者0:0对应主机上的常规用户。如果要使用Bash的特性,如变量扩展和通配符,则需要用bash -c来包装命令,例如podman unshare bash -c 'ls $HOME/dir1/a*'

是否有其他方式运行Podman,使得常规用户成为文件的所有者呢?是的,可以使用--userns keep-id:uid=$uid,gid=$gid选项来改变容器和主机之间UID和GID的映射方式。让我们来尝试一下。

在上面的例子中,ls -l显示了UID 102002和GID 102002。设置shell变量:

uid_from_ls=102002
gid_from_ls=102002

设置shell变量为最低的从属UID和GID:

lowest_subuid=$(podman info --format "{{ (index .Host.IDMappings.UIDMap 1).HostID }}")
lowest_subgid=$(podman info --format "{{ (index .Host.IDMappings.GIDMap 1).HostID }}")

计算容器内部映射到主机上创建文件所有者的UID和GID。

uid=$(( $uid_from_ls - $lowest_subuid + 1))
gid=$(( $gid_from_ls - $lowest_subgid + 1))

(在计算中,我们假设只有一个从属UID范围和一个从属GID范围)

echo $uid
2003
echo $gid
2003

计算结果显示,容器内部的UID是2003,GID是2003。这并不奇怪,因为之前用--user=2003:2003指定了这些值,但同样的计算可以用于指定用户名的情况或未使用--user选项的情况。

重新运行容器,但这次使用映射的UID和GID

mkdir dir1
chmod 777 dir1
podman run --rm \
-v ./dir1:/dir1:Z \
--user $uid:$gid \
--userns keep-id:uid=$uid,gid=$gid \
docker.io/library/ubuntu bash -c "touch /dir1/a; chmod 600 /dir1/a"
id -u
tester
id -g
tester
ls -l dir1/a
-rw-------. 1 tester tester 0 Jan 19 20:31 dir1/a
$

在这个例子中,--user选项指定了容器中的非root用户。由于非root用户也可以在容器镜像中指定,例如:

podman image inspect --format "user: {{.User}}" IMAGE
user: hpc

即使不指定--user,也可能出现同样的问题。

使用--user=root:root(默认值)时,也可能出现同样问题的另一种情况,即root用户在某种方式下创建非root用户拥有的文件(例如,自己创建它们,或者切换到非root用户的有效UID然后创建文件)。

也请参考故障排查提示:

Podman运行失败,提示“Error: unrecognized namespace mode keep-id:uid=1000,gid=1000 passed”

35 传递给容器的设备或文件无法以无root用户身份访问(UID/GID映射问题)

作为非root用户,您有权访问您想要通过--device=...--volume=...--mount=...传递给无root容器的设备、文件和目录。

Podman默认将容器内的非root用户映射到主机上该用户的从属UID和从属GID之一。当容器用户尝试访问文件时,可能会因为容器用户没有主机上常规用户的权限而出现“Permission denied”错误。

症状

  • 对于使用--device=..--volume=..--mount=..传递给容器的文件、目录或设备,容器内的任何访问都会因“Permission denied”而被拒绝,例如:
mkdir dir1
chmod 700 dir1
podman run --rm -v ./dir1:/dir1:Z \
--user 2003:2003 \
docker.io/library/ubuntu ls /dir1
ls: cannot open directory '/dir1': Permission denied

解决方案

我们基本上遵循与前一个故障排查提示相同的解决方案:

容器创建的文件不由用户的常规UID拥有

但对于这个问题,容器UID和GID不能通过简单的加法和减法轻易计算出来。

换句话说,找出容器内要映射到主机上常规用户的UID和GID可能会更具挑战性。

如果--user选项与数字UID和GID一起使用来指定一个无root用户,我们已经知道答案。

如果--user选项与用户名和组名一起使用,我们可以在容器的/etc/passwd文件中查找UID和GID。

如果容器用户不是通过--user设置,而是从容器镜像中指定,我们可以检查容器镜像

podman image inspect --format "user: {{.User}}" IMAGE
user: hpc

然后在容器的/etc/passwd文件中查找它。

如果问题出现在一个以root身份启动但最终切换到无root用户有效UID的容器中,那么找出UID和GID可能会不那么直接。阅读ContainerfileDockerfile/etc/passwd可能会提供线索。

要以无root容器的UID和GID映射到主机上用户的常规UID和GID来运行容器,请遵循以下步骤:

在Bash shell中设置uidgid shell变量为容器内部将运行的用户的UID和GID,例如:

uid=2003
gid=2003

然后运行:

mkdir dir1
echo hello > dir1/file.txt
chmod 700 dir1/file.txt
podman run --rm \
-v ./dir1:/dir1:Z \
--user $uid:$gid \
--userns keep-id:uid=$uid,gid=$gid \
docker.io/library/alpine cat /dir1/file.txt
hello

另请参阅故障排查提示:

Podman run失败,出现"Error: unrecognized namespace mode keep-id:uid=1000,gid=1000 passed"

36 即使有其他容器正在使用,附加存储中的镜像也可以被删除

当附加存储中的镜像被使用时,它不会被锁定,因此即使有其他容器正在使用它,也可以被删除。

症状

WARN[0000] Can't stat lower layer "/var/lib/containers/storage/overlay/l/7HS76F2P5N73FDUKUQAOJA3WI5" because it does not exist. Going through storage to recreate the missing symlinks.

解决方案

确保附加存储中的镜像在被其他存储中的容器使用时不会被删除,是用户的责任。

37 podman-remote或使用Podman API的设置中同步错误修复

在将Podman升级到新版本后,使用podman-remote时,早期版本的Podman中的问题仍然会出现。

症状

使用最新版本的Podman运行podman远程命令时,在Podman客户端或服务器端可能会出现先前版本中已修复的问题。

解决方案

在将Podman升级到特定版本以获取所需的修复时,用户经常犯的错误是只升级Podman客户端。但是,如果设置使用podman-remote或使用通过REST API与远程机器上的Podman服务器通信的客户端,则必须同时升级远程机器上运行的Podman客户端和Podman服务器。Podman客户端和服务器都必须升级到相同的版本。

例如:如果某个特定错误在v4.1.0版本中得到了修复,那么Podman客户端必须为v4.1.0版本,同时Podman服务器也必须为v4.1.0版本。

38 终端输出意外的回车符

在使用 --tty (-t 标志时,终端会输出意外的回车符。

症状

容器程序打印一个换行符 (\n),但终端输出一个回车符和一个换行符 (\r\n)。

podman run --rm -t fedora echo abc | od -c
0000000 a b c \r \n
0000005

直接在主机上运行时,结果符合预期。

echo abc | od -c
0000000 a b c \n
0000004

额外的回车符也可能导致提示符向右偏移。

podman run --rm -t fedora sh -c "echo 1; echo 2; echo 3" | cat -A
1^M$
2^M$
3^M$
$

解决方案

运行 Podman 时不要使用 --tty (-t 标志。

podman run --rm fedora echo abc | od -c
0000000 a b c \n
0000004

--tty (-t 标志只应在程序需要在终端中进行用户交互时使用,例如程序期待用户输入答案。

额外的回车符 \r 是从哪里来的?

额外的 \r 并不是由 Podman 输出的,而是由终端输出的。实际上,终端的重新配置可以消除这个额外的 \r

podman run --rm -t fedora /bin/sh -c "stty -onlcr && echo abc" | od -c
0000000 a b c \n
0000004

39 Podman run 报错:“Error: unrecognized namespace mode keep-id:uid=1000,gid=1000 passed”

Podman 4.3.0 引入了可以与 --userns keep-id 一起使用的 uidgid 选项,但旧版本的 Podman 不识别这些选项。

症状

当使用低于 4.3.0 版本的 Podman 时,uidgid 选项不会被识别,并且会抛出“unrecognized namespace mode”错误。

uid=1000
gid=1000
podman run --rm
--user $uid:$gid \
--userns keep-id:uid=$uid,gid=$gid \
docker.io/library/ubuntu /bin/cat /proc/self/uid_map
Error: unrecognized namespace mode keep-id:uid=1000,gid=1000 passed
$

解决方案

使用 --uidmap--gidmap 选项来描述相同的 UID 和 GID 映射。

运行以下命令:

uid=1000
gid=1000
subuidSize=$(( $(podman info --format "{{ range \
.Host.IDMappings.UIDMap }}+{{.Size }}{{end }}" - 1 ))
subgidSize=$(( $(podman info --format "{{ range \
.Host.IDMappings.GIDMap }}+{{.Size }}{{end }}" - 1 ))
podman run --rm \
--user $uid:$gid \
--uidmap 0:1:$uid \
--uidmap $uid:0:1 \
--uidmap $(($uid+1)):$(($uid+1)):$(($subuidSize-$uid \
--gidmap 0:1:$gid \
--gidmap $gid:0:1 \
--gidmap $(($gid+1)):$(($gid+1)):$(($subgidSize-$gid \
docker.io/library/ubuntu /bin/cat /proc/self/uid_map
0 1 1000
1000 0 1
1001 1001 64536

该命令使用的 UID 和 GID 映射与 Podman 4.3.0(或更高版本)中使用 --userns keep-id:uid=$uid,gid=$gid 指定时相同。

uid=1000
gid=1000
podman run --rm \
--user $uid:$gid \
--userns keep-id:uid=$uid,gid=$gid \
docker.io/library/ubuntu /bin/cat /proc/self/uid_map
0 1 1000
1000 0 1
1001 1001 64536

/bin/cat /proc/self/uid_map 替换为 /bin/cat /proc/self/gid_map 来显示 GID 映射。

40 Podman 无法找到预期的镜像,出现“error locating pulled image”或“image not known”错误

当尝试执行一个从本地存储或远程仓库拉取镜像的 Podman 命令时,Podman 会报告“image not known”(镜像未知)或“error locating pulled image”(定位拉取的镜像时出错)的错误,尽管之前已经验证过该镜像存在。

症状

在验证镜像已经存在于本地或远程仓库之后,引用该镜像的 Podman 命令将以类似以下方式失败:

# ls Containerfile
FROM registry.access.redhat.com/ubi8-minimal:latest
MAINTAINER Podman Community
USER root

# podman build .
STEP 1/2: FROM registry.access.redhat.com/ubi8-minimal
Trying to pull registry.access.redhat.com/ubi8-minimal:latest...
Getting image source signatures
Checking if image destination supports signatures
Copying blob a6577091999b done
Copying config abb1ba1bce done
Writing manifest to image destination
Storing signatures
Error: error creating build container: error locating pulled image "registry.access.redhat.com/ubi8-minimal:latest" name in containers storage: registry.access.redhat.com/ubi8-minimal:latest: image not known

解决方案

这个问题的一般原因是时间问题。为了使 Podman 命令尽可能高效,只会在代码中的关键部分建立读写锁。当从仓库拉取镜像时,首先使用写锁将镜像的副本写入本地存储。在镜像被获取/读取之前,会释放这个锁。如果在镜像写入和第一个进程能够获取它之间的时间,另一个进程执行了有害的命令,如 podman system prune --allpodman system resetpodman rmi --all,那么就会出现这种“image not known”(镜像未知)的错误。

Podman 的维护者已经考虑过使用更重的锁来关闭这个时间窗口。但是,所有 Podman 命令都会遇到的性能下降并不被认为值得完全关闭这个小的时间窗口所付出的代价。

41 使用 --mount=type=secret 的 Podman 构建步骤因“操作不允许”而失败

当在使用 nosuid 挂载的主机文件系统上运行,并且使用 runc 运行时,执行 Dockerfile/Containerfile 中的使用 --mount=type=secret 挂载秘钥的步骤会因为“操作不允许”而失败。

症状

Dockerfile/Containerfile 中的 RUN 行包含类似 --mount=type=secret,id=MY_USER,target=/etc/dnf/vars/MY_USER秘钥挂载。 运行 podman build 时,该过程会因错误消息而失败,例如:

STEP 3/13: RUN --mount=type=secret,id=MY_USER,target=/etc/dnf/vars/MY_USER     
--mount=type=secret,id=MY_USER,target=/etc/dnf/vars/MY_USER
...: time="2023-06-13T18:04:59+02:00" level=error msg="runc create failed: unable to start container process:
error during container init: error mounting \"/var/tmp/buildah2251989386/mnt/buildah-bind-target-11\" to rootfs
at \"/etc/dnf/vars/MY_USER\": mount /var/tmp/buildah2251989386/mnt/buildah-bind-target-11:/etc/dnf/vars/MY_USER (via /proc/self/fd/7),
flags: 0x1021: operation not permitted"
: exit status 1
ERRO[0002] did not get container create message from subprocess: EOF

解决方案

  • 安装 crun,例如使用 dnf install crun
  • 通过向 podman build 传递 --runtime /usr/bin/crun 来使用 crun 运行时。

关于问题的完整讨论,请参阅 Buildah 问题 4228

42 在容器内部进行的 podman 构建中,文件 I/O 密集型操作非常慢

当在运行的容器内部使用 overlay 存储驱动进行嵌套的 podman build 时,文件 I/O 操作(如大量数据的 COPY)会非常慢,或者可能会完全挂起。

症状

在使用默认的 overlay 存储驱动时,在另一个容器内部运行的 podman build 过程中,Containerfile 中的 COPYADD 或 I/O 密集型的 RUN 指令执行速度非常慢,或者完全挂起。

解决方案

这可能是由于子容器使用 fuse-overlayfs 写入 /var/lib/containers/storage 导致的。使用 fuse-overlayfs 写入数据时可能会很慢。解决方案是使用本地目录作为主机系统上的卷挂载到 /var/lib/containers/storage,以使用原生的 overlay 文件系统。具体操作如下:podman run --privileged --rm -it -v ./nested_storage:/var/lib/containers/storage parent:latest。请确保示例中的 parent:latest 基础镜像本身在 /var/lib/containers/storage 中没有内容,以便此操作能够生效。一旦使用本地卷,嵌套容器在写入文件时就不会回退到 fuse-overlayfs,从而嵌套构建会更快完成。

如果您无法访问父容器的运行过程,例如在 CI 环境中,那么第二个选项是在父镜像中更改存储驱动为 vfs。您可以通过在 storage.conf 文件中更改这一行来实现:driver = "vfs"。您可能需要运行 podman system reset 来使更改生效。当 podman info | grep graphDriverName 输出 graphDriverName: vfs 时,说明已经更改成功。虽然这种方法比上述使用卷的方法性能稍差,但比使用 fuse-overlayfs 要快得多。