K8s 下的應用管理 — 瞭解 Helmfile

Wi1dcard發表於2020-04-28

在上一篇文章中,為大家介紹了 Helm 的初步使用。然而這仍然不能滿足我司的工作流,主要問題有:

  1. Helm 不提供 apply 命令;因此在 CI/CD 場景中必須考慮到判斷是 install 還是 upgrade。
  2. 不方便控制安裝的 chart 版本;例如指定版本範圍、鎖定某一版本等。
  3. Values 必須是純文字;不支援模板渲染、不方便區分環境。

因此我們需要 Helm Releases as Code。我聽說過的產品有 HelmsmanHelmfile 兩款。目前我們團隊已經使用後者一段時間,並且有團隊成員貢獻過部分程式碼。

至於為什麼選擇 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 將會幫我們:

  1. 新增 repositories 中宣告的 Helm chart repo。
  2. 根據 release 小節內的配置,安裝或更新 chart。

因此,上篇文章中提到的:

helm repo add ...
helm install ...
helm upgrade ...

可直接被簡化為:

helmfile apply

同時,如果你安裝了 helm-diff 外掛,Helmfile 還會在執行操作前輸出清晰的 diff:

K8s 下的應用管理 — 瞭解 Helmfile

具體安裝過程本文不再詳述。

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 協議》,轉載必須註明作者和本文連結

Former WinForm and PHP engineer. Now prefer Golang and Rust, and mainly working on DevSecOps and Kubernetes.

相關文章