企業級雲原生應用交付及管理系列 - Helm 除錯及維護 (二)

張晉濤發表於2022-04-02

大家好,我是張晉濤。

在上一篇 《企業級雲原生應用交付及管理系列 - Helm 基礎 (一)》 中,我主要介紹了
Helm 的誕生及其發展,包括 Helm 各個版本的情況及社群的發展。

此外,還介紹了 Helm 的架構,概念,外掛以及其基本用法。

本節我將預設讀者已經有一定的 Helm 和 Kubernetes 基礎,
介紹 Helm 的一些高階特性和用法,
如果有不清楚的概念可以看我的歷史文章。

準備

這裡我們使用 helm create 命令來建立一個 Helm chart。執行完成後,會在當前目錄建立一個新的目錄,其中包含了 Helm 預先建立的一個模板。
本文中的後續內容均會基於該 Helm chart 完成。

tao@moelove:~$ helm create moelove
Creating moelove                
tao@moelove:~$ ls                    
moelove
tao@moelove:~$ tree
.
└── moelove
    ├── Chart.yaml
    ├── charts
    ├── templates
    │   ├── NOTES.txt
    │   ├── _helpers.tpl
    │   ├── deployment.yaml
    │   ├── hpa.yaml
    │   ├── ingress.yaml
    │   ├── service.yaml
    │   ├── serviceaccount.yaml
    │   └── tests
    │       └── test-connection.yaml
    └── values.yaml

4 directories, 10 files

Debug

在我們去建立/維護,或者使用 Helm chart 進行應用部署的時候,有時候可能會遇到一些錯誤。
那麼如何對 Helm chart 進行 debug 呢?這是很多人都會遇到的一個問題。

Helm chart 是通過 YAML 進行維護的,而 YAML 是縮排/語法敏感的。
假如你的縮排或者語法有問題,都將會導致報錯。
最簡單的檢查辦法是使用 helm lint 進行檢查。

比如我們進行如下修改:

diff --git a/values.yaml b/values.yaml
index 4a8b237..696a77d 100644
--- a/values.yaml
+++ b/values.yaml
@@ -5,7 +5,7 @@
 replicaCount: 1
 
 image:
-  repository: nginx
+ repository: nginx
   pullPolicy: IfNotPresent
   # Overrides the image tag whose default is the chart appVersion.
   tag: ""

可以看到,我們將 image.repository 的縮排搞錯了。
這時進行安裝將看到如下報錯:

tao@moelove:~$ helm install foo .
Error: INSTALLATION FAILED: cannot load values.yaml: error converting YAML to JSON: yaml: line 9: mapping values are not allowed in this context

我建議你可以使用 helm lint 先進行檢查,避免一些低階錯誤。如下:

tao@moelove:~$ helm lint .
==> Linting .
[INFO] Chart.yaml: icon is recommended
[ERROR] values.yaml: unable to parse YAML: error converting YAML to JSON: yaml: line 9: mapping values are not allowed in this context
[ERROR] templates/: cannot load values.yaml: error converting YAML to JSON: yaml: line 9: mapping values are not allowed in this context
[ERROR] : unable to load chart
        cannot load values.yaml: error converting YAML to JSON: yaml: line 9: mapping values are not allowed in this context

Error: 1 chart(s) linted, 1 chart(s) failed

我們將該內容恢復原樣,並進行如下變更:

diff --git a/values.yaml b/values.yaml
index 4a8b237..c86c0be 100644
--- a/values.yaml
+++ b/values.yaml
@@ -2,7 +2,7 @@
 # This is a YAML-formatted file.
 # Declare variables to be passed into your templates.
 
-replicaCount: 1
+replicaCount: "this should not be string"
 
 image:
   repository: nginx

replicaCount 修改成了一段字串。這時候我們使用 helm lint 是無法檢查出來的。

tao@moelove:~$ helm lint .
==> Linting .
[INFO] Chart.yaml: icon is recommended

1 chart(s) linted, 0 chart(s) failed

因為從 YAML 語法上是無法檢查出來型別的,並且這也是我們具體的業務邏輯(Kubernetes)限制的。

這時,如果進行安裝將得到如下錯誤:

tao@moelove:~$ helm install foo .
Error: INSTALLATION FAILED: unable to build kubernetes objects from release manifest: error validating "": error validating data: ValidationError(Deployment.spec.replicas): invalid type for io.k8s.api.apps.v1.DeploymentSpec.replicas: got "string", expected "integer"

可以看到,對於這種型別錯誤是可以很直接的得到反饋的。

但還有一種情況,就是語法規則,型別均正常,但是不符合業務的實際預期。

比如我們進行如下變更:

diff --git a/values.yaml b/values.yaml
index 4a8b237..8feedd6 100644
--- a/values.yaml                                                                                    
+++ b/values.yaml                                                                                    
@@ -8,7 +8,7 @@ image:
   repository: nginx
   pullPolicy: IfNotPresent
   # Overrides the image tag whose default is the chart appVersion.
-  tag: ""
+  tag: "1.20"
  
 imagePullSecrets: []
 nameOverride: ""

這時,可以安裝成功,但可能我們預期想要安裝的映象是 1.20-alpine
這種場景下,上述兩種方式就都沒有效果了。

我們可以通過 helm install --dry-run --debug 命令進行除錯。
當然,如果你想通過 helm template 進行除錯也可以。

這兩者主要的區別在於,如果增加了 --debug 引數的話,可以輸出更加詳細的資訊,
包括最終使用的 Values 資訊等。

這是我比較推薦的做法,適合在你開發/除錯 Helm chart 時使用。

Helm 單元測試

談到 Helm chart 的單元測試,你可能會產生疑問,YAML 也要寫單元測試?

是的。如果你是 Helm chart 的維護者的話,寫單元測試是個好主意,可以更好的保證大多數內容都是符合預期的。

如果想要為 Helm chart 寫單元測試,我有三個工具推薦。

  • quintush/helm-unittest 是從 helm-unittest/helm-unittest fork 出來的,但是它更加活躍,並且包含了很多功能和修復,此外,它可以很好的與 Helm 3 配合使用;
  • conftest 這是基於 Open Policy Agent (OPA) 的一個工具,通過使用 Rego 編寫策略檔案來完成配置的校驗。我現在在 Apache APISIX Ingress controller 專案中使用它,輔助使用者進行升級檢查;
  • terratest 這是一個使用 Go 開發的通用測試框架,支援多種配置的測試,包括 Helm,AWS/Docker 等;

其中我最喜歡的是 conftest ,因為我更喜歡寫 Rego ,對 OPA 感興趣的小夥伴可以參考我之前的文章 《Open Policy Agent(OPA) 入門實踐》
最簡單的則是 helm-unittest 了,它更加專注一些。

這裡我們使用 helm-unittest 作為示例。

安裝

helm-unittest 可以作為 Helm Plugin 進行安裝,執行以下命令即可:

tao@moelove:~$ helm plugin install https://github.com/quintush/helm-unittest
Support linux-amd64
...
Installed plugin: unittest

測試

只要在 chart 目錄下建立一個 tests 目錄,在其中編寫測試檔案即可。

tao@moelove:~$ cat tests/deployment_test.yaml
suite: test deployment
templates:
  - deployment.yaml
tests:
  - it: should work
    set:
      image.tag: latest
    asserts:
      - isKind:
          of: Deployment
      - matchRegex:
          path: metadata.name
          pattern: -moelove$
      - equal:
          path: spec.template.spec.containers[0].image
          value: nginx:latest

我可以通過如下方式進行執行,注意,一定要增加 -3 引數,以便可以和 Helm v3 進行相容。

tao@moelove:~$ helm unittest -3 .

### Chart [ moelove ] .

 PASS  test deployment  tests/deployment_test.yaml

Charts:      1 passed, 1 total
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshot:    0 passed, 0 total
Time:        5.932023ms

我們可以此 case 做些調整,看看如果測試不通過會是什麼情況:

tao@moelove:~$ cat tests/deployment_test.yaml 
suite: test deployment
templates:
  - deployment.yaml
tests:
  - it: should work
    set:
      image.tag: alpine
    asserts:
      - isKind:
          of: Deployment
      - matchRegex:
          path: metadata.name
          pattern: -moelove$
      - equal:
          path: spec.template.spec.containers[0].image
          value: nginx:latest

這裡我們給 image.tag 設定為 alpine ,但是在斷言中沒有讓其匹配,所以執行測試後會看到失敗。

tao@moelove:~$ helm unittest -3 .

### Chart [ moelove ] .

 FAIL  test deployment  tests/deployment_test.yaml
        - should work

                - asserts[2] `equal` fail
                        Template:       moelove/templates/deployment.yaml
                        DocumentIndex:  0
                        Path:   spec.template.spec.containers[0].image
                        Expected to equal:
                                nginx:latest
                        Actual:
                                nginx:alpine
                        Diff:
                                --- Expected
                                +++ Actual
                                @@ -1,2 +1,2 @@
                                -nginx:latest
                                +nginx:alpine


Charts:      1 failed, 0 passed, 1 total
Test Suites: 1 failed, 0 passed, 1 total
Tests:       1 failed, 0 passed, 1 total
Snapshot:    0 passed, 0 total
Time:        5.22252ms

Error: plugin "unittest" exited with error

我們可以很清晰的看到具體失敗的原因和位置。

如果你在維護 Helm chart,並且需要保證其能夠高質量交付,那麼為其增加單元測試是個不錯的辦法。
至於工具的選擇,主要看個人喜好。helm-unittest 只需要寫 YAML, 而其他兩個工具分別是寫 Rego 和 Go 。

總結

在本文中,我們主要聚焦到了 Helm chart 的除錯和維護這個主題上。
介紹了 Helm 內建的對 chart 的一些檢查工具,同時也介紹瞭如何使用 helm-unittest 為 Helm chart 編寫
單元測試。

在維護,交付和使用 Helm chart 時,掌握這些內容都是非常有用的。如果這篇文章對你有所幫助,歡迎點贊、轉發、留言討論。

如果大家對這個主題感興趣的話,後續我還會更新 Helm 的一些進階內容,敬請期待!


歡迎訂閱我的文章公眾號【MoeLove】

TheMoeLove

相關文章