Docker 改變了軟體部署的方式,但它的架構有一個從第一天就存在的問題:所有容器操作都通過一支以 root 身分執行的常駐 daemon。這支 dockerd 一旦被攻破,攻擊者等於拿到整台主機的最高權限。Docker 後來加了 rootless mode,但那是外掛上去的,不是預設行為。
Podman 從設計階段就走了不同的路。沒有 daemon、預設 rootless、CLI 語法和 Docker 幾乎完全相容。對已經熟悉 Docker 的人來說,遷移成本低到可以忽略。
架構差異:daemon 與 fork-exec 的根本分歧
Docker 的運作模型是 client-server:你下的每一條 docker run 指令都是透過 REST API 送給 dockerd,由 daemon 去呼叫 containerd 和 runc 來建立容器。這代表 daemon 是單點故障——它 crash 了,所有容器的管理介面就斷了。
Podman 用的是 fork-exec 模型。每次執行 podman run,Podman 直接 fork 出 conmon(container monitor)程序來管理容器的生命週期,不需要任何背景 daemon。容器程序是你的 shell 的子程序,用 ps 就能看到完整的程序樹。
這個設計帶來一個附帶好處:systemd 可以直接管理容器。Docker 需要額外的 wrapper script 才能被 systemd 正確追蹤,Podman 天生就跟 systemd 合得來。
安裝與基本使用
在 Ubuntu 24.04 LTS 和 Debian 12 上:
1 | sudo apt update && sudo apt install -y podman |
RHEL 系列(AlmaLinux、Rocky Linux)預設就有 Podman,不需要額外安裝。
裝完之後,試試看:
1 | podman run --rm docker.io/library/alpine echo "hello from podman" |
注意 Podman 預設需要完整的 image reference(docker.io/library/alpine 而不只是 alpine)。如果你不想每次都打完整路徑,編輯 /etc/containers/registries.conf:
1 | unqualified-search-registries = ["docker.io"] |
加了這行之後 podman run alpine 就跟 Docker 一樣的行為。
對於已經習慣打 docker 的人,設定一個 alias 就能無痛過渡:
1 | echo 'alias docker=podman' >> ~/.bashrc |
這不是開玩笑——Podman 的 CLI 介面就是照著 Docker 做的,超過 95% 的指令和參數都完全相同。
Rootless 容器到底改變了什麼
Docker 的 rootless mode 要另外設定,而且有不少限制。Podman 的 rootless 是預設值,不需要任何額外設定。
在 rootless 模式下,容器程序映射到你的使用者 UID namespace 裡。即使容器內部的程序以 root(UID 0)執行,在宿主機上它對應的是一個無特權的 UID。這表示就算容器被攻破、攻擊者拿到容器內的 root,他在宿主機上什麼都做不了。
1 | # 以一般使用者執行 |
有一個實務上要注意的地方:rootless 模式下,bind mount 的檔案權限可能跟你預期的不同。容器內的 root(UID 0)對應到宿主機上的 subuid 範圍,所以直接掛載的目錄可能出現權限被拒。解決方式是在 volume 掛載時加上 :Z 標籤(SELinux 環境)或使用 --userns=keep-id 把容器的 UID 對應回宿主機使用者:
1 | podman run -v ./data:/app/data:Z --userns=keep-id myapp |
Quadlet:用 systemd 管理容器的正確方式
Podman 4.4 之後引入的 Quadlet 是目前在單機上管理容器最優雅的方式。你寫一個類似 INI 格式的 .container 檔案,systemd 會在 daemon-reload 時自動把它轉成標準的 service unit。
以跑一個 Nginx 容器為例,建立 ~/.config/containers/systemd/web.container:
1 | [Container] |
然後:
1 | systemctl --user daemon-reload |
就這樣。你的 Nginx 容器現在是一個標準的 systemd service,可以用 systemctl status、journalctl 來查看狀態和日誌,機器重開後自動啟動,crash 後自動重啟。
Quadlet 也支援 .volume、.network、.pod 檔案類型,可以把完整的多容器架構用純宣告式的方式管理。如果是系統級的服務(需要 root),把檔案放到 /etc/containers/systemd/ 就好。
跟 Docker Compose 比起來,Quadlet 的優勢是跟作業系統的服務管理完全整合。你不需要記住 docker compose up -d 之後還要確認服務有沒有正確啟動,systemd 會處理所有的生命週期管理,包含開機啟動、失敗重試、資源限制和日誌收集。
從 Docker Compose 遷移
已經有一堆 docker-compose.yml 的專案怎麼辦?兩條路可以選。
用 podman-compose(低門檻):
1 | pip install podman-compose |
podman-compose 能直接讀取既有的 docker-compose.yml。大部分簡單到中等複雜度的 Compose 檔案可以直接跑,不需要修改。
用原生 Docker Compose + Podman socket(高相容性):
Podman 可以啟動一個 Docker 相容的 API socket,讓 Docker Compose v2 直接對接:
1 | systemctl --user enable --now podman.socket |
這種方式的相容性更高,因為你用的是正式的 Docker Compose binary,只是底層執行引擎換成了 Podman。複雜的 Compose 功能(build、healthcheck、depends_on condition)都能正常運作。
遷移時最常遇到的三個問題:
- Image 名稱:Podman 預設要求完整的 registry 路徑,
image: nginx要改成image: docker.io/library/nginx(或設定unqualified-search-registries) - Volume 權限:rootless 模式下的 UID 映射可能導致檔案權限問題,加
:Z或userns_mode: keep-id處理 - 特權 port:rootless 模式下無法綁定 1024 以下的 port,需要調整
sysctl net.ipv4.ip_unprivileged_port_start=80或改用 rootful 模式
什麼場景該用 Podman,什麼場景留在 Docker
建議直接用 Podman 的情境:全新的 Linux 伺服器部署、對安全有嚴格要求的環境(金融、醫療、政府)、RHEL/CentOS Stream 生態系(Podman 是原生工具)、以及跑在資源有限的 VPS 上的單機服務(少一支 daemon 就是少一份記憶體開銷)。
留在 Docker 比較合理的情境:團隊已經重度依賴 Docker Desktop 的 GUI 工作流、CI/CD pipeline 綁定了 Docker-in-Docker(DinD)、或者使用了 Docker Swarm(Podman 沒有對應的 orchestration 功能,不過這個需求通常該往 Kubernetes 走)。
兩者建出來的 image 完全相容——都是 OCI 標準格式。在開發機用 Docker 建 image,在生產伺服器用 Podman 跑,完全沒問題。
在 VPS 上部署容器化服務,底層硬體的穩定性直接影響運行品質。NCSE Network 提供搭載 Intel Gold CPU 與 NVMe SSD 的臺灣本地 VPS,機房位於是方電訊,適合需要低延遲、穩定網路的容器部署場景。更多資訊可到 ncse.tw 了解。