跳至內容
出自 Arch Linux 中文维基

Podman 是 Docker 的替代品,提供類似的接口。它支持無根容器和為 docker-compose 提供的墊片服務。

安裝

安裝 podman 軟體包。

Podman 依賴 netavark 作為有根容器的默認網絡後端(參見 podman-network(1))。Netavark 依賴 aardvark-dns 實現同一網絡中容器間的名稱解析。對替代網絡後端(CNI,cni-plugins)的支持已棄用。

若想替代 Docker,可以安裝 podman-docker 來模擬 docker 二進制文件及手冊頁。

Docker 不同,Podman 不需要守護進程,但可通過 cockpit-podmancockpit 等服務提供 API。

關於構建容器的高級用法,請參閱基於 Buildahpodman-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"]

本文或本章節的事實準確性存在爭議。

原因:containers-common 安裝的列表位於 /etc/containers/registries.conf.d/00-shortnames.conf。(在 Talk:Podman 中討論)


提示:為使未限定倉庫的搜索對知名鏡像更安全,可考慮將 https://github.com/containers/shortnames簡稱列表 集成到配置中,例如複製到 /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 中該用戶的 ID 範圍。參見 #Rootless Podman 了解必要配置。

--userns 標誌還有其他有效值,詳見 podman-run(1)。用戶命名空間模式也可通過 containers.conf(5) 在全局或用戶級別配置。

無根 Podman

警告:無根 Podman 依賴非特權用戶命名空間(CONFIG_USER_NS_UNPRIVILEGED),存在嚴重安全隱患,詳見 安全#沙盒程序

默認只有 root 可運行容器(內核術語中的命名空間)。使用無根 Podman 可提升安全性(攻擊者無法獲得系統 root 權限),並允許多個非特權用戶在同一機器上運行容器。另見 podman(1) § Rootless mode 和官方 無根教程(可能已過時)。

啟用 kernel.unprivileged_userns_clone

首先檢查 kernel.unprivileged_userns_clone 的值:

$ sysctl kernel.unprivileged_userns_clone

若當前值為 0,可通過 sysctl內核參數 設為 1 來啟用。

注意:linux-hardened 默認將 kernel.unprivileged_userns_clone 設為 0

設置 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

上述範圍可能已被系統首個用戶的默認範圍占用。如有疑問,請先查閱 /etc/subuid/etc/subgid 文件確認已分配範圍。

注意:許多鏡像需要 65536 個 UID/GID 進行映射(特別是基礎 busyboxalpine 鏡像)。建議為每個用戶至少分配該數量的 UID/GID 以保持與 Docker 的最大兼容性。
為 homed 管理用戶的變通方案

Homed 似乎不會為其用戶分配 giduid 條目。可手動運行:

# usermod --add-subuids 524288-589823 --add-subgids 524288-589823 username

或直接以 root 身份編輯以下配置文件並添加:

/etc/subuid
username:524288:65536
/etc/subgid
username:524288:65536

這將為 username 用戶分配 524288-589823 的 UID/GID 範圍。若範圍已被其他用戶占用,需相應調整。

可能需要重啟以應用更改。

注意:
使 subuid 和 subgid 變更生效

無根 Podman 使用暫停進程保持非特權命名空間活動,這會阻止 /etc/subuid/etc/subgid 的更改在暫停進程運行時生效。要使更改生效需運行:

$ podman system migrate

此後,上述文件中指定的用戶/組即可啟動和運行 Podman 容器。

啟用原生無根 overlay

過去需要使用 fuse-overlayfs 在無根環境中進行 FUSE overlay 掛載。但現代 Podman 和 Linux 內核 支持 原生 無根 overlay,可獲得更好性能。

注意:當使用修改後的 UID/GID 映射啟動無根容器,且尚未用指定容器鏡像和 UID/GID 映射創建過容器時,原生 overlay 相比 fuse-overlayfs 會有性能損失,因為必須更新磁碟上容器文件的所有 UID/GID。這對 --userns auto 尤其明顯,因為每次調用可能使用不同的 UID/GID 映射。詳情參見 Podman 性能指南

要從 fuse-overlayfs 遷移,運行以下命令(會刪除所有已拉取鏡像):

$ podman system reset

同時確保 Podman 使用 overlay 驅動且 containers-storage.conf(5) 中未定義 mount_program 參數。遵循 Docker#啟用本地覆蓋差異引擎(native overlay diff engine) 的說明。

驗證原生無根 overlay 是否啟用:

$ podman info | grep -i overlay

應顯示 graphDriverName: overlayNative Overlay Diff: "true"

網絡

Podman 依賴 passt,其提供的 pasta 是默認的無根網絡後端。

另一種無根網絡後端是 slirp4netns,在 Podman 5 之前是默認選項。

兩者主要區別在 Podman 5.0 重大變更 中概述:

Pasta 默認不進行網絡地址轉換(NAT),而是將主接口的 IP 地址複製到容器命名空間。

上游的 無根 Podman 缺陷 解釋了此變更的影響:

由於 pasta 複製主接口 IP 地址,容器無法通過該 IP 連接宿主機。這意味著除非有多個接口,否則必須顯式傳遞 pasta 網絡配置(通過 containers.conf 或運行時參數)才能實現容器間連接。
提示:容器到宿主的通信問題已在 Podman 5.3 修復

"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 選擇默認無根網絡工具,可設為 pastaslirp4netns。因此遇到問題時,可回退到 slirp4netns(需已安裝):

containers.conf
[network]
default_rootless_network_cmd = "slirp4netns"

存儲

容器鏡像和實例的存儲配置位於 /etc/containers/storage.conf

注意:使用 無根 Podman 時,可在 $XDG_CONFIG_HOME/containers/storage.conf 中按用戶覆蓋存儲設置。

默認的 overlay 驅動經過充分測試,支持在具備該功能的文件系統(BtrfsXFSZFS 等)上使用 reflink 複製 [2] [3]

有關可用選項和其他配置的詳細信息,請參閱 containers-storage.conf(5) § STORAGE_TABLE

外部架構

Podman 可通過 Wikipedia:binfmt_misc 系統運行與宿主機不同 CPU 架構的鏡像。

安裝 qemu-user-staticqemu-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-composepodman-compose。若兩者都安裝,優先使用 docker-compose。可通過 PODMAN_COMPOSE_PROVIDER 環境變量覆蓋此行為。

若使用 docker-compose,需為當前用戶 啟用 podman.socket 用戶單元 並設置 docker socket 環境變量:

$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock

使用 podman-compose 時無需此操作,因其直接調用 podman

注意:

NVIDIA GPU

NVIDIA Container Toolkit 為 NVIDIA GPU 提供容器運行時。安裝 nvidia-container-toolkit 軟體包,其包含的 pacman hook 會生成 GPU 的 CDI 規範並保存至 /etc/cdi/nvidia.yaml

測試配置:

$ podman run --rm --gpus all archlinux nvidia-smi -L
注意:NVIDIA CDI hook 無法與 --userns nomap--userns auto podman run 參數共同使用 [4]

帶重啟策略的容器

要自動啟動帶重啟策略的容器,請 啟用 podman-restart.service

Quadlet

Quadlet 允許通過 systemd 管理 Podman 容器。

對於無根(rootless) Podman,將 Quadlet 文件放置於以下目錄之一:

  • $XDG_CONFIG_HOME/containers/systemd/~/.config/containers/systemd/
  • /etc/containers/systemd/users/UID 對應指定 UID 的用戶
  • /etc/containers/systemd/users/ 適用於所有用戶

對於有根(rootful) Podman,目錄為 /etc/containers/systemd/

Podman 會讀取擴展名為 .container.volume.network.kube.image.pod 的 Quadlet 文件,並通過 systemd.generator(7) 生成對應的 .service 文件。Quadlet 文件會在系統啟動時讀取,或通過運行 daemon-reload 手動加載。

您也可以使用 podletAUR 從 Podman 命令生成 Quadlet 文件。

例如,以下命令用於運行 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 容器

# 容器之間可以使用 systemd 依賴項進行關聯,但需添加 ".service" 後綴。
# 例如:若要讓另一個容器等待本容器啟動,可在其 [Unit] 部分添加 "After=syncthing-lsio.service"

[Container]
ContainerName=syncthing
Image=lscr.io/linuxserver/syncthing:latest

# 啟用容器自動更新
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 映射是運行 linuxserver.io 無根容器所必需的。
# 這會將容器內的 UID=1000 映射到中間 UID=0。
# 對於無根 Podman,中間 UID=0 將被映射到當前用戶的 UID。
UIDMap=1000:0:1
UIDMap=0:1:1000
UIDMap=1001:1001:64536

[Service]
Restart=on-failure

# 延長超時時間以允許拉取鏡像
TimeoutStartSec=300

# [Install] 部分用於啟用生成的服務
[Install]
WantedBy=default.target

可通過以下命令驗證 Quadlet 文件:

$ /usr/lib/podman/quadlet -dryrun -user

然後 reload啟動syncthing-lsio.service

注意:

若無根容器未按預期在啟動時運行,請檢查 podman-user-wait-network-online.service 的狀態。若因超時失敗,可能是由於缺少激活 network-online.target 的服務,此時可createreloadenable 一個虛擬服務解決。參考 [5][6]

/etc/systemd/system/podman-network-online-dummy.service
[Unit]
Description=此服務僅用於激活 network-online.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/echo 激活 network-online.target 中...

[Install]
WantedBy=default.target
參閱 Systemd/User#Automatic_start-up_of_systemd_user_instances 了解如何在沒有開放會話時啟動無根容器。

Container 部分的可用選項列於 podman-systemd.unit(5) § Container units [Container]PodmanArgs= 可用於添加沒有對應文件選項的其他 Podman 參數。

更多示例(包括 PodVolumeNetworkImage 單元)請參閱 podman-systemd.unit(5) § EXAMPLES

鏡像

注意:可以省略鏡像的註冊表前綴,因為 Podman 會自動在 /etc/containers/registries.confunqualified-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 作為 C 庫實現,而非大多數 Linux 發行版使用的 glibc。由於 Arch Linux 使用 glibc,宿主機與 Alpine Linux 容器間存在許多功能差異,可能影響軟體性能和正確性。這些差異列於 https://wiki.musl-libc.org/functional-differences-from-glibc.html

注意:在 Arch Linux(或其他 glibc 系統)上構建的動態連結軟體在 Alpine Linux(或其他不同 C 庫系統)上運行時可能出現錯誤和性能問題。示例參見 [7][8][9]

CentOS

以下命令從 Docker Hub 拉取最新 CentOS 鏡像:

# podman pull docker.io/centos

各 CentOS 版本的完整標籤列表請參閱 Docker Hub 頁面。

Debian

以下命令從 Docker Hub 拉取最新 Debian 鏡像:

# podman pull docker.io/debian

各 Debian 版本的標準版和精簡版標籤列表請參閱 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 容器會停止。要避免此問題,請為運行容器的用戶啟用 lingering

也可按 podman-auto-update(1) § EXAMPLES 創建用戶 systemd 單元。

無根模式提交時報錯

Error committing the finished image: error adding layer with blob "sha256:02823fca9b5444c196f1f406aa235213254af9909fca270f462e32793e2260d8": Error processing tar file(exit status 1) permitted operation

檢查 存儲配置 中存儲驅動是否為 overlay。

在無根模式下使用橋接網絡創建容器時報錯

本文或本節內容已經過時。

原因: CNI 網絡後端已棄用。Netavark 後端是否存在此問題? (在Talk:Podman討論)

若使用 AppArmor,在啟用 dnsname 插件的情況下創建橋接網絡容器時可能遇到問題:

$ podman network create foo
/home/用戶/.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

找不到鏡像

本文或本章節可能需要合併到#Registries

附註: 相關內容應合併至配置章節。(在 Talk:Podman 中討論)

默認情況下註冊表列表未填充(包內文件來自上游)。這意味著若未指定註冊表,嘗試拉取任何鏡像都會出現如下錯誤:

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 的默認設置。

另一種兼容性更高但不太便捷的方式是在 ContainerfileDockerfile 中使用完整註冊表路徑:

Containerfile
FROM docker.io/archlinux/archlinux

權限被拒絕: OCI permission denied

$ 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 deniedAuthentication required 錯誤。以下提示可能有助解決:

  • 標記本地鏡像:
    # podman tag <本地鏡像> docker.io/<DockerHub用戶名>/<DockerHub倉庫>:<標籤>
  • 推送標記的鏡像:
    # podman push docker.io/<DockerHub用戶名>/<DockerHub倉庫>:<標籤> docker://docker.io/<DockerHub用戶名>/<DockerHub倉庫>:<標籤>
  • 登錄相關服務:
# podman login -u <DockerHub用户名> -p <DockerHub密码> registry-1.docker.io
# podman login -u <DockerHub用户名> -p <DockerHub密码> docker.io/<DockerHub用户名>/<DockerHub仓库>
# podman login -u <DockerHub用户名> -p <DockerHub密码> docker.io
  • 登錄前先登出所有註冊表:
    # podman logout --all
  • 在 Docker Hub 倉庫的 Collaborators 標籤頁添加 <DockerHub用戶名> 為協作者

WARN[0000] "/" is not a shared mount, this could cause issues or missing mounts with rootless containers

無根 Buildah/Podman 要求綁定掛載為共享模式,檢查是否設置為私有:

$ findmnt -o PROPAGATION /
PROPAGATION
private

若如此,參考 mount(8) § Shared_subtree_operations臨時設為共享:

# mount --make-shared /

永久生效,請編輯 /etc/fstab 並為對應掛載點添加 shared 選項後重啟。示例條目:

/etc/fstab
# <設備>                                <目錄> <類型> <選項> <dump> <fsck>
UUID=0a3407de-014b-458b-b5c1-848e92a327a3 /     ext4   defaults,shared   0      1

容器內網絡問題

IP 網絡

本文或本章節的事實準確性存在爭議。

原因: 此段關於防火牆影響的描述過於冗長。Netavark 使用 iptables 自動創建防火牆規則,因此 iptables 鏈的默認 DROP 策略不會造成問題。(在 Talk:Podman 中討論)


Podman 容器默認通過虛擬網絡接口橋接到主機。

例如容器內的虛擬接口 eth0@if6 擁有 IP 10.89.0.3(具體 IP 可能不同):

容器內# 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)傳出:

宿主機# 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 規則攔截。特別是 iptables 過濾鏈中的默認 DROP 策略和運行的防火牆(ufwfirewalld)可能影響容器。若懷疑此類問題,請檢查配置(如使用 iptables -L -n -vnft list ruleset)。

修改 docker-compose.yml 後,注意使用 podman compose down 銷毀環境時,networks: 部分創建的網絡可能不會被刪除。如需刪除,請使用 podman network lspodman network rm 手動操作。

DNS 與名稱解析

名稱解析由 Podman 子系統(如 aardvark-dns)處理,既提供外部 DNS(通常通過宿主的 DNS 解析器)也支持容器間名稱解析(例如 webserver.dns.podman 訪問 database.dns.podman)。

上例中,容器通過 /etc/resolv.conf 被自動配置為向宿主側埠 53 發送 DNS 請求:

容器內# cat /etc/resolv.conf
search dns.podman
nameserver 10.89.0.1

檢查宿主是否運行其他占用 53 埠的 DNS 解析器(如 Systemd-resolvedUnbound),這可能會干擾 Podman 名稱解析。若有此情況,可將 Podman 在宿主上的監聽埠更改為其他可用埠,Podman 會自動轉發容器請求:

宿主# # cat /etc/containers/containers.conf
...
dns_bind_port = 20053

內核不支持 overlay 文件系統:'overlay' 不支持在 <文件系統> 上使用

請按照 常規故障排除#內核升級後部分外設無法使用 中的說明重啟系統。

另請參閱