Podman是2018年開始發展的容器技術,可作為Docker的替代品。
記得2021年在屏大的時候,教Linux的電通系劉教授請來業師演講,就說過Podman要取代Docker了,似乎是因為Kubernetes捨棄Docker的原因。網路上也很多人說Docker過氣了!
當時我對Linux還停留在Android手機Termux打打指令的概念,所以只弱弱的問了業師:Docker對桌面用戶有什麼好處呢…?我到底在工三小,ㄟ!現在看來也沒有錯,Podman跟Flatpak你敢說沒關係麼(轉真硬)
啊結果嘞?到現在Docker也還沒消失嘛!
Docker是2013年就開始發展的技術,圍繞著Docker開發的東西相對成熟許多。2019年我開始學著用Docker在Ubuntu跑 self-hosted服務,見到了許多自由軟體的開發者慣於使用Docker發表成品,只要幾條指令就能架起伺服器服務。
直到現在2024年,在用RHEL系Linux發行版的時候發現,RedHat似乎不愛Docker,加上Fedora Silverblue、RHEL、Rocky Linux預設就安裝了Podman,所以學著換用Podman。
測試環境:
- Rocky Linux 9
- Docker 27.3.1
- Podman 4.9.4
1. 遷移到Podman之前的需求考量#
目前我在Linux伺服器上跑的東西:Docker搭配Portainer,用網頁管理許多docker-compose,方便一鍵開關selt-hosted服務。並且搭配Nvidia Container Toolkit在容器中跑CUDA。
我需要跑這些容器服務:
- Portainer
- Jellyfin
- Immich
- Nextcloud
- qBittorrent + Gluetun VPN
- Minecraft基岩版伺服器
- ReDroid
- Ollama Open WebUI
- Stable Diffusion ComfyUI
一直覺得docker run
的指令加一堆引數很醜,所以比較喜歡用docker-compose.yaml
定義服務內容,一清二楚。
又,用指令管理這些服務很麻煩,所以希望有個漂亮的網頁來管理,那就是Portainer。它本身也是個容器服務。
切換到Podman能不能滿足需求呢?是否要整個stack換掉?
2. Podman相較於Docker的優點#
目前找到的很多捧Podman的資料都是RedHat提供的,他們甚至出了一本 The Container Commandos兒童著色本讓你認識Podman……這是什麼"JavaScript for Kids"書籍的變體嗎!?
就我的觀察,Podman優點如下:
- 同為開源軟體,Podman比較傾向自由軟體,而不像Docker那樣商業味濃厚。(屁啦也不想想RedHat是什麼公司)
- Podman指令跟Docker長得很像,甚至有比較誇張的說法:設一條alias指令就能無縫從Docker轉換到Podman。
- Podman因為沒有常駐程式(daemonless),所以跟Systemd整合更好,不會像Dockerd一樣跟Systemd打架,並且迴避掉Docker單點故障的問題。若要讓Podman容器開機自動啟動,可以用Podman Quadlet將其設定為Systemd服務,統一用Systemd指令管理。
- Podman可以跟Kubernetes整合,Docker很難。
- Podman支援rootless模式執行,提升安全性。這點有點類似Flatpak,讓普通用戶也能任意安裝程式。
- 許多RHEL系的Linux發行版內建Podman,不像Docker需要額外安裝。
- 託管容器映像檔的Docker Hub是一家商業平台,2023年曾經發生過趕走開源軟體開發者的事情。那麼RedHat建立的Quay.io又如何呢?很抱歉,它也是商業平台,而且收費更貴,不過它伺服器是開源的,允許使用者自架Docker Registry。
- Podman主要支援的是Linux,至於其他系統,透過完全開源的Podman Desktop程式,讓Windows和macOS用戶也能用Podman,取代閉源的Docker Desktop。
3. 安裝Podman#
Podman跟Docker是可以共存的。
大部分Linux發行版的套件庫都有收,Rocky Linux的安裝方法:
sudo dnf install podman
Podman指令請參考 官方手冊,大部分都跟Docker類似。
4. Podman的權限問題#
比較讓我困惑的是Docker只要將一般使用者加入docker群組就能直接執行docker指令,拉取的映像檔會存在同一個位置,因此docker run
和sudo docker run
是沒差的。
換成Podman,得加--privileged
或者sudo podman
才能確保容器能存取所有裝置,而不會有權限不足或是無法使用低位數通訊埠的問題。這樣下來,podman run
和sudo podman
就會出現差異,二者的映像檔會存在不同的位置,並且權限也有差異。
因為伺服器主要是我一個人管理的,故使用個別使用者能自行管理的rootless podman沒什麼意義,還得調一堆參數開放權限給一般使用者。
所以我就索性所有指令都用sudo podman
跑了。是的,雖然這是個人伺服器,但是我不會什麼指令都用root使用者操作。
還有一點要注意:RHEL系Linux發行版有很嚴格的SELinux規則,即使用sudo podman
,容器有時仍會在存取特定目錄的時候跳出權限不足的錯誤,得用引數--security-opt label=disable
繞過。
5. Podman開機自動啟動容器#
因為Podman是daemonless,所以無法像Docekr一樣在dockerd服務啟動後,就自動帶起所有--restart=always
引數的容器。
得自行寫Systemd Unit設定容器自動啟動,用Systemd去管理容器服務。專為Podman設計的Systemd Unit稱為「Quadlet」,取代舊版的podman-generate-systemd。Quadlet可以使用Systemd來設定多個容器之間的啟動依賴關係。
不過手寫Quadlet檔案有點麻煩,所以用 Podlet工具來自動生成吧。
例如,要新增一個開機啟動的hello-world容器:
# 叫Podlet按照指令`podman run hello-world`生成一個Quadlet檔
podlet --file . --install --description "Hello World" podman run hello-world
cat hello-world.container
# 安裝到Systemd專為容器設計的目錄
sudo mv hello-world.container /etc/containers/systemd/
sudo systemctl daemon-reload
# 讓Quadlet檢查要啟動的容器
sudo /usr/libexec/podman/quadlet --dryrun
# 設定開機自動啟動hello-world容器
sudo systemctl enable --now hello-world.service
sudo systemctl status hello-world.service
6. Podman有docker-compose嗎?#
儘管Podman的指令跟Docker很像,但講到docker-compose就不是那麼一回事了。
Podman得安裝「podman-compose」才能用docker-compose來啟動容器,或者改用Podman的「Pods」來做事,後者有一套類似docker-compose的yaml格式能用。
但很多開發者主流還是Docker呀!他們發表專案的時候只會提供docker-compose範本,整個重寫為Pods太麻煩了,還是用docker-compose吧。
用系統套件管理器安裝 podman-compose,這是個Python封裝的前端。
然後podman-compose
指令的用法就跟docker compose
一樣了。
cd "/含有docker-compose.yaml的目錄"
sudo podman-compose pull
sudo podman-compose up -d
如果要開機自動啟動,則得安裝 Podlet,讓它依照docker-compose內容轉換為Systemd Unit。轉換過程可能會出錯,得自行調整為Qualdet的語法。
podlet compose docker-compose.yaml
不過,實務上我不太會這樣用指令啟動docker-compose,多半都是透過Portainer網頁界面啟動,如此一來就不用費心去轉換docker-compose了。
7. Podman的網頁管理界面?#
RHEL系Linux發行版內建多功能的 Cockpit網頁界面。
Cockpit是有叫做cockpit-podman
的模組啦,但界面很陽春,只能建立Pods,目前還不支援podman-compose。
我覺得還是Portainer功能比較齊全。
所幸,根據 官方文件,Portainer可以連線到Podman的socket,所以網頁界面是可以沿用的:
sudo systemctl enable --now podman.socket
sudo podman run -d \
-p 9443:9443 \
--name portainer \
--restart=always \
--privileged \
-v /run/podman/podman.sock:/var/run/docker.sock \
-v portainer_data:/data portainer/portainer-ce:2.23.0
不過Docker的socket跟Podman不一樣,Portainer的備份功能又僅限同一部機器復原,不能用Backup匯出舊有Portainer資料,所以得手動遷移舊有的Stacks (docker-compose)。
之後就能在Portainer頁面使用docker-compose啟動容器了。
上面提過Podman容器不會在開機後自動啟動,Portainer對此也無能為力。它能夠啟動與停止Podman容器,但是要讓Podman容器開機自動啟動,你就得自己設定Quadlet。
所以,我折衷的作法就是給Portainer容器設定開機自動啟動,再從Portainer的界面去手動啟動容器。
podlet --file . --install --description "Portainer" podman run -d \
-p 9443:9443 \
--name portainer \
--restart=always \
--privileged \
-v /run/podman/podman.sock:/var/run/docker.sock \
-v portainer_data:/data portainer/portainer-ce:2.23.0
sudo mv portainer.container /etc/containers/systemd/
sudo systemctl daemon-reload
sudo systemctl enable --now portainer.service
8. Docker轉Podman後服務都正常嘛?#
至少大部分服務都正常執行,Nvidia Container Tooklit也能夠讓Podman跑CUDA。
僅ReDroid因為架構特殊,需要自訂binfderfs的核心所以比較難處理。此問題與Podman無關,而是Rocky Linux核心本身的問題。
最大的問題還是Podman本身與Docker做事的方法本就不太一樣,或許Portainer還能保持一點相容性,但若要善用Podman的潛力,就得將服務模式調整為Pods的工作方式。
可是這樣要架服務就滿麻煩的了,以後就不能直接抄開發者給的docker-compose啦。