昨天我們深入了解了 Helm 這個 Kubernetes 的套件管理器。從實戰中體驗到 Repository 生態系統可以直接拉取別人打包好的 Chart,雖然沒用自己實作打包成 Helm Chart,但是大方向的概念是一樣的。透過模板化和 values.yaml
解決了多環境配置管理的難題。我們成功部署了 WordPress,見證了原本需要多個 YAML 檔案的複雜應用,現在只需要一個 helm install
指令就能完成。
然而,Helm 的 Go Template 語法有一定的學習門檻,而且在某些場景下,我們可能只是需要對現有的 YAML 檔案進行小幅調整,而不需要完整的模板化功能。今天要看到的 Kustomize 正是為了這類需求而生。它採用完全不同的思路,透過「疊加」(Overlay)和「修補」(Patch)的方式來管理配置,而且已經被整合進 kubectl
成為 Kubernetes 的原生功能。
讓我們從一個實際的痛點開始。假設我們有一個 nginx deployment,需要在 Dev、Stage、Prod 三個環境中部署。開發環境在本地機器只需要 1 個副本,測試環境需要 2-3 個,生產環境則需要 5-10 個來處理大量流量。
最直覺的解決方案是為每個環境建立獨立的目錄,將 YAML 檔案複製三份,然後分別修改 replicas 的值。這看起來沒什麼問題,但當應用程式成長,需要管理的資源從一個 deployment 變成數十個檔案時,問題就來了。每次要新增 Service 或 ConfigMap,都要記得複製到所有環境的目錄;每次要修改配置,都要在三個地方同步更新。遲早會有人忘記同步某個檔案,造成環境間的配置不一致。
這種重複性的配置管理不僅容易出錯,也違反了程式設計的 DRY(Don't Repeat Yourself)
原則。因此用一種方法來重複使用 Kubernetes 配置,只修改需要改變的部分,而不是複製整份配置。
Kustomize
引入了兩個核心概念來解決這個問題:Base 和 Overlays。
Base 代表所有環境共用的基礎配置。這些是不會因環境而改變的資源定義,或是可以作為預設值的配置。比如說,你的 nginx deployment 的基本結構、使用的映像檔、健康檢查設定等,在所有環境都是一樣的,這些就放在 Base 中。
Overlays 則是針對特定環境的客製化配置。每個環境都有自己的 Overlay,用來覆蓋或修改 Base 中的特定值。Dev 環境的 Overlay 可能不需要任何修改(如果 Base 的預設值剛好符合),Stage 環境的 Overlay 將 replicas 改為 2,Prod 環境的 Overlay 則改為 5。
Kustomize
的工作流程很簡單:它會將 Base 配置和特定環境的 Overlay 結合,產生最終的 Kubernetes manifest。整個過程不需要任何模板語法,所有檔案都是標準的 YAML,可以直接被 Kubernetes 和其他工具讀取、驗證。
在深入 Kustomize 之前,讓我們快速和昨天剛看完的 Helm 比較一下他們的差異。
Helm 使用 Go Template 語法來處理變數。我們會在 YAML 中看到 {{ .Values.replicaCount }}
這樣的模板語法,然後在 values.yaml
中定義實際的值。這種方式功能強大,支援條件判斷、迴圈、函式等進階功能,但也讓 YAML 檔案變得不再是純粹的 YAML,有時候會變得難以閱讀。
Kustomize
則保持簡單。它不使用任何模板語法,所有檔案都是標準的 YAML。我們直接編寫完整的 Kubernetes 資源定義,然後透過 Overlay 和 Patch 來修改特定的值。這種方式雖然功能較少,但學習門檻低,而且所有檔案都可以直接被 Kubernetes 工具處理。
選擇 Helm 還是 Kustomize,取決於實際專案的需求。如果是需要完整的套件管理功能、複雜的邏輯處理,Helm 是更好的選擇。但如果只是需要管理多環境配置,希望保持簡單直觀,Kustomize 可能會是更合適的選擇。
Kustomize 的核心是 kustomization.yaml
檔案。這個檔案必須準確命名(不能是 kustomization.yml
或其他變體),Kustomize 會在指定目錄中尋找這個檔案。
kustomization.yaml
包含兩個主要部分:
第一部分是 resources,列出所有要被 Kustomize 管理的 Kubernetes 資源檔案。這就像是告訴 Kustomize:「這些是我要部署的東西」。
第二部分是 transformations(轉換),定義要對這些資源進行的修改。比如添加共同的標籤、修改名稱前綴、調整資源限制等。
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- nginx-deployment.yaml
- nginx-service.yaml
commonLabels:
company: IThome
這個簡單的例子展示了 Kustomize 的基本結構。它會載入兩個資源檔案,然後為所有資源添加一個 company: IThome
的標籤。
隨著應用程式成長,我們可能會將不同的元件組織到不同的目錄中。比如 API 相關的配置放在 api/
目錄,資料庫配置放在 database/
目錄,快取配置放在 cache/
目錄。
傳統上,我們需要進入每個目錄分別執行 kubectl apply
,或是使用多個 -f
參數。但 Kustomize 提供了更方便的解決方案。
我們可以在根目錄建立一個 kustomization.yaml
,列出所有子目錄的資源。但更好的做法是在每個子目錄也建立自己的 kustomization.yaml
,管理該目錄內的資源,然後在根目錄的 kustomization.yaml
中引用這些子目錄:
# 根目錄的 kustomization.yaml
resources:
- api
- database
- cache
# 子目錄 db/kustomization.yaml (子目錄結構都會類似)
resources:
- db-depl.yaml
- db-svc.yaml
當 Kustomize 看到資源指向一個目錄而非檔案時,它會進入該目錄尋找 kustomization.yaml
,並遞迴地處理所有資源。這種階層式的組織方式讓大型專案的配置管理變得更有條理。
執行 kustomize build
指令時,Kustomize 會處理所有資源和轉換,然後將最終的 YAML 輸出到終端。注意,這個指令只是顯示結果,並不會實際部署到 Kubernetes。
要實際部署,你需要將輸出導向 kubectl apply
:
kustomize build k8s | kubectl apply -f -
或者使用 kubectl 的內建支援(注意是 -k
而非 -f
):
kubectl apply -k k8s
這兩個指令效果相同,都會將 Kustomize 處理後的配置部署到 Kubernetes。刪除資源時,只要將 apply
改為 delete
即可。
假設我目前有一個專案,包含了資料庫、訊息佇列和網頁伺服器等多個 Components,結構如下圖。總共有八個不同的資源檔案分散在三個 subdirectories 中。如果按照傳統方式,我們需要進入每個目錄分別執行 kubectl apply
,或是用多個 -f
參數一次指定所有檔案,這不僅麻煩,也容易遺漏。
首先,我們在根目錄建立 kustomization.yaml
,將所有資源檔案的路徑都列出來:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- db/db-config.yaml
- db/db-depl.yaml
- db/db-service.yaml
- message-broker/rabbitmq-config.yaml
- message-broker/rabbitmq-depl.yaml
- message-broker/rabbitmq-service.yaml
- nginx/nginx-depl.yaml
- nginx/nginx-service.yaml
現在只需要一個指令就能將所有服務啟動:
kubectl apply -k k8s/
可以看到所有資源都被成功建立:
雖然上面的方法可行,但當資源檔案越來越多時,根目錄的 kustomization.yaml
會變得太多難以維護。更好的做法是採用階層式管理:在每個 subdirectories 建立自己的 kustomization.yaml
,然後在根目錄引用這些 subdirectories。
# 根目錄的 kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- db/
- message-broker/
- nginx/
每個子目錄都有自己的 kustomization.yaml
:
# db/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- db-config.yaml
- db-depl.yaml
- db-service.yaml
# message-broker/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rabbitmq-config.yaml
- rabbitmq-depl.yaml
- rabbitmq-service.yaml
# nginx/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- nginx-depl.yaml
- nginx-service.yaml
這種結構化的方式讓每個元件的配置都保持在自己的目錄中,維護起來更加清晰:
執行結果與方法一相同,所有資源都成功部署:
階層式管理的好處是當專案規模擴大時,你可以輕鬆地新增或移除整個元件,只需要在根目錄的 kustomization.yaml
中調整子目錄列表即可,不需要逐一管理個別檔案。
今天我們學習了 Kustomize 的基礎概念和實際應用。從理解為什麼需要 Kustomize 開始,我們看到了它如何用 Base 和 Overlays 的概念優雅地解決多環境配置管理的問題,不需要複製大量的 YAML 檔案,也不需要學習複雜的模板語法。
透過實戰,我們掌握了 kustomization.yaml
的基本結構,學會了如何管理分散在多個目錄中的資源檔案。特別是階層式的組織方式,讓大型專案的配置管理變得更有條理。相較於 Helm 的模板化方式,Kustomize 保持了所有檔案都是標準 YAML 的簡潔性,這讓配置更容易理解和除錯。
然而,今天我們只接觸了 Kustomize 的基礎。真正讓 Kustomize 強大的是它的 Transformers 和 Patches 功能。明天我們將深入探討如何使用 Common Transformers 來統一修改資源屬性、用 Image Transformers 管理不同環境的映像檔版本、透過 Strategic Merge Patch 和 JSON Patch 精確地修改配置,以及如何使用 Overlays 和 Components 建立可重複使用的配置片段。