在容器化應用程式佈署的過程中,映象構建是不可或缺的一環。Dockerfile 的線性構建流程在簡單場景下固然便捷,但面對複雜的客製化需求時,其侷限性便顯露無疑。Buildah 作為新一代容器映象構建工具,以其高度的靈活性和可控性,為開發者提供了更強大的武器。它允許我們以程式化的方式,精細地控制映象的每一層,實作更複雜的構建邏輯。不同於 Dockerfile 的黑盒操作,Buildah 將映象構建過程透明化,使每一層的變更都清晰可見,極大地簡化了除錯流程。此外,Buildah 與 Podman 的無縫整合,更使其成為容器生態系統中的一把利器,讓開發者得以在同一工具鏈中完成映象構建和容器管理。
容器映象構建新思路:為何我擁抱 Buildah?
在容器化技術的浪潮中,映象構建是至關重要的一環。Podman 作為 Docker 的有力替代者,在簡化容器管理方面表現出色。但當面對更複雜的構建需求時,我發現 Buildah 才是真正的利器。
Dockerfile 的侷限性:我的痛點
傳統的 Dockerfile 方式雖然易於上手,但其線性、指令式的流程在某些場景下顯得捉襟見肘。例如:
- 靈活性不足:Dockerfile 的指令集相對固定,難以滿足客製化的構建需求。
- 除錯困難:Dockerfile 的每一步都會產生新的映象層,中間層的錯誤難以追蹤。
- 無法程式化控制:Dockerfile 本身無法嵌入程式碼邏輯,難以實作複雜的構建流程。
這些痛點促使我開始尋找更靈活、更可控的映象構建方案。
Buildah:容器構建的瑞士軍刀
Buildah 是一個專注於構建容器映象的工具,它與 Podman 出自同一團隊之手,可以看作是 Podman 在映象構建方面的補充。Buildah 提供了更底層、更靈活的 API,讓開發者能夠以程式化的方式構建映象。
Buildah 的核心概念:可程式設計的構建流程
Buildah 的核心思想是將映象構建過程分解為一系列可程式設計的操作。你可以像操作 Git 倉函式庫一樣,對映象層進行增加、修改、提交等操作。
Buildah 的優勢:我為何選擇它?
- 靈活性:Buildah 允許你使用 Shell 指令碼、Go 程式碼等方式編寫構建邏輯,充分滿足客製化需求。
- 可控性:Buildah 提供了更細粒度的控制,你可以精確控制每一層的內容。
- 可除錯性:Buildah 的每一層都清晰可見,方便你追蹤和除錯構建過程中的問題。
- 與 Podman 無縫整合:Buildah 構建的映象可以直接被 Podman 使用,無需額外轉換。
實戰演練:使用 Buildah 構建 HTTPD 映象
接下來,我將展示如何使用 Buildah 構建一個簡單的 HTTPD 映象。
1. 建立工作目錄
首先,建立一個工作目錄,用於存放構建所需的檔案:
mkdir myhttpd
cd myhttpd
2. 準備 HTTPD 設定檔
建立 index.html 檔案,作為 HTTPD 的首頁內容:
<!DOCTYPE html>
<html>
<head>
<title>玄貓的 Buildah 示例</title>
</head>
<body>
<h1>歡迎來到玄貓的 Buildah 示例!</h1>
</body>
</html>
3. 編寫 entrypoint.sh 指令碼
建立 entrypoint.sh 指令碼,用於啟動 HTTPD 服務:
#!/bin/sh
set -euo pipefail
if [ $UID != 0 ]; then
echo "Running as user $UID"
fi
if [ "$1" == "httpd" ]; then
echo "Starting custom httpd server"
exec $1 -DFOREGROUND
else
echo "Starting container with custom arguments"
exec "$@"
fi
這個指令碼會檢查當前使用者是否為 root,並根據傳入的引數啟動 HTTPD 服務。
4. 編寫 Dockerfile
FROM fedora:latest
# 安裝 httpd 並清理快取
RUN dnf install -y httpd && dnf clean all
# 設定 httpd 日誌輸出到 stdout 和 stderr
RUN sed -i 's|ErrorLog "logs/error_log"|ErrorLog /dev/stderr|' /etc/httpd/conf/httpd.conf && \
sed -i 's|CustomLog "logs/access_log" combined|CustomLog /dev/stdout combined|' /etc/httpd/conf/httpd.conf && \
chown 1001 /var/run/httpd
# 複製網頁內容
COPY index.html /var/www/html
# 定義內容卷
VOLUME /var/www/html
# 複製容器啟動指令碼
COPY entrypoint.sh /entrypoint.sh
# 宣告暴露的連線埠
EXPOSE 8080
# 宣告預設使用者
USER 1001
ENTRYPOINT ["/entrypoint.sh"]
CMD ["httpd"]
5. 使用 Podman 構建映象
podman build -t myhttpd .
6. 檢視映象層
使用 podman inspect 命令檢視映象層:
podman inspect myhttpd --format '{{ .RootFS.Layers }}'
7. 壓縮映象層
使用 --layers=false 選項可以將所有映象層壓縮為一層:
podman build -t myhttpd --layers=false .
Buildah 的更多可能性:我的探索之路
除了基本的映象構建,Buildah 還提供了更多高階功能,例如:
- 從現有容器建立映象:你可以根據一個正在執行的容器建立映象,方便你將容器的狀態儲存下來。
- 直接操作映象層:你可以使用
buildah mount命令將映象層掛載到本地檔案系統,直接修改其內容。 - 多階段構建:你可以使用 Buildah 實作多階段構建,將構建過程中的中間產物分離開來,減小最終映象的體積。
為何我擁抱 Buildah:從零開始開發容器的藝術
在容器技術日新月異的今天,我們有幸擁抱諸多強大的工具。其中,Buildah 以其獨特的靈活性和底層控制力,吸引了我的目光。它不僅僅是一個容器構建工具,更像是一個容器組裝工廠,讓我們可以精雕細琢每一個細節。
Buildah 的核心:容器與映象的生命週期
Buildah 的設計哲學圍繞著容器映象的生命週期管理。它巧妙地利用了 containers/image 專案來處理映象與 registry 的互動,同時藉助 containers/storage 專案來管理映象和容器的檔案系統層。這種分工合作的方式,使得 Buildah 在構建容器時更加高效和可靠。
突破傳統:Buildah 的進階構建策略
Buildah 最吸引我的地方,在於它對傳統 Dockerfile/Containerfile 構建方式的平行支援,以及根據原生 Buildah 命令驅動的構建方式。這種進階的構建策略,為我們開啟了無限可能。
透過將 Dockerfile 指令轉化為標準命令,Buildah 成為了一個可指令碼化的工具。我們可以將其與自定義邏輯和原生 shell 結構(如條件陳述式、迴圈或環境變數)結合使用。例如,Dockerfile 中的 RUN 指令,可以用 buildah run 命令來替代。
這種靈活性在應對複雜的構建需求時尤為重要。例如,在自動化構建流程中,我們可以根據不同的環境變數,動態地調整構建引數,實作高度定製化的容器映象。
保留傳統:Dockerfile 的構建方式
當然,如果團隊需要保留之前 Dockerfile 中實作的構建邏輯,Buildah 也提供了 buildah build(或其別名 buildah bud)命令,可以從 Dockerfile/Containerfile 中讀取指令並構建映象。
安全至上:無 root 模式執行
從安全形度來看,Buildah 另一個備受歡迎的功能是它可以在無 root 模式下流暢執行以構建映象。無需 Unix socket 即可執行構建。正如本章開頭所解釋的,構建始終根據容器;Buildah 也不例外,並且所有構建都在工作容器內執行,從基礎映象之上開始。
Buildah 常用指令概覽
以下是一些 Buildah 中最常用的指令,它們為我們提供了構建容器的強大能力:
buildah from:根據一個基礎映象初始化一個新的工作容器。例如:$ buildah from fedora。buildah run:相當於 Dockerfile 的RUN指令,在工作容器內部執行命令。例如:buildah run <containerID> -- dnf install -y nginx。buildah config:組態映象的中繼資料。這個命令的可用選項與 Dockerfile 中那些不修改檔案系統層,但設定容器中繼資料的指令相關聯。例如:buildah config --entrypoint /entrypoint.sh <containerID>。buildah add:相當於 Dockerfile 的ADD指令,向容器中增加檔案、目錄,甚至是 URL。例如:buildah add <containerID> index.php /var/www.html。buildah copy:與 Dockerfile 的COPY指令相同,向容器中增加檔案、URL 和目錄。例如:buildah copy <containerID> entrypoint.sh /。buildah commit:從一個工作容器提交一個最終映象。這個命令通常是最後執行的命令。例如:buildah commit <containerID> <myhttpd>。buildah build:相當於經典的 Podman build。這個命令接受 Dockerfile 或 Containerfile 作為引數,以及構建目錄路徑。例如:buildah build -t <imageName> .。buildah containers:列出參與 Buildah 構建的活動工作容器,以及用作起點的基礎映象。等效的命令是buildah ls和buildah ps。例如:buildah containers。buildah rm:用於移除工作容器。buildah delete命令是等效的。例如:buildah rm <containerID>。buildah mount:可以用於掛載工作容器的根檔案系統。例如:buildah mount <containerID>。buildah images:列出本地主機快取中的所有可用映象。例如:buildah images --json。
玄貓認為,Buildah 的這些指令為我們提供了構建容器的完整工具集。無論是從零開始構建,還是根據現有的 Dockerfile,Buildah 都能夠勝任。
玄貓帶你認識 Buildah:從零開始開發容器
Buildah 是 Podman 的好夥伴,它能讓你從頭開始建構容器。以下將介紹 Buildah 的常用指令:
buildah tag: 為本地儲存的映像檔加上自訂名稱和標籤。語法為buildah tag <name> <new-name>。例如:buildah tag myapp quay.io/packt/myapp:latest。buildah push: 將本地映像檔推播到遠端私有或公開的 registry,或是 Docker 或 OCI 格式的本地目錄。相較於 Podman 或 Docker 的同等指令,這個指令提供更大的彈性。語法為buildah push [options] <image> [destination]。例如:buildah push quay.io/packt/myapp:latest、buildah push <imageID> docker://<URL>/repository:tag和buildah push <imageID> oci:</path/to/dir>:image:tag。buildah pull: 從 registry、OCI 封存檔或目錄中提取映像檔。語法為buildah pull [options] <image>。例如:buildah pull <imageName>、buildah pull docker://<URL>/repository:tag和buildah pull dir:</path/to/dir>。
想了解更多指令細節嗎?只要在終端機輸入 man buildah-<command> 就能找到對應的說明檔案。例如,想知道 buildah run 的詳細資訊,輸入 man buildah-run 即可。
以下範例展示 Buildah 的基本功能。我們將以 Fedora 為基礎映像檔,客製化成能執行 httpd 程式的容器:
$ container=$(buildah from fedora)
$ buildah run $container -- dnf install -y httpd; dnf clean all
$ buildah config --cmd "httpd -DFOREGROUND" $container
$ buildah config --port 80 $container
$ buildah commit $container myhttpd
$ buildah tag myhttpd registry.example.com/myhttpd:v0.0.1
上述指令將產生一個符合 OCI 標準、可移植的映像檔,功能與從 Dockerfile 建構的映像檔相同,而與只需幾行程式碼,就能包含在簡單的指令碼中。
現在,讓我們深入瞭解第一個指令:
$ container=$(buildah from fedora)
buildah from 指令會從允許的 registry 中提取 Fedora 映像檔,並從中啟動一個工作容器,然後傳回容器名稱。為了方便後續使用,我們將使用 shell 擴充套件語法將容器名稱儲存在 $container 變數中。如此一來,我們就可以將 $container 變數傳遞給後續指令,讓 build 指令在這個工作容器內執行。這是一個常見的模式,特別適合用於自動化指令碼中的 Buildah 指令。
重要提示
Buildah 和 Podman 在容器概念上存在細微差異。雖然兩者都採用相同的技術來建立容器,但 Buildah 容器是短暫的實體,專為修改和提交而建立,而 Podman 容器則用於執行長時間運作的工作負載。
Buildah 的彈性和可嵌入性非常出色。Buildah 指令可以包含在任何地方,使用者可以選擇全自動化的建構流程,或是更具互動性的流程。
例如,Buildah 可以輕鬆與 Ansible 整合,透過原生連線外掛程式與工作容器通訊,提供自動化的建構。
你也可以將 Buildah 納入 CI 流程(例如 Jenkins、Tekton 或 GitLab CI/CD),以完全掌控建構和整合任務。
Buildah 也包含在雲原生社群的較大型專案中,例如 Shipwright 專案 (https://github.com/shipwright-io/build)。
Shipwright 是一個適用於 Kubernetes 的可擴充套件建構框架,提供使用自訂資源定義和不同建構工具來自訂映像檔建構的彈性。Buildah 是你在設計建構流程時可以選擇的解決方案之一。
接下來將會看到更詳細與豐富的範例。現在我們已經瞭解 Buildah 的功能和使用案例,讓我們開始安裝和準備環境。
環境準備
Buildah 可在不同的發行版上使用,並可使用各自的套件管理器進行安裝。本文提供主要發行版上的一些安裝範例。為了清楚起見,本章的實驗室環境都是根據 Fedora 34:
-
Fedora: 若要在 Fedora 上安裝 Buildah,請執行以下
dnf指令:$ sudo dnf -y install buildah -
Debian: 若要在 Debian Bullseye 或更新版本上安裝 Buildah,請執行以下
apt-get指令:$ sudo apt-get update $ sudo apt-get -y install buildah -
CentOS: 若要在 CentOS 上安裝 Buildah,請執行以下
yum指令:$ sudo yum install -y buildah -
RHEL8: 若要在 RHEL8 上安裝 Buildah,請執行以下
yum module指令:$ sudo yum module enable -y container-tools:1.0 $ sudo yum module install -y buildah -
RHEL7: 若要在 RHEL7 上安裝 Buildah,請啟用
rhel-7-server-extras-rpms儲存函式庫,然後使用yum安裝:$ sudo subscription-manager repos --enable=rhel-7-server-extras-rpms $ sudo yum -y install buildah -
Arch Linux: 若要在 Arch Linux 上安裝 Buildah,請執行以下
pacman指令:$ sudo pacman –S buildah -
Ubuntu: 若要在 Ubuntu 20.10 或更新版本上安裝 Buildah,請執行以下
apt-get指令:$ sudo apt-get -y update $ sudo apt-get -y install buildah -
Gentoo: 若要在 Gentoo 上安裝 Buildah,請執行以下
emerge指令:$ sudo emerge app-emulation/libpod -
從原始碼建構: Buildah 也可以從原始碼建構。本章將重點放在簡單的佈署方法上,但如果你有興趣,可以參考以下來嘗試自己的建構:
https://github.com/containers/buildah/blob/main/install.md#building-from-scratch
最後,Buildah 可以佈署為容器,並可以使用巢狀方法在其中執行建構。這個流程將在第 7 章「與現有應用程式建構流程整合」中詳細介紹。
在將 Buildah 安裝到主機後,我們可以繼續驗證安裝。
驗證安裝
安裝 Buildah 後,我們現在可以執行一些基本測試指令來驗證安裝。
若要檢視主機本地儲存中的所有可用映像檔,請使用以下指令:
$ buildah images
# buildah images
映像檔清單會與 podman images 指令輸出的清單相同,因為它們共用相同的本地儲存。
另請注意,這兩個指令分別以非特權使用者和 root 身分執行,分別指向使用者 rootless 本地儲存和系統範圍的本地儲存。
我們可以執行一個簡單的測試建構來驗證安裝。這是一個測試基本建構指令碼的好機會,其唯一目的是驗證 Buildah 是否能夠完全執行完整的建構。
為了本章的目的(以及樂趣),玄貓建立了一個簡單的測試指令碼,用於建立最小的 Python 3 映像檔:
#!/bin/bash
BASE_IMAGE=alpine
TARGET_IMAGE=python3-minimal
玄貓認為,Buildah 作為一個強大的容器建構工具,提供了極大的彈性與控制權。無論是自動化建構流程還是互動式開發,Buildah 都能滿足你的需求。透過與 Ansible 等工具的整合,更能將容器建構融入到現有的 CI/CD 流程中,實作更高效的軟體交付。