Podman 是 Docker 的替代品,提供類似的接口。它支持無根容器和為 docker-compose 提供的墊片服務。
安裝
Podman 依賴 netavark包 作為有根容器的默認網絡後端(參見 podman-network(1))。Netavark 依賴 aardvark-dns包 實現同一網絡中容器間的名稱解析。對替代網絡後端(CNI,cni-plugins包)的支持已棄用。
若想替代 Docker,可以安裝 podman-docker包 來模擬 docker 二進制文件及手冊頁。
與 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
中該用戶的 ID 範圍。參見 #Rootless Podman 了解必要配置。
--userns
標誌還有其他有效值,詳見 podman-run(1)。用戶命名空間模式也可通過 containers.conf(5) 在全局或用戶級別配置。
無根 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
來啟用。
設置 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
文件確認已分配範圍。
為 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
這將為 username
用戶分配 524288-589823
的 UID/GID 範圍。若範圍已被其他用戶占用,需相應調整。
可能需要重啟以應用更改。
- 此為臨時方案,Podman 未官方支持 homed。
- 這是 systemd-homed 的 已知問題。
- 使用 Docker 似乎可行(將用戶加入
docker
組,但有 安全隱患)。
使 subuid 和 subgid 變更生效
無根 Podman 使用暫停進程保持非特權命名空間活動,這會阻止 /etc/subuid
和 /etc/subgid
的更改在暫停進程運行時生效。要使更改生效需運行:
$ podman system migrate
此後,上述文件中指定的用戶/組即可啟動和運行 Podman 容器。
啟用原生無根 overlay
過去需要使用 fuse-overlayfs包 在無根環境中進行 FUSE overlay 掛載。但現代 Podman 和 Linux 內核 支持 原生 無根 overlay,可獲得更好性能。
--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: 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 複製 [2] [3]。
有關可用選項和其他配置的詳細信息,請參閱 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 socket 環境變量:
$ 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 Container Toolkit 為 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 容器。
對於無根(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
的服務,此時可create、reload 並 enable 一個虛擬服務解決。參考 [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 參數。
更多示例(包括 Pod
、Volume
、Network
和 Image
單元)請參閱 podman-systemd.unit(5) § EXAMPLES。
鏡像
/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 作為 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。
在無根模式下使用橋接網絡創建容器時報錯
若使用 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
找不到鏡像
默認情況下註冊表列表未填充(包內文件來自上游)。這意味著若未指定註冊表,嘗試拉取任何鏡像都會出現如下錯誤:
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 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 denied
或 Authentication 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用戶名>
為協作者
無根 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 網絡
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
策略和運行的防火牆(ufw、firewalld)可能影響容器。若懷疑此類問題,請檢查配置(如使用 iptables -L -n -v
或 nft list ruleset
)。
修改 docker-compose.yml
後,注意使用 podman compose down
銷毀環境時,networks:
部分創建的網絡可能不會被刪除。如需刪除,請使用 podman network ls
和 podman 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-resolved 或 Unbound),這可能會干擾 Podman 名稱解析。若有此情況,可將 Podman 在宿主上的監聽埠更改為其他可用埠,Podman 會自動轉發容器請求:
宿主# # cat /etc/containers/containers.conf
... dns_bind_port = 20053
內核不支持 overlay 文件系統:'overlay' 不支持在 <文件系統> 上使用
請按照 常規故障排除#內核升級後部分外設無法使用 中的說明重啟系統。