Podman 作為新一代容器引擎,以其無守護行程的架構和安全性優勢,逐漸受到開發者的青睞。然而,許多開發者已習慣使用 Docker Compose 管理多容器應用程式。因此,如何在 Podman 環境中有效利用 Docker Compose 成為一個關鍵議題。本文將詳細介紹如何在 Podman 中使用 Docker Compose,包含組態解析、命令操作、以及 systemd 和 Kubernetes 的整合,協助開發者無縫遷移並提升容器化應用程式的管理效率。

使用 Podman 與 Docker Compose 的整合

在容器化的世界中,Podman 逐漸成為 Docker 的一個有力替代方案,尤其是在無守護程式(daemonless)的架構下提供了更高的安全性和靈活性。然而,對於許多已經習慣使用 Docker Compose 的開發者來說,如何在 Podman 中使用 Docker Compose 成為一個重要的議題。本篇文章將探討 Podman 與 Docker Compose 的整合使用方法,並提供實用的範例與技巧。

Podman 中缺少的 Docker 命令

在轉移到 Podman 的過程中,瞭解兩者之間的命令差異是非常重要的。以下是一些 Docker 中存在但 Podman 中尚未支援的命令:

儘管 Podman 缺少了一些 Docker 命令,但它也引入了一些 Docker 中沒有的新命令,例如 podman generatepodman healthcheckpodman machine 等。這些新命令為容器管理和排程提供了更多的靈活性。

Docker Compose 簡介

Docker Compose 是一個強大的工具,用於定義和執行多容器的 Docker 應用程式。它使用 YAML 檔案來組態應用程式的服務,從而簡化了複雜應用程式的佈署和管理。

Docker Compose 快速入門

一個典型的 docker-compose.yaml 檔案定義了多個服務及其相關的卷和網路。以下是一個簡單的範例,展示瞭如何使用 Docker Compose 執行一個 Docker Registry:

services:
  registry:
    ports:
      - "5000:5000"
    volumes:
      - registry_volume:/var/lib/registry
    image: docker.io/library/registry
volumes:
  registry_volume: {}

內容解密:

  1. services: 定義了組成應用程式的服務。在這個範例中,只有一個名為 registry 的服務。
  2. ports: 將主機的 5000 埠對映到容器內的 5000 埠,使得外部可以存取 Registry 服務。
  3. volumes: 定義了一個名為 registry_volume 的卷,用於持久化儲存 Registry 的資料。
  4. image: 指定了要使用的 Docker 映象。在這個例子中,使用的是官方的 Registry 映象。

另一個更複雜的例子是 WordPress 與 MySQL 的組合:

services:
  db:
    image: docker.io/library/mysql:latest
    command: '--default-authentication-plugin=mysql_native_password'
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: wordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
      - db
    image: docker.io/library/wordpress:latest
    ports:
      - "80:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress

volumes:
  db_data: {}

內容解密:

  1. db 服務: 使用 MySQL 映象,並設定了必要的環境變數來初始化資料函式庫。
  2. wordpress 服務: 依賴於 db 服務,使用 WordPress 映象,並將主機的 80 埠對映到容器的 80 埠。
  3. depends_on: 表示 wordpress 服務依賴於 db 服務,確保資料函式庫服務在 WordPress 啟動前已經準備就緒。

在 Podman 中使用 Docker Compose

隨著 Podman v3.0 的發布,Podman 開始原生支援 Docker Compose。這意味著使用者可以直接使用 podman-compose 命令來執行和管理原本為 Docker Compose 組態的應用程式。

使用 podman-compose 的好處包括:

  • 無需守護程式: 繼承了 Podman 的無守護程式架構,提高了安全性和降低了資源消耗。
  • 相容性: 可以直接使用現有的 docker-compose.yaml 檔案,無需進行修改。

使用 Docker Compose 與 Podman 的技術

Docker Compose 基本概念與組態解析

Docker Compose 是一種用於定義和執行多容器 Docker 應用程式的工具。透過一個 YAML 檔案來組態應用程式的服務、網路和磁碟區,從而簡化了多容器應用的佈署和管理。

示例解析:WordPress 與 MySQL 服務組態

以下是一個典型的 docker-compose.yaml 檔案,用於啟動 WordPress 和 MySQL 服務:

version: '3'
services:
  db:
    image: docker.io/library/mysql:latest
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=wordpressroot
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress
    expose:
      - 3306
      - 33060

  wordpress:
    image: docker.io/library/wordpress:latest
    ports:
      - 8080:80
    restart: always
    environment:
      - WORDPRESS_DB_HOST=db
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress
      - WORDPRESS_DB_NAME=wordpress

volumes:
  db_data:

組態項解析

在上述組態中,主要包含兩個服務:dbwordpress

  • image:指定服務使用的 Docker 映象。
  • volumes:定義掛載到容器中的磁碟區。
  • restart:設定容器的重啟策略。
  • environment:設定傳遞給容器的環境變數。
  • exposeports:分別用於暴露容器埠和對映容器埠到主機埠。

內容解密:

  1. image 屬性:指定了服務所使用的 Docker 映象,例如 MySQL 和 WordPress。在此例中,使用了 Docker Hub 上的官方最新版本映象。
  2. volumes 組態:將主機上的 db_data 磁碟區掛載到 MySQL 容器的 /var/lib/mysql 目錄,用於持久化資料儲存。
  3. environment 環境變數:為容器設定了必要的環境變數,例如 MySQL 的 root 密碼、資料函式庫名稱、使用者名稱和密碼,以及 WordPress 連線資料函式庫所需的引數。
  4. exposeportsexpose 用於暴露容器的埠,而 ports 將容器內的埠對映到主機的埠,使得外部可以存取這些服務。

建置與執行 Docker Compose 應用程式

要啟動由 Docker Compose 管理的應用程式,可以使用以下命令:

$ docker-compose up

此命令會建立所有定義在 docker-compose.yaml 中的服務和相關磁碟區,並將輸出列印到標準輸出。

常見操作命令

  • 後台執行:使用 -d 選項讓應用程式在後台執行。

$ docker-compose up -d

*   **建置並啟動**:使用 `--build` 選項重新建置映像並啟動服務。

    ```bash
$ docker-compose up --build
  • 停止服務:使用 down 命令停止並移除容器、網路和相關資源。

$ docker-compose down


### 組態 Podman 以支援 Docker Compose

Podman 需要透過本地 UNIX socket 公開其 REST API 服務,以支援 Docker Compose。以下是組態步驟:

1.  **安裝必要套件**:在 Fedora 系統上安裝 `docker-compose` 和 `podman-docker`。

    ```bash
$ sudo dnf install docker-compose podman-docker
  1. 啟動 Podman socket 服務:啟用並啟動 podman.socket systemd 單元。

$ sudo systemctl enable –now podman.socket


#### 安全注意事項

*   Podman 的 UNIX socket 預設僅允許具有 root 許可權的程式存取。可以透過調整檔案許可權或設定自訂 ACLs 以放寬限制。

## 使用 Podman 與 docker-compose 執行 Compose 工作負載

為了幫助讀者學習如何操作 `docker-compose` 並在主機上建立協調的多容器佈署,我們將重用先前範例中的 Go REST API 與 Redis 記憶體儲存。

### 檢查 Dockerfile

首先,讓我們檢查用於建置應用程式的 Dockerfile:
```dockerfile
FROM docker.io/library/golang AS builder
# 複製檔案進行建置
RUN mkdir -p /go/src/golang-redis
COPY go.mod main.go /go/src/golang-redis
# 設定工作目錄
WORKDIR /go/src/golang-redis
# 下載依賴項
RUN go get -d -v ./...
# 安裝套件
RUN go build -v
# 執行階段映像
FROM registry.access.redhat.com/ubi8/ubi-minimal:latest as bin
COPY --from=builder /go/src/golang-redis/golang-redis /usr/local/bin
COPY entrypoint.sh /
EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]

內容解密:

  1. 多階段建置:Dockerfile 使用多階段建置,先在 golang 映像中編譯 Go 應用程式,然後將編譯好的二進位制檔案複製到 ubi-minimal 映像中,以減小最終映像的大小。
  2. 依賴項下載:使用 go get 命令下載依賴項,確保應用程式所需的套件都已安裝。
  3. 暴露埠:使用 EXPOSE 指令暴露 8080 埠,供外部存取。
  4. 入口點:使用 ENTRYPOINT 指令設定容器啟動時執行的命令,這裡是執行 entrypoint.sh 指令碼。

建置與執行應用程式

要建置和執行應用程式,需要切換到專案目錄並執行 docker-compose up 命令:

# cd Chapter13/golang-redis
# docker-compose up --build -d

輸出結果顯示 docker-compose 建立了兩個容器,分別是 golang-redis_redis_1golang-redis_web_1

檢查執行中的容器

使用 podman ps 命令檢查執行中的容器:

# podman ps

結果顯示兩個容器正在執行,且 golang-redis_web_1 容器的 8080 埠已對映到主機的 8080 埠。

網路設定

當建立 Compose 堆積疊時,Podman 會建立一個新的網路,名稱遵循 <project_name>_default 的模式。這個網路使用 dnsname 外掛程式來例項化一個 dnsmasq 程式,並將容器的 IP 解析為服務名稱。

# podman network ls | grep golang-redis

結果顯示網路名稱為 golang-redis_default

DNS 解析

dnsmasq 服務可以將服務名稱解析為容器的 IP 地址。在 /run/containers/cni/dnsname/golang-redis_default/addnhosts 檔案中,可以找到服務名稱與容器 IP 地址的對映關係:

# cat /run/containers/cni/dnsname/golang-redis_default/addnhosts

結果顯示 golang-redis_redis_1 的 IP 地址為 10.89.3.240,而 golang-redis_web_1 的 IP 地址為 10.89.3.241

環境變數

docker-compose.yaml 檔案中,web 服務的環境變數部分定義了 REDIS_HOST=redis。這個變數被注入到執行中的容器中,用於建立與 Redis 的連線字串。

# docker-compose exec web env

結果顯示 REDIS_HOST 環境變數已正確注入到容器中。

從 Docker 遷移到 Podman:與 systemd 和 Kubernetes 互動

在前面的章節中,我們學習瞭如何初始化和管理容器,從簡單的概念到進階的操作。容器技術在最新的 Linux 作業系統版本中代表著應用程式開發的關鍵技術。因此,對於進階開發者和系統管理員來說,容器只是起點。一旦企業或技術專案廣泛採用這項技術,下一步就是將其與基礎作業系統和系統協調平台整合。

在本章中,我們將涵蓋以下主要主題:

  • 設定主機作業系統的先決條件
  • 建立 systemd 單元檔案
  • 管理根據容器的 systemd 服務
  • 生成 Kubernetes YAML 資源
  • 在 Podman 中執行 Kubernetes 資源檔案
  • 在 Kubernetes 中測試結果

使用 Docker Compose 與 Podman

使用 podman-compose

podman-compose 專案在 Podman 3.0 版本之前就已經開始,為需要使用 Compose 檔案來協調容器的使用者提供相容性層。在本小節中,我們將在 Fedora 上使用 podman-compose 的範例進行探討。

podman-compose 工具的 CLI 是用 Python 編寫的。該套件可以透過 dnf 安裝,或者從 GitHub 儲存函式庫取得最新版本:

$ sudo dnf install -y podman-compose

或者,可以使用 Python 的套件管理器 pip3 進行安裝,支援更廣泛的作業系統和發行版:

$ pip3 install podman-compose

現在,我們可以使用 podman-compose 執行與之前範例相同的 Compose 堆積疊,並且享有 Podman 提供的無根(rootless)方法。

以下是所有與 docker-compose 相容的可用命令,以及它們的描述和一些由 podman-compose help 命令輸出的次要變更:

  • help:顯示工具的幫助資訊
  • version:顯示命令的版本
  • pull:提取堆積疊映像
  • push:推播堆積疊映像
  • build:建立堆積疊映像
  • up:建立並啟動整個堆積疊或部分服務
  • down:拆除整個堆積疊
  • ps:顯示執行中的容器狀態
  • run:建立一個類別似服務的容器來執行一次性命令
  • exec:在執行中的容器中執行特定命令
  • start:啟動特定服務
  • stop:停止特定服務
  • restart:重新啟動特定服務
  • logs:顯示服務的日誌

內容解密:

  1. podman-compose up 命令的作用:該命令會根據 docker-compose.yaml 檔案建立並啟動整個堆積疊或指定的服務。它會讀取設定檔中的服務定義,並根據這些定義建立和啟動容器。
  2. podman-compose down 命令的作用:該命令會停止並刪除由 podman-compose up 命令建立的容器和相關資源,但不會刪除資料卷(volumes)。如果需要刪除資料卷,需要額外指定 -v 選項。
  3. podman-compose 的優勢:提供了一個與 docker-compose 相容的工具,使得使用者可以無縫地從 Docker 切換到 Podman,並且支援無根容器。

進一步閱讀

要了解更多關於本章涵蓋的主題,請參考以下資源:

  • Docker Awesome Compose:https://github.com/docker/awesome-compose
  • GitHub 上的 Podman-compose 專案:https://github.com/containers/podman-compose
  • Red Hat 部落格介紹 Podman 中的 Docker Compose 支援:https://www.redhat.com/sysadmin/podman-docker-compose
  • Twelve-Factor App:https://12factor.net/
  • Podman 手冊頁面:https://github.com/containers/podman/blob/main/docs/source/markdown/podman.1.md
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Podman 整合 Docker Compose 技術

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

此圖示說明瞭使用 podman-compose 建立和刪除堆積疊的基本流程。

  1. 流程起始:流程從 podman-compose up 命令開始,該命令負責根據設定檔建立並啟動整個堆積疊或指定的服務。
  2. 建立並啟動堆積疊:一旦執行 podman-compose up,系統會根據 docker-compose.yaml 檔案中的定義建立並啟動容器。
  3. 停止並刪除堆積疊:當需要停止並刪除堆積疊時,使用者可以執行 podman-compose down 命令,該命令會停止並刪除先前建立的容器及其相關資源。

這個流程展示瞭如何使用 podman-compose 管理容器的生命週期,從建立到刪除,確保了操作的便捷性和一致性。