打包與部署:Helm 與 Kustomize

· tech

#kubernetes#operations

📑 目錄

前面每一篇都在教你寫 YAML,但實務有個逃不掉的痛:同一個 app 要上 Development、Staging、Production,而三個環境九成的 YAML 一模一樣,只有幾個地方不同——副本數、映像 tag、資源大小、對外網址。如果你 copy-paste 三份各自維護,改一個共同欄位就要同步三次,遲早出錯。這篇談兩個解決這件事的主流工具:HelmKustomize,以及它們背後兩種很不一樣的世界觀。

同一個 app 一堆 YAML 九成內容共用 Developmentreplicas:1 · image:app:dev · 資源小 · host:dev.local Stagingreplicas:2 · image:app:rc · host:stg.example.com Productionreplicas:10 · image:app:1.4 · 資源大 · host:example.com
跨環境部署的本質:一份 app,三個環境,九成 YAML 相同、只有幾個值不同。與其複製三份各自維護(改一處要同步三次),不如「一份共用來源 + 每個環境的差異」——Helm 與 Kustomize 就是這件事的兩種做法

兩種哲學:模板填空 vs 疊加補丁

Helm 與 Kustomize 都在解「一份來源 + 每環境差異」,但路數南轅北轍。一個把 YAML 當有變數的模板去填,一個把 YAML 當資料去疊補丁:

Helm:模板填空 template.yamlreplicas:{{ .Values.replicas }} values-dev.yamlvalues-prod.yaml要填的值 helm install / upgrade把變數渲染成具體值 填好的具體 YAML → apply 到叢集 Kustomize:疊加補丁 base/ 純 YAML本身就合法可直接 apply overlays/prod一小塊 patch只寫要改的 kubectl apply -k把 patch 合併進 base 合併後的 YAML → apply 到叢集
同一個目標,兩種世界觀:Helm 把 YAML 當「有變數的模板」,靠 values 填空後渲染;Kustomize 把 YAML 當「資料」,base 本身就是合法可跑的 YAML,overlay 只疊上一小塊要改的 patch。前者有模板語言,後者全程都是純 YAML、沒有變數

Helm:把 K8s app 變成可安裝、可版控、可回滾的套件

Helm 常被叫做「K8s 的套件管理器」,類比 apt / npm 很貼切。它的核心是 chart——一個資料夾,裝著 Chart.yaml(套件資訊)、values.yaml(預設值)、和 templates/(帶變數的 YAML 模板)。部署就三個動作:

helm install web ./mychart -f values-prod.yaml   # 安裝一個 release,套 prod 的值
helm upgrade web ./mychart -f values-prod.yaml    # 改版:同一個 release 升上去
helm rollback web 3                               # 一鍵回滾到第 3 版

它比純 YAML 多給你的,是套件管理器該有的東西:每次 install/upgrade 都是一個有版本號的 release,壞了 helm rollback 秒退;還能宣告相依套件(我的 app 需要一個 Redis)。最爽的是現成生態系——要在叢集裡跑 Redis、Prometheus、cert-manager?helm install 一行,不用自己拼幾百行 YAML。代價是那套 Go 模板語言:{{ }}、縮排、條件與迴圈堆起來,複雜的 chart 可以難讀難 debug(這時 helm template 先把它渲染出來看,是保命技)。

Kustomize:純 YAML,base + overlay 疊出各環境

Kustomize 走另一條路:不要模板、不要變數,一切都是合法的 YAML。 你寫一份 base/(本身就能 kubectl apply 的完整 YAML),再為每個環境寫一個 overlays/<env>/,裡面只放這個環境要改的那一小塊 patch:

# overlays/prod/kustomization.yaml
resources: [../../base]      # 疊在 base 上
replicas:
  - name: web
    count: 10                # Production 改成 10 份,其餘全繼承 base
images:
  - name: app
    newTag: "1.4"            # 換 image tag

它內建在 kubectl 裡(kubectl apply -k overlays/prod),不用裝額外工具,還附一堆好用的 transformer:namePrefixcommonLabelsimages(改 tag)、replicas,以及 configMapGenerator——後者會在 ConfigMap 名字後面接一段內容 hash,內容一改、名字就變,於是 Pod 樣板跟著變、自動觸發滾動更新。這正好順手解掉第三篇那個坑:改了 ConfigMap 不會自動重啟 Pod——用 Kustomize 的 generator,這件事免費就對了。

該用哪個?

不必二選一,但方向很清楚:

情境偏 Helm偏 Kustomize
第三方現成 app(Redis、Prometheus)✓ 一行安裝、有生態系
自己的 app manifests✓ 純 YAML、好讀好 review
要版本化 / 一鍵 rollback / 相依管理✓ release 機制
重度參數化、要發給別人用✓ values 就是參數面板
不想多裝工具、不想學模板語言✓ 內建在 kubectl

實務上很多團隊兩個都用:第三方套件用 Helm 裝,自家 app 用 Kustomize 疊環境;甚至用 Helm 的 post-renderer 再過一手 Kustomize。先看你要解的是「安裝別人的套件」還是「把自己的 YAML 分環境」——這一題基本就決定了你該先拿哪把。

反思

「模板」與「疊加」是兩種心智負擔,選你受得了的那種

Helm 與 Kustomize 的差別,表面是工具,底層是你願意把 YAML 當程式、還是當資料。Helm 把 YAML 變成有變數、有邏輯的模板——強大,但你多背了一層模板語言的認知稅,出錯時得先在腦中「渲染」才知道實際長怎樣。Kustomize 堅持一切都是合法 YAML,你隨時 kubectl apply -k 就能看到最終結果,心智模型乾淨,代價是遇到高度動態的參數化會綁手綁腳。我自己的偏好是能用 Kustomize 就用 Kustomize——純 YAML 的可讀性與可 review 性,在團隊裡的長期價值被嚴重低估;只有當「參數多到像在寫程式」時,我才承認 Helm 的模板是對的工具。

打包是「設定外部化」的最後一哩,兩層一起才真的一份 build 跑遍天下

這篇其實是 ConfigMap/Secret 那篇的延伸。那篇講「把設定從映像檔裡挖出來」,讓同一個映像檔能跑遍所有環境;這篇講「把環境差異從 YAML 裡抽出來」,讓同一份部署來源能生出各環境的 manifest。兩層是同一個理想的上下半場:build 一次、設定與部署差異全外部化,一份產物跑遍 Dev 到 Production。 少了任何一層,你都會在某個環節退回去 copy-paste。想通這點,我看 CI/CD 的角度也變了——好的 pipeline,不是「為每個環境各建一次」,而是「建一次,靠設定與 overlay 把它擺進不同環境」。

別為了看起來專業,把簡單的事 Helm 化

最後照例潑冷水。我看過小專案、兩三個環境,一上來就搞一個滿是 {{ }} 的自製 Helm chart,結果每次改個副本數都要跟模板語言搏鬥——把本來三行 diff 能解決的事,包成一個要維護的套件。 打包工具是拿來降低跨環境的重複與風險的,不是拿來展示技術棧的。我的順位一律是 先確認痛點、再上重武器:環境少、差異小,raw YAML 或薄薄一層 Kustomize 就夠;真的多環境、多團隊、要發佈共用,才值得 Helm 那套機制。工具的重量,要配得上問題的重量——這是我走完整個 K8s 系列後,最想留下的一句總結。