Podman
Podman 是 Docker 的替代品,提供类似的界面。它支持无根容器和用于 docker-compose 的 shim 服务。
安装
Podman 依赖 netavark 软件包作为有根容器的默认网络后端(参见 podman-network(1))。Netavark 依赖 aardvark-dns 以在同一网络中的容器之间进行名称解析。对替代网络后端(CNI,cni-plugins)的支持已被弃用。
如果您想替换 Docker,可以安装 podman-docker 来模拟 docker 二进制文件以及 man 手册页。
与 Docker 不同,Podman 不需要守护进程,但有一个守护进程通过 cockpit-podman 为 cockpit 等服务提供 API。
有关构建容器的高级用法,请参阅基于 Buildah 的 podman-build(1)。
配置
用于配置容器行为的配置文件位于 /usr/share/containers/
。您必须先将必要的文件复制到 /etc/containers
才能编辑。要配置 Podman 使用的网络桥接接口,请参阅 /etc/cni/net.d/87-podman.conflist
。
镜像仓库
默认情况下,Arch Linux 中未配置任何容器镜像仓库 [1]。这意味着像 podman search httpd
这样的非限定搜索将不起作用。要使 Podman 的行为类似于 Docker,请配置 containers-registries.conf(5)
/etc/containers/registries.conf.d/10-unqualified-search-registries.conf
unqualified-search-registries = ["docker.io"]
/etc/containers/registries.conf.d/shortnames.conf
。用户命名空间模式
默认情况下,Podman 容器中的进程与调用者在同一用户命名空间中运行,即容器未通过 user_namespaces(7) 功能隔离。这是 --userns=host
的行为,请参见 podman-run(1)。
--userns=auto
标志会自动为容器创建一个唯一的用户命名空间,使用空的 UID 和 GID 范围
- 对于 root 启动的容器,
--userns=auto
标志要求在/etc/subuid
和/etc/subgid
文件中指定用户名containers
,并具有未使用的 ID 范围。例如:containers:2147483647:2147483648
。 - 对于其他用户启动的容器,将使用
/etc/subuid
和/etc/subgid
文件中的用户范围。有关必要的配置,请参见 #无根 Podman。
--userns
标志还有其他有效值,详细信息请参见 podman-run(1)。用户命名空间模式也可以在 containers.conf(5) 文件中按系统或按用户配置。
无根 Podman
CONFIG_USER_NS_UNPRIVILEGED
),这有一些严重的安全隐患,详细信息请参见 安全性#沙盒化应用程序。默认情况下,只有 root
允许运行容器(或内核术语中的命名空间)。运行无根 Podman 可以提高安全性,因为攻击者将不会拥有您系统的 root 权限,并且还允许多个非特权用户在同一台机器上运行容器。另请参见 podman(1) § 无根模式 和官方 无根教程(可能已过时)。
启用 kernel.unprivileged_userns_clone
首先,通过运行以下命令检查 kernel.unprivileged_userns_clone
的值
$ sysctl kernel.unprivileged_userns_clone
如果当前设置为 0
,请通过 sysctl 或 内核参数 将其设置为 1
来启用它。
设置 subuid 和 subgid
为了让用户运行无根 Podman,每个想要使用它的用户都必须存在 subuid(5) 和 subgid(5) 配置条目。默认情况下,使用 useradd(8) 创建的新的 用户 具有这些条目。
为 shadow 4.11.1-3 之前创建的用户迁移
默认情况下,在 shadow 4.11.1-3 之前创建的用户在 /etc/subuid
和 /etc/subgid
中没有条目。可以使用 usermod(8) 命令或手动修改文件来为他们创建条目。
以下命令启用 username
用户和组运行 Podman 容器(或那种情况下的其他类型的容器)。它为给定的用户和组分配给定的 UID 和 GID 范围。
# usermod --add-subuids 100000-165535 --add-subgids 100000-165535 username
用户 username
的上述范围可能已被另一个用户占用,因为它定义了系统上第一个用户的默认范围。如有疑问,请先查阅 /etc/subuid
和 /etc/subgid
文件以查找已保留的范围。
homed 管理用户的变通方法
Homed 似乎没有为其用户分配 gid 和 uid 条目。要手动执行此操作,请运行
# usermod --add-subuids 524288-589823 --add-subgids 524288-589823 username
或者,只需以 root 身份编辑以下配置文件并添加以下行
/etc/subuid
username:524288:65536
/etc/subgid
username:524288:65536
这会将 uid 和 gid 范围 524288-589823
分配给 username
用户。如果这些范围已被其他用户占用,则您需要相应地移动/调整范围。
您可能需要重启才能反映更改。
- 这只是一个变通方法,Podman 似乎不正式支持 homed。
- 这是 systemd-homed 的一个 已知问题。
- 使用 Docker 似乎有效(将用户添加到
docker
组,但它有自己的 安全隐患)。
传播 subuid 和 subgid 的更改
无根 Podman 使用暂停进程来保持非特权命名空间处于活动状态。这会阻止 /etc/subuid
和 /etc/subgid
文件的任何更改传播到正在运行暂停进程的无根容器。为了传播这些更改,需要运行
$ podman system migrate
在此之后,上述文件中指定的用户/组能够启动和运行 Podman 容器。
启用原生无根 Overlay
以前,在无根环境中使用 FUSE overlay 挂载需要使用 fuse-overlayfs 软件包。但是,现代版本的 Podman 和 Linux 内核 支持 原生 无根 overlay,这会产生更好的性能。
--userns auto
,其中每次调用都可能使用不同的 UID/GID 映射。有关详细信息,请参见 Podman 性能指南。要从 fuse-overlayfs 迁移,请运行以下命令(不幸的是,它将删除所有拉取的镜像)
$ podman system reset
还要确保 Podman 使用 overlay
驱动程序,并且未在 containers-storage.conf(5) 中定义 mount_program
参数。请按照 Docker#启用原生 overlay diff 引擎 中的说明进行操作。
要验证是否启用了原生无根 overlay,请运行
$ podman info | grep -i overlay
它应显示 graphDriverName: overlay
和 Native Overlay Diff: "true"
。
网络
Podman 依赖 passt,它提供 pasta 作为默认的无根网络后端。
另一种无根网络后端是 slirp4netns,它是 Podman 5 之前的默认后端。
两者之间的主要区别在 Podman 5.0 详细的重大更改 中概述
- Pasta 默认不执行网络地址转换 (NAT),并将主接口的 IP 地址复制到容器命名空间中。
上游的 无根 Podman 的缺点 中解释了此更改的后果
- 由于 pasta 复制了主接口的 IP 地址,因此从容器到该 IP 的连接不起作用。这意味着,除非您有多个接口,否则如果不显式传递 pasta 网络配置(在
containers.conf
中或在运行时),就无法建立容器间连接。
“Podman 5.0 重大更改”博客文章中给出了一个模仿 slirp4netns 行为的示例
containers.conf
[network] pasta_options = ["-a", "10.0.2.0", "-n", "24", "-g", "10.0.2.2", "--dns-forward", "10.0.2.3"]
此外,默认的无根网络工具可以在 containers.conf
的 [network]
部分中使用 default_rootless_network_cmd
进行选择,可以将其设置为 pasta
或 slirp4netns
。因此,如果您遇到错误,您可以随时像这样恢复到 slirp4netns(前提是已安装)
containers.conf
[network] default_rootless_network_cmd = "slirp4netns"
存储
容器镜像和实例的存储方式和位置的配置在 /etc/containers/storage.conf
中进行。
$XDG_CONFIG_HOME/containers/storage.conf
。默认的 overlay
驱动程序经过充分测试,并且在支持它的文件系统(Btrfs,XFS,ZFS...)上支持 reflink 复制 [3] [4]。
有关可用替代方案和其他配置选项的更多信息,请参见 containers-storage.conf(5) § STORAGE_TABLE。
非本地架构
Podman 能够使用 Wikipedia:binfmt_misc 系统运行为与主机不同的 CPU 架构构建的镜像。
要启用它,请安装 qemu-user-static 和 qemu-user-static-binfmt。
systemd 附带 systemd-binfmt.service
服务,该服务应启用新规则。
验证 binfmt 规则是否已添加
$ ls /proc/sys/fs/binfmt_misc
DOSWin qemu-cris qemu-ppc qemu-sh4eb status qemu-aarch64 qemu-m68k qemu-ppc64 qemu-sparc qemu-alpha qemu-microblaze qemu-riscv64 qemu-sparc32plus qemu-arm qemu-mips qemu-s390x qemu-sparc64 qemu-armeb qemu-mipsel qemu-sh4 register
Podman 现在应该能够运行非本地架构镜像。大多数命令在传递 --arch
选项时使用非本地架构。
示例
# podman run --arch arm64 'docker.io/alpine:latest' arch
aarch64
Docker Compose
Podman 有一个 compose 子命令,它是 compose 提供程序的薄包装器,可以是 docker-compose 或 podman-compose。如果两者都已安装,docker-compose 优先。您可以使用 PODMAN_COMPOSE_PROVIDER
环境变量覆盖此设置。
如果您想使用 docker-compose,您将需要为该用户 启用 podman.socket
用户单元 并设置 docker 套接字环境变量
$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
当使用 podman-compose 时,不需要这样做,因为它将直接使用 podman。
- 如果您已在 docker 中启用 buildkit,则集成将不起作用。您需要通过设置
DOCKER_BUILDKIT=0
环境变量 来禁用 buildkit。 - podman-compose 存在兼容性问题,例如 环境变量的传递与 docker-compose 的行为不匹配。
NVIDIA GPU
NVIDIA 容器工具包 为 NVIDIA GPU 提供容器运行时。安装 nvidia-container-toolkit 软件包。它包含一个 pacman hook,用于为您的 GPU 生成 CDI 规范并将其保存在 /etc/cdi/nvidia.yaml
中。
测试设置
$ podman run --rm --gpus all archlinux nvidia-smi -L
具有重启策略的容器
要自动启动具有重启策略的容器,启用 podman-restart.service
。
Quadlet
Quadlet 允许使用 systemd 管理 Podman 容器。
对于无根 Podman,将 Quadlet 文件放置在以下目录之一
$XDG_CONFIG_HOME/containers/systemd/
或~/.config/containers/systemd/
/etc/containers/systemd/users/UID
,适用于与UID
匹配的用户/etc/containers/systemd/users/
,适用于所有用户
对于具有 root 权限的 Podman,目录为 /etc/containers/systemd/
。
Podman 将读取扩展名为 .container、.volume、.network、.kube、.image 和 .pod 的 Quadlet 文件。将使用 systemd.generator(7) 生成相应的 .service 文件。Quadlet 文件在启动期间或通过手动运行 daemon-reload 读取。
Quadlet 文件也可以使用 podletAUR 从 Podman 命令生成。
例如,这是一个将从 LinuxServer.io 运行 Syncthing 容器的命令
$ podman run \ --rm \ --replace \ --label io.containers.autoupdate=registry \ --name syncthing \ --hostname=syncthing \ --uidmap 1000:0:1 \ --uidmap 0:1:1000 \ --uidmap 1001:1001:64536 \ --env PUID=1000 \ --env PGID=1000 \ --env TZ=Etc/UTC \ --publish 127.0.0.1:8384:8384/tcp \ --publish 22000:22000/tcp \ --volume /path/to/syncthing/config:/config \ --volume /path/to/data1:/data1 \ lscr.io/linuxserver/syncthing:latest
要将其作为 systemd 服务进行管理,请创建以下 Quadlet 文件
~/.config/containers/systemd/syncthing-lsio.container
[Unit] Description=Syncthing container # Containers can depend on one another using systemd dependencies, but with a ".service" suffix. # For example, to make another container wait until this one starts, add "After=syncthing-lsio.service" # to its [Unit] section. [Container] ContainerName=syncthing Image=lscr.io/linuxserver/syncthing:latest # Enable auto-update container AutoUpdate=registry Volume=/path/to/syncthing/config:/config Volume=/path/to/data1:/data1 HostName=syncthing PublishPort=127.0.0.1:8384:8384/tcp PublishPort=22000:22000/tcp Environment=PUID=1000 Environment=PGID=1000 Environment=TZ=Etc/UTC # UID mapping is needed to run linuxserver.io container as rootless podman. # This will map UID=1000 inside the container to intermediate UID=0. # For rootless podman intermediate UID=0 will be mapped to the UID of current user. UIDMap=1000:0:1 UIDMap=0:1:1000 UIDMap=1001:1001:64536 [Service] Restart=on-failure # Extend Timeout to allow time to pull the image TimeoutStartSec=300 # The [Install] section allows enabling the generated service. [Install] WantedBy=default.target
我们可以通过以下方式验证 Quadlet 文件
$ /usr/lib/podman/quadlet -dryrun -user
然后,重新加载 并 启动/启用 syncthing-lsio.service
。请参阅 systemd/User#自动启动 systemd 用户实例 以在没有任何打开会话的情况下启动无根容器。
如果您想在启动时启动容器,请确保您的 网络管理器 支持并配置为 在网络启动后运行服务。
After=
和 Wants=
属性,隐式地依赖于 network-online.target
(作为 root 用户)或 podman-user-wait-network-online.service
(作为用户)。这是为了确保如果需要拉取镜像,并且在容器启动时网络是可访问的。有关详细信息,请参见 podman-systemd.unit(5) § 隐式网络依赖项。Container
部分的有效选项在 podman-systemd.unit(5) § 容器单元 [Container] 下列出。PodmanArgs=
可用于添加其他没有相应文件选项的 Podman 参数。
有关更多示例,包括 Pod
、Volume
、Network
和 Image
单元,请参见 podman-systemd.unit(5) § 示例。
镜像
/etc/containers/registries.conf
中的 unqualified-search-registries
中定义的所有注册表中按定义的顺序自动搜索镜像。以下镜像将始终包含前缀,以允许在配置中不包含 docker.io
的情况。Arch Linux
以下命令从 Docker Hub 拉取 Arch Linux x86_64 镜像。
# podman pull docker.io/archlinux
有关可用标签的完整列表,包括带和不带构建工具的版本,请参见 Docker Hub 页面。
另请参见 README.md。
Alpine Linux
Alpine Linux 是小型容器镜像的流行选择,特别是对于编译为静态二进制文件的软件。以下命令从 Docker Hub 拉取最新的 Alpine Linux 镜像
# podman pull docker.io/alpine
Alpine Linux 使用 musl libc 实现,而不是大多数 Linux 发行版使用的 glibc libc 实现。由于 Arch Linux 使用 glibc,因此 Arch Linux 主机和 Alpine Linux 容器之间存在许多功能差异,这些差异会影响软件的性能和正确性。这些差异的列表记录在 https://wiki.musl-libc.org/functional-differences-from-glibc.html 中。
请注意,在 Arch Linux(或任何其他使用 glibc 的系统)上动态链接的软件在 Alpine Linux(或任何其他使用不同 libc 的系统)上运行时,可能会出现错误和性能问题。有关示例,请参见 [6]、[7] 和 [8]。
CentOS
以下命令从 Docker Hub 拉取最新的 CentOS 镜像
# podman pull docker.io/centos
有关每个 CentOS 版本的可用标签的完整列表,请参见 Docker Hub 页面。
Debian
以下命令从 Docker Hub 拉取最新的 Debian 镜像
# podman pull docker.io/debian
有关可用标签的完整列表,包括每个 Debian 版本的标准版和 slim 版,请参见 Docker Hub 页面。
故障排除
向进程添加暂停
WARN[0000] Failed to add pause process to systemd sandbox cgroup: Process org.freedesktop.systemd1 exited with status 1
可以使用以下方法解决:https://github.com/containers/crun/issues/704
# echo +cpu +cpuset +io +memory +pids > /sys/fs/cgroup/cgroup.subtree_control
容器在 Shell 退出时终止
从机器注销后,某些用户的 Podman 容器会停止。要防止这种情况,请为运行容器的用户启用用户持久。
您还可以按照 podman-auto-update(1) § 示例 中的描述创建用户 systemd 单元。
在无根模式下提交时出错
Error committing the finished image: error adding layer with blob "sha256:02823fca9b5444c196f1f406aa235213254af9909fca270f462e32793e2260d8": Error processing tar file(exit status 1) permitted operation
检查存储驱动程序是否为 存储配置 中的 overlay。
在无根模式下创建带有桥接网络的容器时出错
如果您正在使用 AppArmor,则在启用 dnsname
插件的情况下使用桥接网络创建容器时,可能会遇到问题
$ podman network create foo
/home/user/.config/cni/net.d/foo.conflist
$ podman run --rm -it --network=foo docker.io/library/alpine:latest ip addr
Error: command rootless-cni-infra [alloc 89398a9315256cb1938075c377275d29c2b6ebdd75a96b5c26051a89541eb928 foo festive_hofstadter ] in container 1f4344bbd1087c892a18bacc35f4fdafbb61106c146952426488bc940a751efe failed with status 1, stdout="", stderr="exit status 3\n"
可以通过将以下行添加到 /etc/apparmor.d/local/usr.sbin.dnsmasq
来解决此问题
owner /run/user/[0-9]*/containers/cni/dnsname/*/dnsmasq.conf r, owner /run/user/[0-9]*/containers/cni/dnsname/*/addnhosts r, owner /run/user/[0-9]*/containers/cni/dnsname/*/pidfile rw,
然后重新加载 AppArmor 配置文件
# apparmor_parser -R /etc/apparmor.d/usr.sbin.dnsmasq # apparmor_parser /etc/apparmor.d/usr.sbin.dnsmasq
未找到镜像
默认情况下,注册表列表未填充,因为软件包中的文件来自上游。这意味着默认情况下,尝试拉取任何未指定注册表的镜像将导致类似于以下的错误
Error: short-name "archlinux" did not resolve to an alias and no unqualified-search registries are defined in "/etc/containers/registries.conf"
一个初始配置可能是以下内容
/etc/containers/registries.conf.d/00-unqualified-search-registries.conf
unqualified-search-registries = ["docker.io"]
/etc/containers/registries.conf.d/01-registries.conf
[[registry]] location = "docker.io"
这等效于默认的 docker 配置。
另一种不太方便的替代方法,但与未配置短名称的系统具有更高的兼容性,是在 Containerfile
或 Dockerfile
中使用完整的注册表路径。
Containerfile
FROM docker.io/archlinux/archlinux
权限被拒绝:OCI 权限被拒绝
$ podman exec openvas_openvas_1 bash
Error: crun: writing file `/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/user.slice/libpod-b3e8048a9b91e43c214b4d850ac7132155a684d6502e12e22ceb6f73848d117a.scope/container/cgroup.procs`: Permission denied: OCI permission denied
可以解决:BBS#253966
$ env DBUS_SESSION_BUS_ADDRESS= podman ... $ env DBUS_SESSION_BUS_ADDRESS= podman-compose ...
推送镜像到 Docker Hub:访问被拒绝/需要身份验证
当使用 podman push
将容器镜像推送到 Docker Hub 时,可能会发生以下错误:Requested access to the resource is denied
或 Authentication required
。以下提示可以帮助修复潜在问题
- 标记本地镜像
# podman tag <localImage> docker.io/<dockerHubUsername>/<dockerHubRepository>:<Tag>
- 推送标记的镜像
# podman push docker.io/<dockerHubUsername>/<dockerHubRepository>:<Tag> docker://docker.io/<dockerHubUsername>/<dockerHubRepository>:<Tag>
- 登录到 docker.io、Docker Hub 仓库和 Docker Hub 注册表服务器
# podman login -u <DockerHubUsername> -p <DockerHubPassword> registry-1.docker.io # podman login -u <DockerHubUsername> -p <DockerHubPassword> docker.io/<dockerHubUsername>/<dockerHubRepository> # podman login -u <DockerHubUsername> -p <DockerHubPassword> docker.io
- 在登录之前从所有注册表注销,例如,
# podman logout --all
- 在仓库的 Docker Hub Collaborators 选项卡中添加
<dockerHubUsername>
作为协作者
以 rootless 身份运行的 Buildah/Podman 期望绑定挂载是共享的,请检查它是否设置为私有
$ findmnt -o PROPAGATION /
PROPAGATION private
在这种情况下,请参阅 mount(8) § Shared_subtree_operations 并临时将挂载设置为共享,使用
# mount --make-shared /
要永久设置它,请编辑 /etc/fstab 并将shared 选项添加到所需的挂载点,然后重启。 它将产生如下条目
/etc/fstab
# <device> <dir> <type> <options> <dump> <fsck> UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults,shared 0 1
容器内部的网络问题
IP 网络
默认情况下,Podman 容器通过它们自己的虚拟网络接口与主机桥接。
例如,在容器内部,虚拟接口 eth0@if6
的 IP 地址为 10.89.0.3(IP 地址在您的系统上可能不同!)
container# ip addr
... 2: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 ... inet 10.89.0.3/24 brd 10.89.0.255 scope global eth0 valid_lft forever preferred_lft forever
在主机上,来自容器的数据包从主机端的另一个虚拟接口(此处命名为 podman1
)传出,就像通过 IP 地址 10.89.0.1 路由一样。
host# ip addr
... 4: podman1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 ... inet 10.89.0.1/24 brd 10.89.0.255 scope global podman1
尽管是虚拟 IP 地址,数据包仍然通过内核的数据包过滤系统路由,因此可能会被 iptables/nftables 规则阻止。 特别是,INPUT
或 FORWARD
iptables 过滤器链中的默认 DROP
策略和/或运行的防火墙(ufw, firewalld)在某些情况下会影响容器。 如果您认为可能是这种情况,请检查您的配置(例如使用 iptables -L -n -v
或 nft list ruleset
)。
在更改 docker-compose.yml
后,请注意,创建的网络(来自 networks:
部分)在使用 podman compose down
销毁环境时可能不会被销毁。 如果这是您的意图,请确保(如有必要,使用 podman network ls
和 podman network rm
检查并删除它们)。
DNS 和名称解析
名称解析由 Podman 的子系统处理(例如 aardvark-dns),它提供外部 DNS(通常通过主机的 DNS 解析器)和容器名称解析(例如,webserver.dns.podman
与 database.dns.podman
通信)。
在上面的示例中,容器由 Podman 通过 /etc/resolv.conf
自动配置,以向管道主机端端口 53 上运行的 DNS 解析器发出请求
container# cat /etc/resolv.conf
search dns.podman nameserver 10.89.0.1
检查您是否在主机上的端口 53 上运行了另一个 DNS 解析器(例如 Systemd-resolved 或 Unbound),因为它可能会干扰 Podman 名称解析。 如果是这种情况,您可以将 Podman 在主机上使用的端口更改为任何其他可用端口,Podman 应该会自动将来自容器的容器请求转发到主机上的正确端口
host# # cat /etc/containers/containers.conf
... dns_bind_port = 20053
内核不支持 overlay fs: 不支持在 <filesystem> 上使用 'overlay'
重新启动您的系统,如 General troubleshooting#Cannot use some peripherals after kernel upgrade 中所述。