在上一篇文章中,為大家介紹了 Helm 的初步使用。然而這仍然不能滿足我司的工作流,主要問題有:
- Helm 不提供
apply
命令;因此在 CI/CD 場景中必須考慮到判斷是 install 還是 upgrade。 - 不方便控制安裝的 chart 版本;例如指定版本範圍、鎖定某一版本等。
- Values 必須是純文字;不支援模板渲染、不方便區分環境。
因此我們需要 Helm Releases as Code
。我聽說過的產品有 Helmsman 和 Helmfile 兩款。目前我們團隊已經使用後者一段時間,並且有團隊成員貢獻過部分程式碼。
至於為什麼選擇 Helmfile,背後的真正原因是在發現 Helmfile 的時候還沒聽說過 Helmsman。後來瞭解 Helmsman 並嘗試後發現它並沒有解決我們後兩個問題,因此也就沒有替換了。
接下來我針對以上幾個痛點來說說我們是怎麼做的。
一鍵 Apply
Helmfile 的文件非常簡明、直接,示例配置檔案 就在 README 裡。我節選一小段來說幾個必要的配置項:
repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami
releases:
- name: my-release # Release name
namespace: staging # Release namespace
chart: bitnami/redis # Chart name
values: # 等效於 helm 的 --values 選項
- foo.yaml
set: # 等效於 helm 的 --set 選項
- name: cluster.enabled
value: "false"
將以上內容儲存為 helmfile.yaml
檔案,隨後執行 helmfile apply
即可。Helmfile 將會幫我們:
- 新增
repositories
中宣告的 Helm chart repo。 - 根據
release
小節內的配置,安裝或更新 chart。
因此,上篇文章中提到的:
helm repo add ...
helm install ...
helm upgrade ...
可直接被簡化為:
helmfile apply
同時,如果你安裝了 helm-diff 外掛,Helmfile 還會在執行操作前輸出清晰的 diff:
具體安裝過程本文不再詳述。
Chart 版本控制
大多數社群提供的 charts 都採用 Semver 2.0 作為版本號。因此大多數情況下我們都希望鎖定主版本,防止誤升級引入 breaking change。Helmfile 提供了 version
引數可用於指定版本範圍,例如:
# ...
releases:
- name: my-release
namespace: staging
chart: bitnami/redis
version: ^10.5.13 # 防止升級到 v11.x.x
# ...
同時,Helmfile 還提供了 lock 檔案,功能與常見版本管理器中的 lock 類似。配合 CI 時,除非提交程式碼改動 lock 檔案,否則在任意時間點執行 CI 安裝的 chart 版本是一致的。
你可以通過 helmfile deps
命令生成 lock,以上 helmfile.yaml
生成的示例 helmfile.lock
檔案長這樣:
version: v0.102.0
dependencies:
- name: redis
repository: https://charts.bitnami.com/bitnami
version: 10.5.13
digest: sha256:20f2840c2642bf98f03d2b5cf890c73e1f2c100a0f0475777ae7788b2a0ae98d
generated: "2020-03-24T14:14:01.663801+08:00"
可看出包含具體的 chart 版本、Helmfile 版本、雜湊值、生成時間等引數。
當需要更新 lock 檔案時,同樣執行 helmfile deps
即可。
動態 Values
在 CI 上部署時,有些 values 的值不是固定的,可能來自於環境變數,也可能由於環境差異而不同。
環境變數
在之前的文章中我們介紹過 review apps,其中有一項很重要的需求是,每次開新分支部署的 release 不能同名,否則資源會因為重名而安裝失敗。所以我們要讀取 GitLab CI 的 $CI_ENVIRONMENT_SLUG
環境變數,並拼接到最終的 Helm release name。因此可以這樣做:
# ...
releases:
- name: api-{{ requiredEnv "CI_ENVIRONMENT_SLUG" }}
chart: ./deploy/chart
# ...
其實 helmfile.yaml
是個 Go template 格式的模板,因此你還可以把環境變數傳遞給 values,也可以使用 if
之類的語法。例如:
# ...
releases:
- name: my-release
namespace: staging
chart: bitnami/redis
set:
- name: cluster.enabled
value: {{ requiredEnv "REDIS_CLUSTER_ENABLED" }}
區分環境
Helmfile 提供了名為 environments
的配置,此處並不是指環境變數,而是一個專屬於 Helmfile 的概念。來看看例子。首先建立兩個檔案:
# environments/staging/values.yaml
releaseName: staging-release
# environment/production/values.yaml
releaseName: production-release
配置 helmfile.yaml
:
# ...
environments:
staging:
values: # 針對 staging 環境的 values,可通過 {{ .Environment.Values.* }} 讀取
- environments/staging/values.yaml
production:
values: # 針對 production 環境的 values,讀取方式相同
- environment/production/values.yaml
releases:
- name: {{ .Environment.Values.releaseName }} # 引用 environment values 的值(例如 staging-release 或 production-release)
namespace: {{ .Environment.Name }} # 引用 envrionment 名稱(例如 staging 或 production)
chart: bitnami/redis
values:
- values/{{ .Environment.Name }}/values.yaml # 根據環境名稱讀取不同的 Helm values 檔案
{{ if eq .Environment.Name "production" }} # Go template 的 if 語句
- values/production-specified-values.yaml # 僅用於 production 環境的 Helm values
{{ end }}
在執行 helmfile
時使用 -e
選項即可指定安裝的環境:
helmfile -e staging apply
注意 -e
必須在子命令 apply
之前,它是一個全域性選項。
小結
不得不說,Helmfile 的確很靈活,但採用 Go template + YAML 語法編寫配置的方式稍有些難以閱讀和維護。
本作品採用《CC 協議》,轉載必須註明作者和本文連結