Podman 作為無守護行程的容器引擎,其 API 與系統服務的整合至關重要。本文除了介紹如何設定 Podman API 服務外,也說明瞭如何利用 systemd 管理 Podman 服務,確保服務的穩定性和可靠性。同時,文章也涵蓋了容器網路組態的檢查,包含路由表檢視和連線測試,以及如何透過埠對映將容器服務暴露給外部網路。最後,文章也示範了容器生命週期管理的常用指令,例如如何從執行中的容器分離,方便開發者和系統管理員進行容器操作。

組態你的環境

正如我們之前提到的,Podman 是一個無守護程式的容器管理器,它不需要背景服務來執行容器。然而,使用者可能需要與 Podman 公開的 Libpod API 互動,特別是在從根據 Docker 的環境遷移時。

Podman 可以透過 UNIX 通訊端(預設行為)或 TCP 通訊端公開其 API。後者選項安全性較低,因為它使 Podman 可以從外部存取,但在某些情況下是必要的,例如當它應該被 Windows 或 macOS 工作站上的 Podman 使用者端存取時。

重點注意事項

在暴露於網際網路的機器上使用 TCP 端點執行 API 服務時要小心,因為該服務將全域可存取。

以下命令在 UNIX 通訊端上公開 Podman API:

$ sudo podman system service --time=0 \
unix:///run/podman/podman.sock

執行此命令後,使用者可以連線到 API 服務。

內容解密:

此命令的作用是啟動 Podman 的系統服務,並將其組態為透過 UNIX 通訊端 /run/podman/podman.sock 提供 API 服務。 --time=0 表示服務將持續執行,直到手動停止。

在終端機視窗上執行此命令並不是一個方便的方法。相反,最佳方法是使用 systemd 通訊端(參見 man systemd.socket)。

systemd 中的通訊端單元是一種特殊的服務啟動器:當請求到達通訊端的預定義端點時,systemd 立即啟動同名的服務。

安裝 Podman 時,會建立 podman.socketpodman.service 單元檔案。 podman.socket 的內容如下:

# cat /usr/lib/systemd/system/podman.socket
[Unit]
Description=Podman API Socket
Documentation=man:podman-system-service(1)
[Socket]
ListenStream=%t/podman/podman.sock
SocketMode=0660
[Install]
WantedBy=sockets.target

內容解密:

此組態定義了一個名為 podman.socket 的通訊端單元,監聽 /run/podman/podman.sock 上的連線請求,並設定通訊端的許可權模式為 0660

podman.service 的內容如下:

# cat /usr/lib/systemd/system/podman.service
[Unit]
Description=Podman API Service
Requires=podman.socket
After=podman.socket
Documentation=man:podman-system-service(1)
StartLimitIntervalSec=0
[Service]
Type=exec
KillMode=process
Environment=LOGGING="--log-level=info"
ExecStart=/usr/bin/podman $LOGGING system service
[Install]
WantedBy=multi-user.target

內容解密:

此組態定義了一個名為 podman.service 的服務單元,它依賴於 podman.socket 單元。當 podman.socket 接收到連線請求時,systemd 將啟動 podman.serviceExecStart 欄位指定了要執行的命令,即 podman system service 命令。

要啟用和啟動通訊端單元,請執行以下命令:

# systemctl enable --now podman.socket

我們可以使用簡單的 curl 命令測試結果:

# curl --unix-socket /run/podman/podman.sock \
http://d/v3.0.0/libpod/info

內容解密:

此命令透過 UNIX 通訊端 /run/podman/podman.sock 向 Podman API 傳送 HTTP 請求,以取得容器引擎的組態資訊。

列印的輸出將是一個 JSON 負載,其中包含容器引擎組態。

當我們存取該 URL 時,底層發生了什麼?當連線請求發出時,服務單元立即由通訊端觸發啟動。有些人可能會注意到第一次執行命令時有輕微的延遲(大約十分之一秒)。

在 5 秒鐘的不活動後,podman.service 將再次停用。這是由於 podman system service 命令的預設行為,除非傳遞 --time 選項以提供不同的超時時間(值為 0 表示永久)。

自訂 Podman 的行為(可選)

Podman 的預設組態適用於大多數使用案例,但其組態具有很高的靈活性。以下組態檔案可用於自定義其行為:

  • containers.conf:此 TOML 格式的檔案包含 Podman 執行時組態,以及 conmon 和容器執行時二進位制檔案的搜尋路徑。它預設安裝在 /usr/share/containers/ 路徑下,可以被 /etc/containers/containers.conf$HOME/.config/containers/containers.conf 檔案覆寫,用於系統範圍和使用者範圍的設定。
  • storage.conf:此 TOML 格式的檔案用於自定義容器引擎使用的儲存設定。特別是,此檔案允許您自定義預設儲存驅動程式,以及容器儲存的讀寫目錄(也稱為圖形根目錄),這是額外的驅動程式儲存選項。預設情況下,驅動程式設定為 overlay。

內容解密:

這些組態檔案可以用來自定義 Podman 的行為,例如日誌記錄、DNS 解析、環境變數、分享記憶體使用、Cgroup 管理等。使用者可以根據自己的需求修改這些組態檔案,以實作自定義的容器管理。

執行第一個容器

在前一節中,我們探討瞭如何在您偏好的 Linux 發行版上安裝 Podman,以及安裝後的基本套件內容。現在,我們可以開始使用這個無需守護程式的容器引擎。

Podman 的基本組態

在執行第一個容器之前,瞭解 Podman 的一些基本組態是非常有用的。這些組態包括使用者層級的設定,這些設定會影響無根(rootless)容器的行為。使用者層級的組態檔案位於 $XDG_CONFIG_HOME/containers/storage.conf$HOME/.config/containers/storage.conf

此外,還有一些其他重要的組態檔案:

  • mounts.conf:定義了在啟動容器時自動掛載的卷標。這對於將金鑰和憑證等秘密自動傳遞到容器內非常有用。預設路徑是 /usr/share/containers/mounts.conf,可以透過位於 /etc/containers/mounts.conf 的檔案進行覆寫。在無根模式下,覆寫檔案可以放置在 $HOME/.config/containers/mounts.conf

  • seccomp.json:這是一個 JSON 檔案,允許使用者自定義容器內行程允許執行的系統呼叫,並定義被阻止的系統呼叫。這個主題將在第 11 章「容器安全」中再次討論,以提供對容器安全約束的更深入理解。該檔案的預設路徑是 /usr/share/containers/seccomp.jsonseccomp 手冊頁(man seccomp)提供了關於 seccomp 在 Linux 系統上如何運作的概述。

  • policy.json:這是一個 JSON 檔案,定義了 Podman 如何執行簽名驗證。該檔案的預設路徑是 /etc/containers/policy.json,可以透過使用者層級的 $HOME/.config/containers/policy.json 進行覆寫。

    這個組態檔案接受三種策略:

    • insecureAcceptAnything:接受來自指定註冊中心的任何映像。
    • reject:拒絕來自指定註冊中心的任何映像。
    • signedBy:只接受由特定已知實體簽名的映像。

    預設組態是接受每個映像(insecureAcceptAnything 策略),但可以修改為只提取可透過簽名驗證的受信任映像。使用者可以定義自定義 GPG 金鑰來驗證簽名和簽署它們的身分。有關可能的策略和組態範例的更多詳細資訊,請參閱相關的手冊頁(man containers-policy.json)。

執行第一個容器

現在,是時候執行我們的第一個容器了。

在 Podman 中,執行容器的操作是透過 podman run 命令處理的,該命令接受許多選項來控制剛執行的容器的行為、隔離、通訊、儲存等。

執行一個全新的容器的最簡單和最短的 Podman 命令如下:

$ podman run <imageID>

我們必須用想要執行的映像名稱/位置/標籤替換 imageID 字串。如果映像不在快取中,或者我們之前沒有下載過,Podman 將為我們從相應的容器註冊中心提取映像。

互動式和偽終端

為了介紹這個命令及其選項,讓我們從簡單的開始,執行以下命令:

$ podman run -i -t fedora /bin/bash

輸出結果如下:

Resolved "fedora" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull registry.fedoraproject.org/fedora:latest...
Getting image source signatures
Copying blob ecfb9899f4ce done
Copying config 37e5619f4a done
Writing manifest to image destination
Storing signatures
[root@ec444ad299ab /]#

讓我們看看 Podman 在我們執行前面的命令後做了什麼:

  1. 它識別出映像名稱 fedora 是最新 Fedora 容器映像的別名。
  2. 然後,它意識到映像在本地快取中缺失,因為這是我們第一次嘗試執行它。
  3. 它從正確的註冊中心下載映像。它選擇了 Fedora Project 註冊中心,因為它與註冊中心組態中的別名相匹配。
  4. 最後,它啟動了容器,並為我們提供了一個互動式 shell,執行我們請求的 Bash shell 程式。

前面的命令提示了一個互動式 shell,這得益於我們可以分析的兩個選項:

  • --tty, -t:使用此選項,Podman 分配一個偽終端(見 man pty),並將其附加到容器的標準輸入。
  • --interactive, -i:使用此選項,Podman 保持 stdin 開啟並準備好附加到前面的偽終端。

#### 內容解密:

  1. -i-t 選項用於啟動一個互動式的容器 shell。-i 確保容器的 stdin 保持開啟,而 -t 為容器分配一個偽終端,使得我們能夠與容器內的 shell 進行互動。
  2. 當容器被建立時,其內的隔離行程將執行在一個可寫入的根檔案系統上,這是由層疊覆寫(layered overlay)實作的。這允許任何行程寫入檔案,但別忘了這些變更只會持續到容器執行為止,因為容器預設是短暫的。
  3. 在互動式 shell 中,我們可以執行任何命令並檢查其輸出,如安裝 iputilsiproute 軟體包的範例所示。

在互動式 Shell 中執行命令

現在,您可以在我們剛剛開啟的控制檯中執行任何命令並檢查其輸出:

[root@ec444ad299ab /]# dnf install -y iputils iproute

輸出結果如下:

Last metadata expiration check: 0:01:50 ago on Mon Sep 13 08:54:20 2021.
Dependencies resolved.
================================================================================
 Package           Architecture   Version                  Repository      Size
================================================================================
Installing:
 iproute           x86_64         5.10.0-2.fc34            fedora         679 k
 iputils           x86_64         20210202-2.fc34          fedora         170 k

#### 內容解密:

  1. 在容器內執行 dnf install 命令來安裝所需的軟體包,如 iputilsiproute
  2. dnf 是 Fedora 系統上的軟體包管理器,用於安裝、更新和管理軟體包。
  3. 安裝過程中,dnf 解析依賴關係並下載必要的軟體包進行安裝。
  4. 安裝完成後,這些軟體包將可用於在容器內進行網路相關的操作和測試。

執行第一個容器

安裝依賴與網路組態檢查

在前面的例子中,我們安裝了兩個套件來檢查容器的網路組態。首先,進入容器後,我們執行了 ip r 命令來檢視路由表:

[root@ec444ad299ab /]# ip r
default via 10.0.2.2 dev tap0
10.0.2.0/24 dev tap0 proto kernel scope link src 10.0.2.100

內容解密:

  1. default via 10.0.2.2 dev tap0:表示預設路由是透過 tap0 網路介面將流量導向 10.0.2.2
  2. 10.0.2.0/24 dev tap0 proto kernel scope link src 10.0.2.100:表示 10.0.2.0/24 網段直接連線在 tap0 網路介面上,且本機的 IP 地址是 10.0.2.100

接著,我們使用 ping 命令測試與預設路由器的連線:

[root@ec444ad299ab /]# ping -c2 10.0.2.2
PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data.
64 bytes from 10.0.2.2: icmp_seq=1 ttl=255 time=0.030 ms
64 bytes from 10.0.2.2: icmp_seq=2 ttl=255 time=0.200 ms
---
 10.0.2.2 ping statistics 
---
2 packets transmitted, 2 received, 0% packet loss, time 1034ms
rtt min/avg/max/mdev = 0.030/0.115/0.200/0.085 ms

內容解密:

  1. ping -c2 10.0.2.2:傳送兩個 ICMP 請求到 10.0.2.2
  2. 結果顯示兩個請求都成功收到回應,丟包率為 0%,證明網路連線正常。

從執行中的容器分離

使用 Podman 時,可以啟動一個互動式的容器並在稍後分離。首先,啟動一個容器:

$ podman run -i -t registry.fedoraproject.org/f29/httpd

內容解密:

  1. -i:保持 STDIN 開啟,允許互動操作。
  2. -t:分配一個偽終端(pseudo-TTY),模擬一個終端介面。

要從容器分離,按下 Ctrl + PCtrl + Q

[Tue Sep 14 09:26:05.755346 2021] [core:notice] [pid 1:tid 140416655523200] AH00094: Command line: ‘httpd -D FOREGROUND’

內容解密:

  1. 這條命令讓 Apache HTTP Server 以前景模式執行。
  2. 使用 Ctrl + PCtrl + Q 分離後,容器繼續在背景執行。

網路埠對映

Podman 提供 -p 選項來進行埠對映,將容器內的埠對映到主機的埠:

$ podman run -p 8080:8080 -d -i -t registry.fedoraproject.org/f29/httpd

內容解密:

  1. -p 8080:8080:將容器內的 8080 埠對映到主機的 8080 埠。
  2. -d:以分離模式執行容器。

檢視容器的埠對映:

$ podman port fc9d97642801
8080/tcp -> 0.0.0.0:8080

內容解密:

  1. 這表示將容器的 8080/tcp 埠對映到主機的所有 IP 地址上的 8080 埠。

測試埠對映是否成功:

$ curl –s 127.0.0.1:8080 | head

內容解密:

  1. 使用 curl 命令存取 http://127.0.0.1:8080,並顯示前幾行回應內容。