Table of contents
1 Helm 簡介
自動化部署、everything as code 係今時今日既 best practices,而通常部署一個 business application,甚至乎只係部署一個 microservice,都會涉及好幾個 Kubernetes(K8s)既資源。
而 Helm 就具備左以下功能:
Helm 功能 | 描述 |
---|
打包相關資源 | 將唔同類型既 K8s 資源打包成一舊野,易於一同部署、管理版本。 |
製作範本 | 我地可以用 Helm 製作一啲 generic Helm templates,畀差唔多既 microservices 用,可能只係 app name、version、某啲 K8s 資源既配置有分別。 |
通常啲 Helm charts 都會體現曬以上既功能。
此外,
- Helm 只係一個本地既 CLI 工具,唔似 Istio 咁要安裝喺 K8s cluster 上面運行。
- 話雖如此,我地都可以喺自動化 CI/CD pipeline 度用 Helm CLI 執行 Helm commands。
- 雖然我地可以用 Helm 管理 K8s 資源,但唔代表用左 Helm 之後就唔可以用
kubectl edit
command 去臨時修改由 Helm 部署既 K8s 資源。
2 準備工作
2.1 安裝 K8s
我地可以安裝 Docker Desktop,然後開啟內置既 Kubernetes 功能。
2.2 用 Nexus 建立 Helm repository
之後,我地需要:
- 登入 admin 帳號。
- 去 Administration 頁面。
- Repository > Repositories > 撳「Create repository」 > 揀「helm (hosted)」。
- 喺「Name」輸入
helm
。
- 撳「Create repository」。
2.3 安裝 Helm CLI
- 去 Helm 既 GitHub releases 頁面下載:GitHub - helm/helm - Releases。
- 解壓縮 ZIP 檔。
- 將
helm.exe
註冊落系統既 PATH variable。
3 動手寫
喺呢篇入門文章裡面,我地會將以下既 K8s 資源打包成一個 Helm chart,然後用 Helm CLI 既 command 部署喺 K8s。
Deployment
Service
HorizontalPodAutoscaler
ConfigMap
Secret
3.1 Spring Boot web app Docker image
呢個 web app 需要:
- Expose port
8080
。
- 用 Spring Boot Actuator(expose HTTP endpoints 畀 K8s probes 用)。
- 用 Spring Data JPA 或者 Spring Data JDBC(會用到一啲 Spring 配置,當中包括密碼)。
- 用 H2(in-memory database)。
因為個 project 都係啲簡單 Spring Boot 野,所以呢篇文章就省略 implementation。
之後我地要將佢整成 Docker image(app 既版本係 1.5.0
),就要喺有 Dockerfile
既 directory 執行以下 command:
docker image build --no-cache -t spring-boot-3-helm-demo:1.5.0 -f Dockerfile .
3.2 Helm chart
Project structure:
/templates
ConfigMap.yml
Deployment.yml
HorizontalPodAutoscaler.yml
Secret.yml
Service.yml
Chart.yaml
values.yaml
註:Chart.yaml
以及 values.yaml
一定要係 .yaml
副檔名,唔可以係 .yml
副檔名。
3.2.1 Template 檔案
Deployment.yml
:
1kind: Deployment
2apiVersion: apps/v1
3metadata:
4 name: spring-boot-3-helm-demo
5 namespace: default
6spec:
7 replicas: 2 # 呢個會係固定數量
8 selector:
9 matchLabels:
10 app: spring-boot-3-helm-demo
11 template:
12 metadata:
13 labels:
14 app: spring-boot-3-helm-demo
15 spec:
16 terminationGracePeriodSeconds: 120 # 唔可以少過 Spring Boot 既 graceful shutdown 時限配置
17 containers:
18 - name: spring-boot-3-helm-demo
19 image: spring-boot-3-helm-demo:{{ .Values.appVersion | default .Chart.AppVersion }}
20 imagePullPolicy: Never # 只限本地測試用
21 securityContext:
22 readOnlyRootFilesystem: true # 安全需要
23 ports:
24 - containerPort: 8080
25 startupProbe:
26 httpGet:
27 path: /actuator/health/liveness
28 port: 8080
29 initialDelaySeconds: 5
30 periodSeconds: 5
31 failureThreshold: 30
32 timeoutSeconds: 5
33 livenessProbe:
34 httpGet:
35 path: /actuator/health/liveness
36 port: 8080
37 initialDelaySeconds: 5
38 periodSeconds: 10
39 failureThreshold: 3
40 timeoutSeconds: 5
41 readinessProbe:
42 httpGet:
43 path: /actuator/health/readiness
44 port: 8080
45 initialDelaySeconds: 5
46 periodSeconds: 10
47 failureThreshold: 3
48 timeoutSeconds: 5
49 resources:
50 limits:
51 cpu: 1000m
52 requests:
53 cpu: 500m
54 envFrom:
55 - configMapRef:
56 name: config-spring-boot-3-helm-demo
57 - secretRef:
58 name: secret-spring-boot-3-helm-demo
59
60# 用 emptyDir 方法,令 /tmp 可以寫入
61 volumeMounts:
62 - name: empty-tmp-dir
63 mountPath: /tmp
64 volumes:
65 - name: empty-tmp-dir
66 emptyDir: {}
註:
- Docker Desktop 既 K8s 會喺 Docker Desktop 內置既 image repository 度 pull Docker image。
{{ .Values.appVersion | default .Chart.AppVersion }}
係 Helm 自帶既 Go template syntax。解釋:
- 如果
values.yaml
或者 command 既 --set
option 有 appVersion
,就用佢,否則默認值係 Chart.yaml
裡面既 appVersion
。
Service.yml
:
1kind: Service
2apiVersion: v1
3metadata:
4 name: svc-spring-boot-3-helm-demo
5 namespace: default
6spec:
7 type: ClusterIP
8 selector:
9 app: spring-boot-3-helm-demo
10 ports:
11 - port: 8080
12 targetPort: 8080
13 protocol: TCP
HorizontalPodAutoscaler.yml
:
1kind: HorizontalPodAutoscaler
2apiVersion: autoscaling/v2
3metadata:
4 name: hpa-spring-boot-3-helm-demo
5 namespace: default
6spec:
7 scaleTargetRef:
8 apiVersion: apps/v1
9 kind: Deployment
10 name: spring-boot-3-helm-demo
11 minReplicas: 4
12 maxReplicas: 8
13 metrics:
14 - type: Resource
15 resource:
16 name: cpu
17 target:
18 type: Utilization
19 averageUtilization: 50
20 - type: Resource
21 resource:
22 name: memory
23 target:
24 type: Utilization
25 averageUtilization: 50
註:要用到 K8s HorizontalPodAutoscaler
,我地需要喺 K8s 裡面運行 metrics server。
ConfigMap.yml
:
1kind: ConfigMap
2apiVersion: v1
3metadata:
4 name: config-spring-boot-3-helm-demo
5 namespace: default
6data: {{- toYaml .Values.container.env | nindent 2 }}
註:
{{- toYaml .Values.container.env | nindent 2 }}
係 Helm 自帶既 Go template syntax。解釋:
- 將
values.yaml
或者 command 既 --set
option 既 container.env
轉換成 YAML 格式,並且每一個 element 都用 nindent
function 加入 newline 以及 2
格 spaces 既 indentation。
Secret.yml
:
1kind: Secret
2apiVersion: v1
3metadata:
4 name: secret-spring-boot-3-helm-demo
5 namespace: default
6type: Opaque
7stringData: {{- toYaml .Values.secrets.raw | nindent 2 }}
8data: {{- toYaml .Values.secrets.base64 | nindent 2 }}
註:
stringData
裡面既 values 係正常 string。
data
裡面既 values 要係 Base64-encoded string。
- 例如用 JavaScript 既
btoa("StrongPassword123")
function call 就會得到 U3Ryb25nUGFzc3dvcmQxMjM=
。
3.2.2 Chart.yaml
1apiVersion: v2
2name: spring-boot-3-helm-demo
3type: application
4description: A Helm chart for spring-boot-3-helm-demo
5version: 1.2.3
6appVersion: "0.0.0"
解釋:
Field | 描述 |
---|
apiVersion | 呢個係 Helm chart 既 API 版本,同 Helm CLI 版本(3.x )係兩樣野黎。 |
name | 用黎定義呢個 Helm chart 既名。如果用呢個 Helm chart 黎做多個 apps 既 K8s resource template,咁呢個就應該係個 generic 既名。呢個同 Helm release 名係兩樣野黎。 |
version | 用黎定義呢個 Helm chart 既版本。當 K8s YAML definitions、helpers 有變更,我地就要打包成一個新版本既 Helm chart。呢個同 app 既版本係兩樣野黎。 |
appVersion | 用黎定義呢個 Helm chart 裡面既 business app 既版本。如果用呢個 Helm chart 黎做單一 app 既 K8s resource template,咁喺 Chart.yaml 檔 set 呢個就合理。如果用呢個 Helm chart 黎做多個 apps 既 K8s resource template,咁就應該用 helm upgrade 既 --set option 會 override appVersion 既 value。通常會用 appVersion 黎改變個 app 既 K8s Deployment 用既 Docker image tag 版本。 |
3.2.3 values.yaml
values.yaml
既作用係提供 template variables,避免將太多野 hardcode 入啲 template 檔案裡面。
1container:
2 env:
3 logging.level.root: WARN
4 management.endpoints.web.exposure.include: "*"
5 server.shutdown: graceful
6 spring.lifecycle.timeout-per-shutdown-phase: 120s
7 spring.jpa.show-sql: "true"
8 spring.jpa.open-in-view: "false"
9 spring.jpa.hibernate.ddl-auto: update
10
11secrets:
12 raw:
13 spring.datasource.url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=4;MODE=MSSQLServer;DATABASE_TO_UPPER=FALSE;CASE_INSENSITIVE_IDENTIFIERS=TRUE
14 spring.datasource.username: sa
15 base64:
16 spring.datasource.password: U3Ryb25nUGFzc3dvcmQxMjM=
注意:唔可以將 container.env
用 YAML 既 flattened key 寫法,一定要分成兩行帶有 indentation 咁寫。
# 錯誤示範
container.env: # Helm 唔畀我地咁寫
foo: bar
3.2.4 檢查 Helm chart 問題
我地可以喺開發 Helm chart 既時候(打包 Helm chart 之前)檢查 coding issues:
helm lint .
如果冇問題,Helm 會出以下既 log:
==> Linting .
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
3.2.5 註冊 Helm repository
執行以下 command:
helm repo add helm http://localhost:8081/repository/helm/
Helm 會用 Chart.yaml
、template 檔案、values.yaml
等等去做檢查。
3.2.6 打包 Helm chart
喺 Helm chart 既 project root directory 執行以下 command:
helm package .
3.2.7 上載 Helm chart 到 Helm repository
執行以下 command:
curl -u admin:password -T spring-boot-3-helm-demo-1.2.3.tgz http://localhost:8081/repository/helm/
3.2.8 確認 Helm chart 喺 Helm repository
我地要先更新一下本地既 repository cache data。
helm repo update
列出所有 Helm charts:
helm search repo -l
驗證 spring-boot-3-helm-demo
既 Helm chart 存唔存在:
helm search repo -l spring-boot
3.3 測試 Helm release
測試既 command 同部署既 command 一樣,只需要加上 --dry-run --debug
既 options:
helm upgrade --install --atomic --timeout 10m --cleanup-on-fail spring-boot-3-helm-demo-1.5.0 helm/spring-boot-3-helm-demo --version 1.2.3 --set appVersion=1.5.0 --dry-run --debug
3.4 部署 Helm release
執行以下 command:
helm upgrade --install --atomic --timeout 10m --cleanup-on-fail spring-boot-3-helm-demo-1.5.0 helm/spring-boot-3-helm-demo --version 1.2.3 --set appVersion=1.5.0
註:
spring-boot-3-helm-demo-1.5.0
係個 release 既名。
--version 1.2.3
係個 Helm chart 既版本。
--set appVersion=1.5.0
係個 app 既版本。
等一陣之後,Helm 會出以下既 log:
1Release "spring-boot-3-helm-demo-1.5.0" does not exist. Installing it now.
2NAME: spring-boot-3-helm-demo-1.5.0
3LAST DEPLOYED: Tue Feb 25 19:03:34 2025
4NAMESPACE: default
5STATUS: deployed
6REVISION: 1
7TEST SUITE: None
再用 kubectl
command 驗證所有 K8s 資源都被成功創建:
kubectl get deployment,svc,hpa,configmap,secret
就會出以下既結果:
1NAME READY UP-TO-DATE AVAILABLE AGE
2deployment.apps/spring-boot-3-helm-demo 4/4 4 4 2m46s
3
4NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
5service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 39d
6service/svc-spring-boot-3-helm-demo ClusterIP 10.106.127.253 <none> 8080/TCP 2m46s
7
8NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
9horizontalpodautoscaler.autoscaling/hpa-spring-boot-3-helm-demo Deployment/spring-boot-3-helm-demo 0%/50%, <unknown>/50% 4 8 4 2m46s
10
11NAME DATA AGE
12configmap/config-spring-boot-3-helm-demo 7 2m46s
13configmap/kube-root-ca.crt 1 39d
14
15NAME TYPE DATA AGE
16secret/secret-spring-boot-3-helm-demo Opaque 3 2m46s
17secret/sh.helm.release.v1.spring-boot-3-helm-demo-1.5.0.v1 helm.sh/release.v1 1 2m46s
3.5 列出所有 Helm releases
執行以下 command:
helm list --all
3.6 刪除已部署既 Helm release
執行以下 command:
helm uninstall spring-boot-3-helm-demo-1.5.0
註:
- Helm
3.x
用既係 3-way strategic merge patches。
-
In Helm 3, we now use a three-way strategic merge patch. Helm considers the old manifest, its live state, and the new manifest when generating a patch.
- 除非明確加上
--keep-history
option,否則默認係會完全刪除曬個 Helm release,唔留低 old manifest。
4 參考資料