理解容器映像檔的建置流程對於容器技術的應用至關重要。本文從 Podman 的基本用法出發,逐步介紹 Dockerfile 的語法和指令,並探討 Buildah 的進階功能,讓讀者能更有效率地建置和管理容器映像檔。 Buildah 提供了更細緻的操作,讓開發者可以控制映像檔的每個層級,並透過指令碼自動化建置流程,提升開發效率。相較於 Dockerfile 的宣告式方法,Buildah 更具彈性,適合處理複雜的建置場景。
使用 Buildah 從零開始建立容器
容器的最大魅力在於能夠將應用程式封裝到不可變的映像檔中,並在不同系統上無縫執行。本章將探討如何使用不同技術和工具建立映像檔,包括瞭解映像檔建立的底層原理以及如何從零開始建立映像檔。
技術需求
在開始本章之前,需要一台安裝了 Podman 的機器。書中的範例均在 Fedora 34 或更高版本的系統上執行,但讀者可以在自己選擇的作業系統上重現這些範例。
此外,對第 4 章「管理執行中的容器」中涵蓋的主題有基本的瞭解,將有助於輕鬆掌握 Open Container Initiative(OCI)映像檔的概念。
使用 Podman 進行基本映像檔建立
容器的 OCI 映像檔是一組不可變的層,按照複製時寫入(copy-on-write)的邏輯堆積疊在一起。當建立映像檔時,所有層都會按照特定的順序建立,然後推播到容器註冊中心,註冊中心將我們的層以 tar 為基礎的壓縮檔形式儲存,同時儲存額外的映像檔後設資料。
底層構建原理
容器映像檔可以透過不同的方式建立,但最常見的方法可能是根據 Dockerfile 的方法。Dockerfile 是一個純文字檔案,列出了在建立過程中要執行的動作。
隨著時間的推移,Dockerfile 成為了 OCI 映像檔建立的標準,並在許多使用案例中被採用。
重要注意事項 為了標準化和消除與品牌的關聯,還引入了 Containerfile。Containerfile 與 Dockerfile 具有相同的語法,並且原生支援 Podman。在本文中,我們將交替使用 Dockerfile 和 Containerfile 這兩個術語。
準備環境
在使用 Buildah 建立映像檔之前,我們需要準備好我們的環境。這包括安裝必要的工具和組態工作目錄。
選擇構建策略
在建立映像檔時,我們有多種策略可供選擇。我們可以從零開始建立映像檔,也可以根據現有的映像檔進行修改和擴充套件。
從零開始建立映像檔
從零開始建立映像檔需要對 OCI 映像檔的結構和內容有深入的瞭解。我們將學習如何使用 Buildah 從頭開始建立一個新的映像檔。
從 Dockerfile 建立映像檔
除了從零開始建立映像檔外,我們還可以使用 Dockerfile 或 Containerfile 來建立映像檔。這種方法更為常見,也更方便。本章將介紹如何使用 Dockerfile 建立映像檔。
使用 Buildah 建立容器映像檔
Buildah 是 Podman 的配套工具,專門用於建立容器映像檔。使用 Buildah,我們可以更靈活、更高效地建立和管理容器映像檔。
基本概念
在使用 Buildah 之前,我們需要了解一些基本概念,例如 OCI 映像檔的結構、層的概念等。
安裝與組態 Buildah
在使用 Buildah 之前,我們需要在系統上安裝並組態好 Buildah。這包括安裝 Buildah 軟體包和組態相關的環境變數。
# 在 Fedora 系統上安裝 Buildah
sudo dnf install -y buildah
詳細解說:
sudo dnf install -y buildah:這條命令用於在 Fedora 系統上安裝 Buildah。其中,sudo表示以管理員許可權執行命令,dnf install是用於安裝軟體包的命令,-y表示自動確認安裝,buildah是要安裝的軟體包名稱。
使用 Buildah 建立映像檔
一旦安裝並組態好 Buildah,我們就可以開始使用它來建立容器映像檔了。本章將介紹如何使用 Buildah 從零開始建立一個新的映像檔,以及如何使用 Dockerfile 或 Containerfile 建立映像檔。
# 使用 Buildah 從零開始建立一個新的容器映像檔
buildah from scratch
詳細解說:
buildah from scratch:這條命令用於從零開始建立一個新的容器映像檔。其中,buildah from是用於建立新映像檔的命令,scratch表示從零開始。
進一步閱讀
有關更多資訊,請參閱以下資源:
- Containers Storage 專案頁面:https://github.com/containers/storage
- Container Labeling:https://danwalsh.livejournal.com/81269.html
- 為什麼應該為 Linux 容器使用多類別安全:https://www.redhat.com/en/blog/why-you-should-be-using-multi-category-security-your-linux-containers
- Udica:生成 SELinux 策略:https://github.com/containers/udica
- Overlay 原始碼:https://github.com/containers/storage/blob/main/drivers/overlay/overlay.go
使用 Podman 進行基礎映像檔建置
在探討 Dockerfile 的語法之前,我們先來瞭解 Dockerfile 的基本概念。Dockerfile 是一系列建置指令,建置工具會依序執行這些指令。以下是一個簡單的範例:
FROM docker.io/library/fedora
RUN dnf install -y httpd && dnf clean all -y
COPY index.html /var/www/html
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
內容解密:
- FROM 指令:定義了基礎映像檔,本例中使用的是 Fedora。
- RUN 指令:在建置過程中執行特定動作,本例中是安裝
httpd軟體包並清除快取。 - COPY 指令:將建置工作目錄中的檔案或目錄複製到映像檔中,本例中是將
index.html複製到/var/www/html。 - CMD 指令:定義容器啟動時執行的命令,本例中是啟動
httpd服務。
當執行 RUN 和 COPY 指令時,新的變更層會被快取在中間層中,這些中間層由暫時容器表示。這是 Docker 的原生功能,當沒有變更時,可以重複使用快取層,提高建置效率。
映像檔層與快取機制
並非所有的 Dockerfile 指令都會改變檔案系統,只有像 RUN、COPY 和 ADD 這樣的指令會建立新的映像檔層。其他指令,如 CMD,只會建立包含元資料的空層,不會影響最終的映像檔檔案系統。
為什麼要限制層數?
過多的層會影響圖形驅動程式的效能,因此建議減少 RUN、COPY 和 ADD 指令的使用。
檢查映像檔歷史
可以使用 podman inspect 命令檢查映像檔的歷史和每個層的操作。以下是一個範例輸出:
$ podman inspect myhttpd
[...省略輸出]
"History": [
{
"created": "2021-04-01T17:59:37.09884046Z",
"created_by": "/bin/sh -c #(nop) LABEL maintainer=Clement Verna <[email protected]>",
"empty_layer": true
},
{
"created": "2021-10-24T21:27:18.783034844Z",
"created_by": "/bin/sh -c dnf install -y httpd && dnf clean all -y",
"comment": "FROM docker.io/library/fedora:latest"
},
{
"created": "2021-10-24T21:27:21.095937071Z",
"created_by": "/bin/sh -c #(nop) COPY file:78c6e1dcd6f819581b54094fd38a3fd8f170a2cb768101e533c964e04aacab2e in /var/www/html"
},
{
"created": "2021-10-24T21:27:21.182063974Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/usr/sbin/httpd\", \"-DFOREGROUND\"]",
"empty_layer": true
}
]
[...省略輸出]
內容解密:
- 歷史記錄:顯示了映像檔建置過程中的每一步操作,包括時間戳記和執行的命令。
- 層的建立:可以看到
RUN和COPY指令建立了新的層,而CMD指令只更新了元資料。
Dockerfile 和 Containerfile 指令
Dockerfile 和 Containerfile 共用相同的語法。以下是一些常見的指令:
- FROM:定義基礎映像檔。
- RUN:在暫時容器中執行命令。
- COPY:將檔案或目錄複製到映像檔中。
- ADD:將檔案、目錄或遠端 URL 複製到映像檔中,並支援自動解壓縮 tar 檔案。
- ENTRYPOINT:定義容器啟動時執行的命令。
這些指令遵循相同的模式:INSTRUCTION arguments。
最佳實踐
為了避免建立過多的層,建議將多個命令合併到一個 RUN 指令中,例如:
RUN dnf upgrade -y && \
dnf install httpd -y && \
dnf clean all -y
這種做法可以減少最終映像檔的層數,提高效能。
使用Podman進行基本映像檔建置
在深入瞭解如何使用Podman進行容器映像檔建置之前,我們需要先熟悉Dockerfile中的各種指令。這些指令對於定義映像檔的建置過程至關重要。
Dockerfile指令詳解
- FROM: 指定基礎映像檔。如果本地不存在,Podman會自動從遠端倉函式庫提取。
- RUN: 在映像檔建置過程中執行命令。可以是用於安裝軟體包、執行指令碼等。
- ENTRYPOINT: 組態容器啟動時執行的命令。可以是可執行檔或指令碼。
- CMD: 提供預設引數給ENTRYPOINT指令。可以是完整命令或引數列表。
- LABEL: 為映像檔新增自定義標籤,用於在建置時或執行時作為中繼資料。
- EXPOSE: 宣告容器內部程式監聽的埠。
- ENV: 設定環境變數,在建置過程中和容器執行時都可用。
- VOLUME: 指定容器執行時將建立的卷,用於持久化資料。
- USER: 設定接下來RUN、CMD和ENTRYPOINT指令執行的使用者和群組。
- WORKDIR: 設定建置過程中的工作目錄,該值在容器執行期間保留。
- ONBUILD: 定義觸發命令,在映像檔作為父映像檔被用於新建置時執行。
使用Podman進行映像檔建置
Podman提供了與Docker相同的建置命令和語法,使得從Docker切換到Podman無需額外的學習成本。Podman的優勢在於它可以在無根模式下使用fork/exec模型建置容器。
簡單的Podman建置範例
考慮以下使用httpd Dockerfile的簡單建置範例:
$ podman build -t myhttpd .
建置步驟詳解
FROM指令: 定義基礎映像檔,如果本地不存在則自動提取。
FROM docker.io/library/fedoraRUN指令: 安裝
httpd套件並清理系統。RUN dnf install -y httpd && dnf clean all -y實際執行時,等同於
"bash -c 'dnf install -y httpd && dnf clean all –y'"。COPY指令: 將
index.html檔案複製到預設的httpd檔案根目錄。COPY index.html /var/www/htmlCMD指令: 定義預設容器啟動命令,由於未設定ENTRYPOINT,因此實際執行的命令是
"bash -c '/usr/sbin/httpd -DFOREGROUND'"。CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
自定義Dockerfile範例
以下是一個自定義的Dockerfile,用於建置自定義的Web伺服器:
FROM docker.io/library/fedora
# 安裝必要的套件
RUN set -euo pipefail; \
dnf upgrade -y; \
dnf install httpd -y; \
dnf clean all -y; \
rm -rf /var/cache/dnf/*
# 為無根執行自定義Web伺服器組態
RUN set -euo pipefail; \
sed -i 's|Listen 80|Listen 8080|' /etc/httpd/conf/httpd.conf; \
# 其他自定義組態...
內容解密:
Dockerfile中的指令如何影響映像檔建置?
FROM指令的作用:指定了基礎映像檔,決定了映像檔的初始狀態和層級結構。RUN指令的作用:在映像檔建置過程中執行特定的命令,如安裝軟體或修改組態,從而建立新的層級。CMD和ENTRYPOINT指令的作用:定義了容器啟動時的預設行為,CMD提供了預設引數,而ENTRYPOINT則指定了執行的命令或指令碼。ENV和VOLUME指令的作用:分別用於設定環境變數和持久化儲存卷,增強了容器的可組態性和資料管理能力。
為何選擇Podman進行映像檔建置?
- 無根模式支援:Podman允許在無根模式下建置容器,提高了安全性和靈活性。
- 與Docker相容的語法:Podman與Docker具有相同的建置命令和語法,降低了遷移成本。
如何最佳化Dockerfile?
- 最小化層級數量:透過合併
RUN指令減少層級數量,最佳化映像檔大小和建置效率。 - 利用快取機制:合理安排指令順序,使不常變化的指令靠前,以充分利用快取。
使用 Buildah 從零開始建立容器
基本映像檔建置與 Podman 的使用
在容器化的世界中,建立自定義映像檔是一項基本任務。Podman 提供了一個簡便的方式來建置容器映像檔,並且與 Docker 映像檔格式相容。以下是一個使用 Podman 建置自定義 httpd 伺服器映像檔的範例:
FROM fedora:33
# 安裝 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"]
內容解密:
- 基礎映像檔選擇:使用
fedora:33作為基礎映像檔,確保容器具備基本的作業系統環境。 - httpd 套件安裝:使用
dnf安裝httpd套件,並清理快取以節省映像檔空間。 - 日誌輸出設定:修改
httpd.conf將日誌輸出重新導向到stdout和stderr,便於容器日誌管理。 - 網頁內容複製:將
index.html複製到/var/www/html,作為伺服器的網頁根目錄。 - 卷冊定義:定義
/var/www/html為卷冊,便於持久化儲存網頁內容。 - 進入點指令碼複製:複製
entrypoint.sh到容器中,用於控制容器的啟動行為。 - 埠號暴露:宣告容器服務的埠號為
8080,以便外部存取。 - 預設使用者設定:設定預設使用者為
1001,以提升容器的安全性。
建置與標記映像檔
使用 Podman 建置映像檔:
$ podman build -t myhttpd .
建置完成後,可以檢視映像檔清單:
$ podman images | grep myhttpd
localhost/myhttpd latest 6dc90348520c 2 minutes ago 248 MB
對映像檔進行標記,以便推播到遠端倉函式庫:
$ podman tag localhost/myhttpd quay.io/<username>/myhttpd:v1.0
映像檔層級與壓縮
Podman 建置的映像檔由多個層級組成,可以使用 podman inspect 檢視層級資訊:
$ podman inspect myhttpd --format '{{ .RootFS.Layers }}'
若要減少層級數量,可以使用 --layers=false 選項重新建置映像檔:
$ podman build -t myhttpd --layers=false .
Meet Buildah:Podman 的建置工具
儘管 Podman 在建置 Dockerfiles/Containerfiles 時表現出色,但在更複雜的建置任務中,Dockerfiles/Containerfiles 的限制逐漸顯現。Buildah 的出現彌補了這一不足,提供更靈活的建置策略。
Buildah 是由與 Podman 相同的社群開發的開源專案,提供 OCI 相容的映像檔建置功能。Buildah 的主要優勢在於其靈活性和可程式設計性,允許使用者透過指令碼自定義建置流程。