包管理工具 HELM(7)
包管理工具 HELM(7)
1. Helm安裝使用
Helm
這個東西其實早有耳聞,但是一直沒有用在生產環境,而且現在對這貨的評價也是褒貶不一。正好最近需要再次部署一套測試環境,對於單體服務,部署一套測試環境我相信還是非常快的,但是對於微服務架構的應用,要部署一套新的環境,就有點折磨人了,微服務越多、你就會越絕望的。雖然我們線上和測試環境已經都遷移到了kubernetes
環境,但是每個微服務也得維護一套yaml
檔案,而且每個環境下的配置檔案也不太一樣,部署一套新的環境成本是真的很高。如果我們能使用類似於yum的工具來安裝我們的應用的話是不是就很爽歪歪了啊?Helm
就相當於kubernetes
環境下的yum包管理工具。
1.1 用途
做為 Kubernetes 的一個包管理工具,Helm
具有如下功能:
- 建立新的 chart
- chart 打包成 tgz 格式
- 上傳 chart 到 chart 倉庫或從倉庫中下載 chart
- 在
Kubernetes
叢集中安裝或解除安裝 chart- 管理用
Helm
安裝的 chart 的釋出週期
1.2 重要概念
Helm 有三個重要概念:
- chart:包含了建立
Kubernetes
的一個應用例項的必要資訊- config:包含了應用釋出配置資訊
- release:是一個 chart 及其配置的一個執行例項
1.3 Helm元件
Helm 有以下兩個組成部分: Helm Structrue
Helm Client
是使用者命令列工具,其主要負責如下:
- 本地 chart 開發
- 倉庫管理
- 與 Tiller sever 互動
- 傳送預安裝的 chart
- 查詢 release 資訊
- 要求升級或解除安裝已存在的 release
Tiller Server
是一個部署在Kubernetes
叢集內部的 server,其與 Helm client、Kubernetes API server 進行互動。Tiller server 主要負責如下:
- 監聽來自 Helm client 的請求
- 通過 chart 及其配置構建一次釋出
- 安裝 chart 到
Kubernetes
叢集,並跟蹤隨後的釋出- 通過與
Kubernetes
互動升級或解除安裝 chart- 簡單的說,client 管理 charts,而 server 管理髮布 release
1.4 安裝
我們可以在Helm Realese頁面下載二進位制檔案,這裡下載的v2.10.0版本,解壓後將可執行檔案helm
拷貝到/usr/local/bin
目錄下即可,這樣Helm
客戶端就在這臺機器上安裝完成了。
現在我們可以使用Helm
命令檢視版本了,會提示無法連線到服務端Tiller
:
[root@node01 ~]# wget https://get.helm.sh/helm-v2.10.0-linux-amd64.tar.gz
[root@node01 ~]# tar xf helm-v2.10.0-linux-amd64.tar.gz
[root@node01 ~]# ls linux-amd64/
helm LICENSE README.md
[root@node01 ~]# cp -a ./linux-amd64/helm /usr/local/bin/
[root@node01 ~]# helm version
Client: &version.Version{SemVer:"v2.10.0", GitCommit:"9ad53aac42165a5fadc6c87be0dea6b115f93090", GitTreeState:"clean"}
Error: could not find tiller
要安裝 Helm 的服務端程式,我們需要使用到kubectl
工具,所以先確保kubectl
工具能夠正常的訪問 kubernetes 叢集的apiserver
哦。
然後我們在命令列中執行初始化操作:
[root@node01 ~]# helm init --upgrade --tiller-image cnych/tiller:v2.10.0 --stable-repo-url https://cnych.github.io/kube-charts-mirror/
Creating /root/.helm
Creating /root/.helm/repository
Creating /root/.helm/repository/cache
Creating /root/.helm/repository/local
Creating /root/.helm/plugins
Creating /root/.helm/starters
Creating /root/.helm/cache/archive
Creating /root/.helm/repository/repositories.yaml
Adding stable repo with URL: https://cnych.github.io/kube-charts-mirror/
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /root/.helm.
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.
Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation
Happy Helming!
這個命令會把預設的 google 的倉庫地址替換成我同步的一個映象地址。
如果在安裝過程中遇到了一些其他問題,比如初始化的時候出現瞭如下錯誤:
E0125 14:03:19.093131 56246 portforward.go:331] an error occurred forwarding 55943 -> 44134: error forwarding port 44134 to pod d01941068c9dfea1c9e46127578994d1cf8bc34c971ff109dc6faa4c05043a6e, uid : unable to do port forwarding: socat not found.
2018/01/25 14:03:19 (0xc420476210) (0xc4203ae1e0) Stream removed, broadcasting: 3
2018/01/25 14:03:19 (0xc4203ae1e0) (3) Writing data frame
2018/01/25 14:03:19 (0xc420476210) (0xc4200c3900) Create stream
2018/01/25 14:03:19 (0xc420476210) (0xc4200c3900) Stream added, broadcasting: 5
Error: cannot connect to Tiller
解決方案:在節點上安裝socat
可以解決
$ sudo yum install -y socat
Helm 服務端正常安裝完成後,Tiller
預設被部署在kubernetes
叢集的kube-system
名稱空間下:
[root@node01 ~]# kubectl get pod -n kube-system -l app=helm
NAME READY STATUS RESTARTS AGE
tiller-deploy-86b844d8c6-d2kw8 1/1 Running 0 27s
此時,我們檢視 Helm 版本就都正常了:
[root@node01 ~]# helm version
Client: &version.Version{SemVer:"v2.10.0", GitCommit:"9ad53aac42165a5fadc6c87be0dea6b115f93090", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.10.0", GitCommit:"9ad53aac42165a5fadc6c87be0dea6b115f93090", GitTreeState:"clean"}
另外一個值得注意的問題是RBAC
,我們的 kubernetes 叢集是1.10.0版本的,預設開啟了RBAC
訪問控制,所以我們需要為Tiller
建立一個ServiceAccount
,讓他擁有執行的許可權,詳細內容可以檢視 Helm 文件中的Role-based Access Control。 建立rbac.yaml
檔案:
[root@node01 ~]# vim rbac-config.yaml
[root@node01 ~]# cat rbac-config.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
然後使用kubectl
建立:
[root@node01 ~]# kubectl create -f rbac-config.yaml
serviceaccount "tiller" created
clusterrolebinding.rbac.authorization.k8s.io "tiller" created
建立了tiller
的 ServceAccount 後還沒完,因為我們的 Tiller 之前已經就部署成功了,而且是沒有指定 ServiceAccount 的,所以我們需要給 Tiller 打上一個 ServiceAccount 的補丁:
[root@node01 ~]# kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
deployment.extensions "tiller-deploy" patched
上面這一步非常重要,不然後面在使用 Helm 的過程中可能出現
Error: no available release name found
的錯誤資訊。
至此, Helm
客戶端和服務端都配置完成了,接下來我們看看如何使用吧。
1.5 使用
我們現在了嘗試建立一個 Chart:
[root@node01 ~]# helm create hello-helm
Creating hello-helm
[root@node01 ~]# tree hello-helm
hello-helm
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ └── service.yaml
└── values.yaml
2 directories, 7 files
我們通過檢視templates
目錄下面的deployment.yaml
檔案可以看出預設建立的 Chart 是一個 nginx 服務,具體的每個檔案是幹什麼用的,我們可以前往 Helm 官方文件進行檢視,後面會和大家詳細講解的。比如這裡我們來安裝 1.7.9 這個版本的 nginx,則我們更改 value.yaml 檔案下面的 image tag 即可,將預設的 stable 更改為 1.7.9
,為了測試方便,我們把 Service 的型別也改成 NodePort
[root@node01 ~]# vim hello-helm/values.yaml
[root@node01 ~]# cat hello-helm/values.yaml
...
image:
repository: nginx
tag: 1.7.9
pullPolicy: IfNotPresent
nameOverride: ""
fullnameOverride: ""
service:
type: NodePort
port: 80
...
現在我們來嘗試安裝下這個 Chart :
[root@node01 ~]# helm install ./hello-helm
NAME: foppish-billygoat
LAST DEPLOYED: Mon Sep 20 15:11:34 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1beta2/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
foppish-billygoat-hello-helm 1 0 0 0 0s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
foppish-billygoat-hello-helm-79f56fc76f-9cdm8 0/1 Pending 0 0s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
foppish-billygoat-hello-helm NodePort 10.109.79.65 <none> 80:31941/TCP 0s
NOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services foppish-billygoat-hello-helm)
export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
[root@node01 ~]# kubectl get pods -l app=hello-helm
NAME READY STATUS RESTARTS AGE
foppish-billygoat-hello-helm-79f56fc76f-9cdm8 1/1 Running 0 2m
[root@node01 ~]# kubectl get svc -l app=hello-helm
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
foppish-billygoat-hello-helm NodePort 10.109.79.65 <none> 80:31941/TCP 3m
等到 Pod 建立完成後,我們可以根據建立的 Service 的 NodePort 來訪問該服務了,然後在瀏覽器中開啟http://k8s.haimaxy.com:31941就可以正常的訪問我們剛剛部署的 nginx 應用了。
檢視release
:
[root@node01 ~]# helm list
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
foppish-billygoat 1 Mon Sep 20 15:11:34 2021 DEPLOYED hello-helm-0.1.0 1.0 default
打包chart
:
[root@node01 ~]# helm package hello-helm
Successfully packaged chart and saved it to: /root/hello-helm-0.1.0.tgz
[root@node01 ~]# ll -d /root/hello-helm-0.1.0.tgz
-rw-r--r-- 1 root root 2598 9月 20 15:27 /root/hello-helm-0.1.0.tgz
然後我們就可以將打包的tgz
檔案分發到任意的伺服器上,通過helm fetch
就可以獲取到該 Chart 了。
刪除release
:
[root@node01 ~]# helm delete foppish-billygoat
release "foppish-billygoat" deleted
然後我們看到kubernetes
叢集上的該 nginx 服務也已經被刪除了。
[root@node01 ~]# kubectl get pods -l app=hello-helm
No resources found.
還有更多關於Helm的使用命令,我們可以前往官方文件檢視。
2. Helm 的基本使用
上節課我們成功安裝了Helm
的客戶端以及服務端Tiller Server
,我們也自己嘗試建立了我們的第一個 Helm Chart 包,這節課就來和大家一起學習下 Helm 中的一些常用的操作方法。
2.1 倉庫
Helm 的 Repo 倉庫和 Docker Registry 比較類似,Chart 庫可以用來儲存和共享打包 Chart 的位置,我們在安裝了 Helm 後,預設的倉庫地址是 google 的一個地址,這對於我們不能上網的同學就比較苦惱了,沒辦法訪問到官方提供的 Chart 倉庫,可以用helm repo list
來檢視當前的倉庫配置:
[root@node01 ~]# helm repo list
NAME URL
stable https://cnych.github.io/kube-charts-mirror/
local http://127.0.0.1:8879/charts
我們可以看到除了一個預設的 stable 的倉庫配置外,還有一個 local 的本地倉庫,這是我們本地測試的一個倉庫地址。其實要建立一個 Chart 倉庫也是非常簡單的,Chart 倉庫其實就是一個帶有index.yaml
索引檔案和任意個打包的 Chart 的 HTTP 伺服器而已,比如我們想要分享一個 Chart 包的時候,將我們本地的 Chart 包上傳到該伺服器上面,別人就可以使用了,所以其實我們自己託管一個 Chart 倉庫也是非常簡單的,比如阿里雲的 OSS、Github Pages,甚至自己建立的一個簡單伺服器都可以。
為了解決上網的問題,我這裡建了一個 Github Pages 倉庫,每天會自動和官方的倉庫進行同步,地址是:https://github.com/cnych/kube-charts-mirror,這樣我們就可以將我們的 Helm 預設倉庫地址更改成我們自己的倉庫地址了:
[root@node01 ~]# helm repo remove stable
"stable" has been removed from your repositories
[root@node01 ~]# helm repo add stable https://cnych.github.io/kube-charts-mirror/
"stable" has been added to your repositories
[root@node01 ~]# helm repo list
NAME URL
local http://127.0.0.1:8879/charts
stable https://cnych.github.io/kube-charts-mirror/
[root@node01 ~]# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈ Happy Helming!⎈
倉庫新增完成後,可以使用 update 命令進行倉庫更新。當然如果要我們自己來建立一個 web 伺服器來服務 Helm Chart 的話,只需要實現下面幾個功能點就可以提供服務了:
- 將索引和
Chart
置於伺服器目錄中- 確保索引檔案
index.yaml
可以在沒有認證要求的情況下訪問- 確保 yaml 檔案的正確內容型別(text/yaml 或 text/x-yaml)
如果你的 web 服務提供了上面幾個功能,那麼也就可以當做 Helm Chart 倉庫來使用了。
2.2 查詢 chart
Helm 將 Charts 包安裝到 Kubernetes 叢集中,一個安裝例項就是一個新的 Release,要找到新的 Chart,我們可以通過搜尋命令完成。
記住,如果不能上網,將預設的 stable 的倉庫地址更換成上面我們建立的地址
直接執行helm search
命令可以檢視有哪些 Charts 是可用的:
[root@node01 ~]# helm search
NAME CHART VERSION APP VERSION DESCRIPTION
...
stable/minio 1.6.3 RELEASE.2018-08-25T01-56-38Z Minio is a high performance distributed object storage se...
stable/mission-control 0.4.2 3.1.2 A Helm chart for JFrog Mission Control
stable/mongodb 4.2.2 4.0.2 NoSQL document-oriented database that stores JSON-like do...
stable/mongodb-replicaset 3.5.6 3.6 NoSQL document-oriented database that stores JSON-like do...
...
stable/zetcd 0.1.9 0.0.3 CoreOS zetcd Helm chart for Kubernetes
...
如果沒有使用過濾條件,helm search 顯示所有可用的 charts。可以通過使用過濾條件進行搜尋來縮小搜尋的結果範圍:
[root@node01 ~]# helm search mysql
NAME CHART VERSION APP VERSION DESCRIPTION
stable/mysql 0.10.2 5.7.14 Fast, reliable, scalable, and easy to use open-source rel...
stable/mysqldump 1.0.0 5.7.21 A Helm chart to help backup MySQL databases using mysqldump
stable/prometheus-mysql-exporter 0.2.1 v0.11.0 A Helm chart for prometheus mysql exporter with cloudsqlp...
stable/percona 0.3.3 5.7.17 free, fully compatible, enhanced, open source drop-in rep...
stable/percona-xtradb-cluster 0.5.0 5.7.19 free, fully compatible, enhanced, open source drop-in rep...
stable/phpmyadmin 1.3.0 4.8.3 phpMyAdmin is an mysql administration frontend
stable/gcloud-sqlproxy 0.6.0 1.11 Google Cloud SQL Proxy
stable/mariadb 5.2.3 10.1.37 Fast, reliable, scalable, and easy to use open-source rel...
可以看到明顯少了很多 charts 了,同樣的,我們可以使用 inspect 命令來檢視一個 chart 的詳細資訊:
[root@node01 ~]# helm inspect stable/mysql|head -27
appVersion: 5.7.14
description: Fast, reliable, scalable, and easy to use open-source relational database
system.
engine: gotpl
home: https://www.mysql.com/
icon: https://www.mysql.com/common/logos/logo-mysql-170x115.png
keywords:
- mysql
- database
- sql
maintainers:
- email: [email protected]
name: olemarkus
- email: [email protected]
name: viglesiasce
name: mysql
sources:
- https://github.com/kubernetes/charts
- https://github.com/docker-library/mysql
version: 0.10.2
---
## mysql image version
## ref: https://hub.docker.com/r/library/mysql/tags/
##
image: "mysql"
imageTag: "5.7.14"
...
使用 inspect 命令可以檢視到該 chart 裡面所有描述資訊,包括執行方式、配置資訊等等。
通過 helm search 命令可以找到我們想要的 chart 包,找到後就可以通過 helm install 命令來進行安裝了。
2.3 安裝 chart
要安裝新的軟體包,直接使用 helm install 命令即可。最簡單的情況下,它只需要一個 chart 的名稱引數:
[root@node01 ~]# helm install stable/mysql
NAME: geared-moth
LAST DEPLOYED: Mon Sep 20 16:01:21 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Secret
NAME TYPE DATA AGE
geared-moth-mysql Opaque 2 1s
==> v1/ConfigMap
NAME DATA AGE
geared-moth-mysql-test 1 1s
==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
geared-moth-mysql Pending 1s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
geared-moth-mysql ClusterIP 10.105.138.133 <none> 3306/TCP 1s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
geared-moth-mysql 1 1 1 0 1s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
geared-moth-mysql-58778f7d84-jrx57 0/1 Pending 0 0s
NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
geared-moth-mysql.default.svc.cluster.local
To get your root password run:
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default geared-moth-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)
To connect to your database:
1. Run an Ubuntu pod that you can use as a client:
kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il
2. Install the mysql client:
$ apt-get update && apt-get install mysql-client -y
3. Connect using the mysql cli, then provide your password:
$ mysql -h geared-moth-mysql -p
To connect to your database directly from outside the K8s cluster:
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
# Execute the following command to route the connection:
kubectl port-forward svc/geared-moth-mysql 3306
mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}
現在 mysql chart 已經安裝上了,安裝 chart 會建立一個新 release 物件。上面的 release 被命名為 hmewing-squid。如果你想使用你自己的 release 名稱,只需使用--name引數指定即可,比如:
$ helm install stable/mysql --name mydb
在安裝過程中,helm 客戶端將列印有關建立哪些資源的有用資訊,release 的狀態以及其他有用的配置資訊,比如這裡的有訪問 mysql 服務的方法、獲取 root 使用者的密碼以及連線 mysql 的方法等資訊。
值得注意的是 Helm 並不會一直等到所有資源都執行才退出。因為很多 charts 需要的映象資源非常大,所以可能需要很長時間才能安裝到叢集中去。
要跟蹤 release 狀態或重新讀取配置資訊,可以使用 helm status 檢視:
[root@node01 ~]# helm status geared-moth|head
LAST DEPLOYED: Mon Sep 20 16:01:21 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
geared-moth-mysql Pending 4m
==> v1/Service
...
可以看到當前 release 的狀態是DEPLOYED,下面還有一些安裝的時候出現的資訊。
[root@node01 ~]# helm ls
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
geared-moth 1 Mon Sep 20 16:01:21 2021 DEPLOYED mysql-0.10.2 5.7.14
2.4 自定義 chart
上面的安裝方式是使用 chart 的預設配置選項。但是在很多時候,我們都需要自定義 chart 以滿足自身的需求,要自定義 chart,我們就需要知道我們使用的 chart 支援的可配置選項才行。
要檢視 chart 上可配置的選項,使用helm inspect values
命令即可,比如我們這裡檢視上面的 mysql 的配置選項:
[root@node01 ~]# helm inspect values stable/mysql
## mysql image version
## ref: https://hub.docker.com/r/library/mysql/tags/
##
image: "mysql"
imageTag: "5.7.14"
## Specify password for root user
##
## Default: random 10 character string
# mysqlRootPassword: testing
## Create a database user
##
# mysqlUser:
## Default: random 10 character string
# mysqlPassword:
## Allow unauthenticated access, uncomment to enable
##
# mysqlAllowEmptyPassword: true
## Create a database
##
# mysqlDatabase:
## Specify an imagePullPolicy (Required)
## It's recommended to change this to 'Always' if the image tag is 'latest'
## ref: http://kubernetes.io/docs/user-guide/images/#updating-images
##
imagePullPolicy: IfNotPresent
extraVolumes: |
# - name: extras
# emptyDir: {}
extraVolumeMounts: |
# - name: extras
# mountPath: /usr/share/extras
# readOnly: true
extraInitContainers: |
# - name: do-something
# image: busybox
# command: ['do', 'something']
# Optionally specify an array of imagePullSecrets.
# Secrets must be manually created in the namespace.
# ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod
# imagePullSecrets:
# - name: myRegistryKeySecretName
## Node selector
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
nodeSelector: {}
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
readinessProbe:
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
## Persist data to a persistent volume
persistence:
enabled: true
## database data Persistent Volume Storage Class
## If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner. (gp2 on AWS, standard on
## GKE, AWS & OpenStack)
##
# storageClass: "-"
accessMode: ReadWriteOnce
size: 8Gi
annotations: {}
## Configure resource requests and limits
## ref: http://kubernetes.io/docs/user-guide/compute-resources/
##
resources:
requests:
memory: 256Mi
cpu: 100m
# Custom mysql configuration files used to override default mysql settings
configurationFiles: {}
# mysql.cnf: |-
# [mysqld]
# skip-name-resolve
# ssl-ca=/ssl/ca.pem
# ssl-cert=/ssl/server-cert.pem
# ssl-key=/ssl/server-key.pem
# Custom mysql init SQL files used to initialize the database
initializationFiles: {}
# first-db.sql: |-
# CREATE DATABASE IF NOT EXISTS first DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
# second-db.sql: |-
# CREATE DATABASE IF NOT EXISTS second DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
metrics:
enabled: false
image: prom/mysqld-exporter
imageTag: v0.10.0
imagePullPolicy: IfNotPresent
resources: {}
annotations: {}
# prometheus.io/scrape: "true"
# prometheus.io/port: "9104"
livenessProbe:
initialDelaySeconds: 15
timeoutSeconds: 5
readinessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
## Configure the service
## ref: http://kubernetes.io/docs/user-guide/services/
service:
## Specify a service type
## ref: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-types
type: ClusterIP
port: 3306
# nodePort: 32000
ssl:
enabled: false
secret: mysql-ssl-certs
certificates:
# - name: mysql-ssl-certs
# ca: |-
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# cert: |-
# -----BEGIN CERTIFICATE-----
# ...
# -----END CERTIFICATE-----
# key: |-
# -----BEGIN RSA PRIVATE KEY-----
# ...
# -----END RSA PRIVATE KEY-----
## Populates the 'TZ' system timezone environment variable
## ref: https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html
##
## Default: nil (mysql will use image's default timezone, normally UTC)
## Example: 'Australia/Sydney'
# timezone:
# To be added to the database server pod(s)
podAnnotations: {}
然後,我們可以直接在 YAML 格式的檔案中來覆蓋上面的任何配置,在安裝的時候直接使用該配置檔案即可:(config.yaml)
[root@node01 ~]# vim config.yaml
[root@node01 ~]# cat config.yaml
mysqlUser: haimaxyUser
mysqlDatabase: haimaxyDB
service:
type: NodePort
我們這裡通過 config.yaml 檔案定義了 mysqlUser 和 mysqlDatabase,並且把 service 的型別更改為了 NodePort,然後現在我們來安裝的時候直接指定該 yaml 檔案:
[root@node01 ~]# helm install -f config.yaml stable/mysql --name mydb
NAME: mydb
LAST DEPLOYED: Mon Sep 20 16:19:48 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
mydb-mysql-dfc999888-rpp4m 0/1 Pending 0 0s
==> v1/Secret
NAME TYPE DATA AGE
mydb-mysql Opaque 2 0s
==> v1/ConfigMap
NAME DATA AGE
mydb-mysql-test 1 0s
==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mydb-mysql Pending 0s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
mydb-mysql NodePort 10.97.123.216 <none> 3306:31001/TCP 0s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
mydb-mysql 1 1 1 0 0s
NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
mydb-mysql.default.svc.cluster.local
To get your root password run:
MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default mydb-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)
To connect to your database:
1. Run an Ubuntu pod that you can use as a client:
kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il
2. Install the mysql client:
$ apt-get update && apt-get install mysql-client -y
3. Connect using the mysql cli, then provide your password:
$ mysql -h mydb-mysql -p
To connect to your database directly from outside the K8s cluster:
MYSQL_HOST=$(kubectl get nodes --namespace default -o jsonpath='{.items[0].status.addresses[0].address}')
MYSQL_PORT=$(kubectl get svc --namespace default mydb-mysql -o jsonpath='{.spec.ports[0].nodePort}')
mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}
我們可以看到當前 release 的名字已經變成 mydb 了。然後可以檢視下 mydb 關聯的 Service 是否變成 NodePort 型別的了:
[root@node01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
geared-moth-mysql ClusterIP 10.105.138.133 <none> 3306/TCP 19m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 97d
mydb-mysql NodePort 10.97.123.216 <none> 3306:31001/TCP 1m
看到服務 mydb-mysql 變成了 NodePort 型別的,二之前預設建立的 mewing-squid-mysql 是 ClusterIP 型別的,證明上面我們通過 YAML 檔案來覆蓋 values 是成功的。
接下來我們檢視下 Pod 的狀況:
[root@node01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
geared-moth-mysql-58778f7d84-jrx57 0/1 Pending 0 20m
mydb-mysql-dfc999888-rpp4m 0/1 Pending 0 2m
比較奇怪的是之前預設建立的和現在的 mydb 的 release 建立的 Pod 都是 Pending 狀態,直接使用 describe 命令檢視下:
[root@node01 ~]# kubectl describe pod mydb-mysql-dfc999888-rpp4m
Name: mydb-mysql-dfc999888-rpp4m
Namespace: default
Node: <none>
Labels: app=mydb-mysql
pod-template-hash=897555444
Annotations: <none>
Status: Pending
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 16s (x15 over 3m) default-scheduler pod has unbound PersistentVolumeClaims
我們可以發現兩個 Pod 處於 Pending 狀態的原因都是 PVC 沒有被繫結上,所以這裡我們可以通過 storageclass 或者手動建立一個合適的 PV 物件來解決這個問題。
另外為了說明 helm 更新的用法,我們這裡來直接禁用掉資料持久化,可以在上面的 config.yaml 檔案中設定:
persistence:
enabled: false
另外一種方法就是在安裝過程中使用--set
來覆蓋對應的 value 值,比如禁用資料持久化,我們這裡可以這樣來覆蓋:
$ helm install stable/mysql --set persistence.enabled=false --name mydb
2.5 升級
我們這裡將資料持久化禁用掉來對上面的 mydb 進行升級:
[root@node01 ~]# cat config.yaml
mysqlUser: haimaxyUser
mysqlDatabase: haimaxyDB
service:
type: NodePort
persistence:
enabled: false
[root@node01 ~]# helm upgrade -f config.yaml mydb stable/mysql
Release "mydb" has been upgraded. Happy Helming!
LAST DEPLOYED: Mon Sep 20 17:30:24 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
...
可以看到已經變成 DEPLOYED 狀態了,現在我們再去看看 Pod 的狀態呢:
[root@node01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
geared-moth-mysql-58778f7d84-jrx57 0/1 Pending 0 1h
mydb-mysql-6ffc84bbf6-jrkgx 1/1 Running 0 1m
我們看到 mydb 關聯的 Pod 已經變成了 PodInitializing 的狀態,已經不是 Pending 狀態了,同樣的,使用 describe 命令檢視:
[root@node01 ~]# kubectl describe pod mydb-mysql-6ffc84bbf6-jrkgx
Name: mydb-mysql-6ffc84bbf6-jrkgx
Namespace: default
Node: node01/192.168.200.11
Start Time: Mon, 20 Sep 2021 17:30:24 +0800
Labels: app=mydb-mysql
pod-template-hash=2997406692
Annotations: <none>
Status: Running
IP: 10.244.1.7
Controlled By: ReplicaSet/mydb-mysql-6ffc84bbf6
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m default-scheduler Successfully assigned mydb-mysql-6ffc84bbf6-jrkgx to node01
Normal SuccessfulMountVolume 2m kubelet, node01 MountVolume.SetUp succeeded for volume "data"
Normal SuccessfulMountVolume 2m kubelet, node01 MountVolume.SetUp succeeded for volume "default-token-ztfk2"
Normal Pulled 2m kubelet, node01 Container image "busybox:1.25.0" already present on machine
Normal Created 2m kubelet, node01 Created container
Normal Started 2m kubelet, node01 Started container
Normal Pulled 2m kubelet, node01 Container image "mysql:5.7.14" already present on machine
Normal Created 2m kubelet, node01 Created container
Normal Started 2m kubelet, node01 Started container
我們可以看到現在沒有任何關於 PVC 的錯誤資訊了,這是因為我們剛剛更新的版本中就是禁用掉了的資料持久化的,證明 helm upgrade 和 --values 是生效了的。現在我們使用 helm ls 命令檢視先當前的 release:
[root@node01 ~]# helm ls
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
geared-moth 1 Mon Sep 20 16:01:21 2021 DEPLOYED mysql-0.10.2 5.7.14 default
mydb 2 Mon Sep 20 17:30:24 2021 DEPLOYED mysql-0.10.2 5.7.14 default
可以看到 mydb 這個 release 的REVISION已經變成2了,這是因為 release 的版本是遞增的,每次安裝、升級或者回滾,版本號都會加1,第一個版本號始終為1,同樣我們可以使用 helm history 命令檢視 release 的歷史版本:
[root@node01 ~]# helm history mydb
REVISION UPDATED STATUS CHART DESCRIPTION
1 Mon Sep 20 16:19:48 2021 SUPERSEDED mysql-0.10.2 Install complete
2 Mon Sep 20 17:30:24 2021 DEPLOYED mysql-0.10.2 Upgrade complete
當然如果我們要回滾到某一個版本的話,使用 helm rollback 命令即可,比如我們將 mydb 回滾到上一個版本:
[root@node01 ~]# #helm rollback mydb 1
2.6 刪除
上節課我們就學習了要刪除一個 release 直接使用 helm delete 命令就 OK:
[root@node01 ~]# helm delete geared-moth
release "geared-moth" deleted
這將從叢集中刪除該 release,但是這並不代表就完全刪除了,我們還可以通過--deleted
引數來顯示被刪除掉 release:
[root@node01 ~]# helm list --deleted
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
geared-moth 1 Mon Sep 20 16:01:21 2021 DELETED mysql-0.10.2 5.7.14 default
[root@node01 ~]# helm list --all
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
geared-moth 1 Mon Sep 20 16:01:21 2021 DELETED mysql-0.10.2 5.7.14 default
mydb 2 Mon Sep 20 17:30:24 2021 DEPLOYED mysql-0.10.2 5.7.14 default
helm list --all則會顯示所有的 release,包括已經被刪除的
由於 Helm 保留已刪除 release 的記錄,因此不能重新使用 release 名稱。(如果 確實 需要重新使用此 release 名稱,則可以使用此 --replace 引數,但它只會重用現有 release 並替換其資源。)這點是不是和 docker container 的管理比較類似
請注意,因為 release 以這種方式儲存,所以可以回滾已刪除的資源並重新啟用它。
如果要徹底刪除 release,則需要加上--purge
引數:
[root@node01 ~]# helm delete geared-moth --purge
release "geared-moth" deleted
[root@node01 ~]# helm list --deleted
[root@node01 ~]#
[root@node01 ~]# helm list --all
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
mydb 2 Mon Sep 20 17:30:24 2021 DEPLOYED mysql-0.10.2 5.7.14 default
3. Helm 模板之內建函式和Values
上節課和大家一起學習了Helm
的一些常用操作方法,這節課來和大家一起定義一個chart
包,瞭解 Helm 中模板的一些使用方法。
3.1 定義 chart
Helm 的 github 上面有一個比較完整的文件,建議大家好好閱讀下該文件,這裡我們來一起建立一個chart
包。
一個 chart 包就是一個資料夾的集合,資料夾名稱就是 chart 包的名稱,比如建立一個 mychart 的 chart 包:
[root@node01 ~]# helm create mychart
Creating mychart
[root@node01 ~]# tree mychart
mychart
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ └── service.yaml
└── values.yaml
2 directories, 7 files
chart 包的目錄上節課我們就已經學習過了,這裡我們再來仔細看看 templates 目錄下面的檔案:
- NOTES.txt:chart 的 “幫助文字”。這會在使用者執行 helm install 時顯示給使用者。
- deployment.yaml:建立 Kubernetes deployment 的基本 manifest
- service.yaml:為 deployment 建立 service 的基本 manifest
- ingress.yaml: 建立 ingress 物件的資源清單檔案
- _helpers.tpl:放置模板助手的地方,可以在整個 chart 中重複使用
這裡我們明白每一個檔案是幹嘛的就行,然後我們把 templates 目錄下面所有檔案全部刪除掉,這裡我們自己來建立模板檔案:
[root@node01 ~]# rm -rf mychart/templates/*.*
3.2 建立模板
這裡我們來建立一個非常簡單的模板 ConfigMap,在 templates 目錄下面新建一個configmap.yaml
檔案:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
實際上現在我們就有一個可安裝的 chart 包了,通過helm install
命令來進行安裝:
[root@node01 ~]# helm install ./mychart/
NAME: doltish-scorpion
LAST DEPLOYED: Mon Sep 20 23:18:29 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
mychart-configmap 1 0s
在上面的輸出中,我們可以看到我們的 ConfigMap 資源物件已經建立了。然後使用如下命令我們可以看到實際的模板被渲染過後的資原始檔:
[root@node01 ~]# helm get manifest doltish-scorpion
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
現在我們看到上面的 ConfigMap 檔案是不是正是我們前面在模板檔案中設計的,現在我們刪除當前的release
:
[root@node01 ~]# helm delete doltish-scorpion
release "doltish-scorpion" deleted
3.3 新增一個簡單的模板
我們可以看到上面我們定義的 ConfigMap 的名字是固定的,但往往這並不是一種很好的做法,我們可以通過插入 release 的名稱來生成資源的名稱,比如這裡 ConfigMap 的名稱我們希望是:ringed-lynx-configmap,這就需要用到 Chart 的模板定義方法了。
Helm Chart 模板使用的是Go語言模板編寫而成,並添加了Sprig庫中的50多個附件模板函式以及一些其他特殊的函。
需要注意的是kubernetes資源物件的 labels 和 name 定義被限制 63個字元,所以需要注意名稱的定義。
現在我們來重新定義下上面的 configmap.yaml 檔案:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
我們將名稱替換成了{{ .Release.Name }}-configmap
,其中包含在{{
和}}
之中的就是模板指令,{{ .Release.Name }}
將 release 的名稱注入到模板中來,這樣最終生成的 ConfigMap 名稱就是以 release 的名稱開頭的了。這裡的 Release 模板物件屬於 Helm 內建的一種物件,還有其他很多內建的物件,稍後我們將接觸到。
現在我們來重新安裝我們的 Chart 包,注意觀察 ConfigMap 資源物件的名稱:
[root@node01 ~]# helm install ./mychart
NAME: veering-toad
LAST DEPLOYED: Mon Sep 20 23:25:48 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
veering-toad-configmap 1 0s
可以看到現在生成的名稱變成了quoting-zebra-configmap,證明已經生效了,當然我們也可以使用命令helm get manifest quoting-zebra
檢視最終生成的清單檔案的樣子。
3.4 除錯
我們用模板來生成資原始檔的清單,但是如果我們想要除錯就非常不方便了,不可能我們每次都去部署一個release
例項來校驗模板是否正確,所幸的時 Helm 為我們提供了--dry-run --debug
這個可選引數,在執行helm install
的時候帶上這兩個引數就可以把對應的 values 值和生成的最終的資源清單檔案打印出來,而不會真正的去部署一個release
例項,比如我們來除錯上面建立的 chart 包:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '46499'
[debug] SERVER: "127.0.0.1:46499"
[debug] Original chart version: ""
[debug] CHART PATH: /root/mychart
NAME: dunking-manta
REVISION: 1
RELEASED: Mon Sep 20 23:48:30 2021
CHART: mychart-0.1.0
USER-SUPPLIED VALUES:
{}
...
service:
port: 80
type: ClusterIP
tolerations: []
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: dunking-manta-configmap
data:
myvalue: "Hello World"
現在我們使用--dry-run
就可以很容易地測試程式碼了,不需要每次都去安裝一個 release 例項了,但是要注意的是這不能確保 Kubernetes 本身就一定會接受生成的模板,在除錯完成後,還是需要去安裝一個實際的 release 例項來進行驗證的。
3.5 內建物件
剛剛我們使用{{.Release.Name}}
將 release 的名稱插入到模板中。這裡的 Release 就是 Helm 的內建物件,下面是一些常用的內建物件,在需要的時候直接使用就可以:
- Release:這個物件描述了 release 本身。它裡面有幾個物件:
- Release.Name:release 名稱
- Release.Time:release 的時間
- Release.Namespace:release 的 namespace(如果清單未覆蓋)
- Release.Service:release 服務的名稱(始終是 Tiller)。
- Release.Revision:此 release 的修訂版本號,從1開始累加。
- Release.IsUpgrade:如果當前操作是升級或回滾,則將其設定為 true。
- Release.IsInstall:如果當前操作是安裝,則設定為 true。
- Values:從
values.yaml
檔案和使用者提供的檔案傳入模板的值。預設情況下,Values 是空的。 - Chart:
Chart.yaml
檔案的內容。所有的 Chart 物件都將從該檔案中獲取。chart 指南中Charts Guide列出了可用欄位,可以前往檢視。 - Files:這提供對 chart 中所有非特殊檔案的訪問。雖然無法使用它來訪問模板,但可以使用它來訪問 chart 中的其他檔案。請參閱 "訪問檔案" 部分。
- Files.Get 是一個按名稱獲取檔案的函式(.Files.Get config.ini)
- Files.GetBytes 是將檔案內容作為位元組陣列而不是字串獲取的函式。這對於像圖片這樣的東西很有用。
- Capabilities:這提供了關於 Kubernetes 叢集支援的功能的資訊。
- Capabilities.APIVersions 是一組版本資訊。
- Capabilities.APIVersions.Has $version 指示是否在群集上啟用版本(batch/v1)。
- Capabilities.KubeVersion 提供了查詢 Kubernetes 版本的方法。它具有以下值:Major,Minor,GitVersion,GitCommit,GitTreeState,BuildDate,GoVersion,Compiler,和 Platform。
- Capabilities.TillerVersion 提供了查詢 Tiller 版本的方法。它具有以下值:SemVer,GitCommit,和 GitTreeState。
- Template:包含有關正在執行的當前模板的資訊
- Name:到當前模板的檔案路徑(例如 mychart/templates/mytemplate.yaml)
- BasePath:當前 chart 模板目錄的路徑(例如 mychart/templates)。
上面這些值可用於任何頂級模板,要注意內建值始終以大寫字母開頭。這也符合Go的命名約定。當你建立自己的名字時,你可以自由地使用適合你的團隊的慣例。
3.6 values 檔案
上面的內建物件中有一個物件就是 Values,該物件提供對傳入 chart 的值的訪問,Values 物件的值有4個來源:
- chart 包中的 values.yaml 檔案
- 父 chart 包的 values.yaml 檔案
- 通過 helm install 或者 helm upgrade 的
-f
或者--values
引數傳入的自定義的 yaml 檔案(上節課我們已經學習過)- 通過
--set
引數傳入的值
chart 的 values.yaml 提供的值可以被使用者提供的 values 檔案覆蓋,而該檔案同樣可以被--set
提供的引數所覆蓋。
這裡我們來重新編輯 mychart/values.yaml 檔案,將預設的值全部清空,新增一個新的資料:(values.yaml)
[root@node01 ~]# vim mychart/values.yaml
[root@node01 ~]# cat mychart/values.yaml
course: k8s
然後我們在上面的 templates/configmap.yaml 模板檔案中就可以使用這個值了:(configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
course: {{ .Values.course }}
可以看到最後一行我們是通過{{ .Values.course }}
來獲取 course 的值的。現在我們用 debug 模式來檢視下我們的模板會被如何渲染:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '37197'
[debug] SERVER: "127.0.0.1:37197"
[debug] Original chart version: ""
[debug] CHART PATH: /root/mychart
NAME: garish-waterbuffalo
REVISION: 1
RELEASED: Mon Sep 20 23:52:57 2021
CHART: mychart-0.1.0
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
course: k8s
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: garish-waterbuffalo-configmap
data:
myvalue: "Hello World"
course: k8s
我們可以看到 ConfigMap 中 course 的值被渲染成了 k8s,這是因為在預設的 values.yaml 檔案中該引數值為 k8s,同樣的我們可以通過--set
引數來輕鬆的覆蓋 course 的值:
[root@node01 ~]# helm install --dry-run --debug --set course=python ./mychart
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: flippant-hound-configmap
data:
myvalue: "Hello World"
course: python
由於--set
比預設 values.yaml 檔案具有更高的優先順序,所以我們的模板生成為 course: python。
values 檔案也可以包含更多結構化內容,例如,我們在 values.yaml 檔案中可以建立 course 部分,然後在其中新增幾個鍵:
[root@node01 ~]# vim mychart/values.yaml
[root@node01 ~]# cat mychart/values.yaml
course:
k8s: devops
python: django
現在我們稍微修改模板:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
k8s: {{ .Values.course.k8s }}
python: {{ .Values.course.python }}
同樣可以使用 debug 模式檢視渲染結果:
[root@node01 ~]# helm install --dry-run --debug ./mychart
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: truculent-gorilla-configmap
data:
myvalue: "Hello World"
k8s: devops
python: django
可以看到模板中的引數已經被 values.yaml 檔案中的值給替換掉了。雖然以這種方式構建資料是可以的,但我們還是建議保持 value 樹淺一些,平一些,這樣維護起來要簡單一點。
到這裡,我們已經看到了幾個內建物件的使用方法,並用它們將資訊注入到了模板之中。
4. Helm 模板之模板函式與管道
上節課我們學習瞭如何將資訊渲染到模板之中,但是這些資訊都是直接傳入模板引擎中進行渲染的,有的時候我們想要轉換一下這些資料才進行渲染,這就需要使用到 Go 模板語言中的一些其他用法。
4.1 模板函式
比如我們需要從.Values
中讀取的值變成字串的時候就可以通過呼叫quote模板函式來實現:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
k8s: {{ quote .Values.course.k8s }}
python: {{ .Values.course.python }}
模板函式遵循呼叫的語法為:functionName arg1 arg2...
。在上面的模板檔案中,quote .Values.course.k8s
呼叫quote
函式並將後面的值作為一個引數傳遞給它。最終被渲染為:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '41661'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: vested-iguana-configmap
data:
myvalue: "Hello World"
k8s: "devops"
python: django
我們可以看到.Values.course.k8s
被渲染成了字串devops
。上節課我們也提到過 Helm 是一種 Go 模板語言,擁有超過60多種可用的內建函式,一部分是由Go 模板語言本身定義的,其他大部分都是Sprig模板庫提供的一部分,我們可以前往這兩個文件中檢視這些函式的用法。
比如我們這裡使用的quote
函式就是Sprig 模板庫
提供的一種字串函式,用途就是用雙引號將字串括起來,如果需要雙引號"
,則需要新增\
來進行轉義,而squote
函式的用途則是用雙引號將字串括起來,而不會對內容進行轉義。
所以在我們遇到一些需求的時候,首先要想到的是去檢視下上面的兩個模板文件中是否提供了對應的模板函式,這些模板函式可以很好的解決我們的需求。
4.2 管道
模板語言除了提供了豐富的內建函式之外,其另一個強大的功能就是管道的概念。和UNIX
中一樣,管道我們通常稱為Pipeline
,是一個鏈在一起的一系列模板命令的工具,以緊湊地表達一系列轉換。簡單來說,管道是可以按順序完成一系列事情的一種方法。比如我們用管道來重寫上面的 ConfigMap 模板:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
k8s: {{ .Values.course.k8s | quote }}
python: {{ .Values.course.python }}
這裡我們直接呼叫quote
函式,而是調換了一個順序,使用一個管道符|將前面的引數傳送給後面的模板函式:{{ .Values.course.k8s | quote }}
,使用管道我們可以將幾個功能順序的連線在一起,比如我們希望上面的 ConfigMap 模板中的 k8s 的 value 值被渲染後是大寫的字串,則我們就可以使用管道來修改:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
k8s: {{ .Values.course.k8s | upper | quote }}
python: {{ .Values.course.python }}
這裡我們在管道中增加了一個upper
函式,該函式同樣是Sprig 模板庫提供的,表示將字串每一個字母都變成大寫。然後我們用debug
模式來檢視下上面的模板最終會被渲染成什麼樣子:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '44004'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: jaundiced-giraffe-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: django
我們可以看到之前我們的devops
已經被渲染成了"DEVOPS"
了,要注意的是使用管道操作的時候,前面的操作結果會作為引數傳遞給後面的模板函式,比如我們這裡希望將上面模板中的 python 的值渲染為重複出現3次的字串,則我們就可以使用到Sprig 模板庫提供的repeat
函式,不過該函式需要傳入一個引數repeat COUNT STRING
表示重複的次數:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
k8s: {{ .Values.course.k8s | upper | quote }}
python: {{ .Values.course.python | quote | repeat 3 }}
該repeat
函式會將給定的字串重複3次返回,所以我們將得到這個輸出:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '35730'
[debug] SERVER: "127.0.0.1:35730"
[debug] Original chart version: ""
[debug] CHART PATH: /root/mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 7: did not find expected key
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: dining-goose-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "django""django""django"
我們可以看到上面的輸出中 python 對應的值變成了3個相同的字串,這顯然是不符合我們預期的,我們的預期是形成一個字串,而現在是3個字串了,而且上面還有錯誤資訊,根據管道處理的順序,我們將quote
函式放到repeat
函式後面去是不是就可以解決這個問題了:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
k8s: {{ .Values.course.k8s | upper | quote }}
python: {{ .Values.course.python | repeat 3 | quote }}
現在是不是就是先重複3次.Values.course.python
的值,然後呼叫quote
函式:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '34845'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: precise-lionfish-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjango"
現在是不是就正常了,也得到了我們的預期結果,所以我們在使用管道操作的時候一定要注意是按照從前到後一步一步順序處理的。
4.3 default 函式
另外一個我們會經常使用的一個函式是default 函式
:default DEFAULT_VALUE GIVEN_VALUE
。該函式允許我們在模板內部指定預設值,以防止該值被忽略掉了。比如我們來修改上面的 ConfigMap 的模板:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: {{ .Values.hello | default "Hello World" | quote }}
k8s: {{ .Values.course.k8s | upper | quote }}
python: {{ .Values.course.python | repeat 5 | quote }}
由於我們的values.yaml
檔案中只定義了 course 結構的資訊,並沒有定義 hello 的值,所以如果沒有設定預設值的話是得不到{{ .Values.hello }}
的值的,這裡我們為該值定義了一個預設值:Hello World
,所以現在如果在values.yaml
檔案中沒有定義這個值,則我們也可以得到預設值:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '36302'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: exiled-ferret-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjangodjangodjango"
我們可以看到myvalue
值被渲染成了Hello World,證明我們的預設值生效了。
5. Helm 模板之控制流程
模板函式和管道
是通過轉換資訊並將其插入到YAML文
件中的強大方法。但有時候需要新增一些比插入字串更復雜一些的模板邏輯。這就需要使用到模板語言中提供的控制結構了。
控制流程為我們提供了控制模板生成流程的一種能力,Helm 的模板語言提供了以下幾種流程控制:
if/else
條件塊with
指定範圍range
迴圈塊
除此之外,它還提供了一些宣告和使用命名模板段的操作:
define
在模板中宣告一個新的命名模板template
匯入一個命名模板block
聲明瞭一種特殊的可填寫的模板區域
關於命名模板
的相關知識點,我們會在後面的課程中和大家接觸到,這裡我們暫時和大家介紹if/else
、with
、range
這3中控制流程的用法。
5.1 if/else 條件
if/else
塊是用於在模板中有條件地包含文字塊的方法,條件塊的基本結構如下:
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
當然要使用條件塊就得判斷條件是否為真,如果值為下面的幾種情況,則管道的結果為 false:
- 一個布林型別的
假
- 一個數字
零
- 一個
空
的字串- 一個
nil
(空或null
)- 一個空的集合(
map
、slice
、tuple
、dict
、array
)
除了上面的這些情況外,其他所有條件都為真
。
同樣還是以上面的 ConfigMap 模板檔案為例,新增一個簡單的條件判斷,如果 python 被設定為 django,則新增一個web: true
:(tempaltes/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: {{ .Values.hello | default "Hello World" | quote }}
k8s: {{ .Values.course.k8s | upper | quote }}
python: {{ .Values.course.python | repeat 3 | quote }}
{{ if eq .Values.course.python "django" }}web: true{{ end }}
在上面的模板檔案中我們增加了一個條件語句判斷{{ if eq .Values.course.python "django" }}web: true{{ end }}
,其中運算子eq
是判斷是否相等的操作,除此之外,還有ne
、lt
、gt
、and
、or
等運算子都是 Helm 模板已經實現了的,直接使用即可。這裡我們{{ .Values.course.python }}
的值在values.yaml
檔案中預設被設定為了django,所以正常來說下面的條件語句判斷為真
,所以模板檔案最終被渲染後會有web: true
這樣的的一個條目:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '36977'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: dandy-crab-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjango"
web: true
可以看到上面模板被渲染後出現了web: true
的條目,如果我們在安裝的時候覆蓋下 python 的值呢,比如我們改成 ai:
[root@node01 ~]# helm install --dry-run --debug --set course.python=ai ./mychart
[debug] Created tunnel using local port: '45502'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: banking-fish-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "aiaiai"
根據我們模板檔案中的定義,如果{{ .Values.course.python }}
的值為django
的話就會新增web: true
這樣的一個條目,但是現在我們是不是通過引數--set
將值設定為了 ai,所以這裡條件判斷為假,正常來說就不應該出現這個條目了,上面我們通過 debug 模式檢視最終被渲染的值也沒有出現這個條目,證明條件判斷是正確的。
5.2 空格控制
上面我們的條件判斷語句是在一整行中的,如果平時經常寫程式碼的同學可能非常不習慣了,我們一般會將其格式化為更容易閱讀的形式,比如:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: {{ .Values.hello | default "Hello World" | quote }}
k8s: {{ .Values.course.k8s | upper | quote }}
python: {{ .Values.course.python | repeat 3 | quote }}
{{ if eq .Values.course.python "django" }}
web: true
{{ end }}
這樣的話看上去比之前要清晰很多了,但是我們通過模板引擎來渲染一下,會得到如下結果:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '43335'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: pioneering-zebra-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjango"
web: true
我們可以看到渲染出來會有多餘的空行,這是因為當模板引擎執行時,它將一些值渲染過後,之前的指令被刪除,但它之前所佔的位置完全按原樣保留剩餘的空白了,所以就出現了多餘的空行。YAML
檔案中的空格是非常嚴格的,所以對於空格的管理非常重要,一不小心就會導致你的YAML
檔案格式錯誤。
我們可以通過使用在模板標識{{
後面新增破折號和空格{{-
來表示將空白左移,而在}}
前面新增一個空格和破折號-}}
表示應該刪除右邊的空格,另外需要注意的是換行符也是空格!
使用這個語法,我們來修改我們上面的模板檔案去掉多餘的空格:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: {{ .Values.hello | default "Hello World" | quote }}
k8s: {{ .Values.course.k8s | upper | quote }}
python: {{ .Values.course.python | repeat 3 | quote }}
{{- if eq .Values.course.python "django" }}
web: true
{{- end }}
現在我們來檢視上面模板渲染過後的樣子:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '38602'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: quieting-deer-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjango"
web: true
現在是不是沒有多餘的空格了,另外我們需要謹慎使用-}},比如上面模板檔案中:
python: {{ .Values.course.python | repeat 3 | quote }}
{{- if eq .Values.course.python "django" -}}
web: true
{{- end }}
如果我們在if條件後面增加-}},這會渲染成:
python: "djangodjangodjango"web: true
因為-}}
它刪除了雙方的換行符,顯然這是不正確的。
有關模板中空格控制的詳細資訊,請參閱官方 Go 模板文件Official Go template documentation
5.3 使用 with 修改範圍
接下來我們來看下with
關鍵詞的使用,它用來控制變數作用域。還記得之前我們的{{ .Release.xxx }}
或者{{ .Values.xxx }}
嗎?其中的.就是表示對當前範圍的引用,.Values
就是告訴模板在當前範圍中查詢Values
物件的值。而with
語句就可以來控制變數的作用域範圍,其語法和一個簡單的if
語句比較類似:
{{ with PIPELINE }}
# restricted scope
{{ end }}
with
語句可以允許將當前範圍.設定為特定的物件,比如我們前面一直使用的.Values.course
,我們可以使用with
來將.範圍指向.Values.course
:(templates/configmap.yaml)
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: {{ .Values.hello | default "Hello World" | quote }}
{{- with .Values.course }}
k8s: {{ .k8s | upper | quote }}
python: {{ .python | repeat 3 | quote }}
{{- if eq .python "django" }}
web: true
{{- end }}
{{- end }}
可以看到上面我們增加了一個{{- with .Values.course }}xxx{{- end }}
的一個塊,這樣的話我們就可以在當前的塊裡面直接引用.python
和.k8s
了,而不需要進行限定了,這是因為該with
宣告將.指向了.Values.course
,在{{- end }}
後.就會復原其之前的作用範圍了,我們可以使用模板引擎來渲染上面的模板檢視是否符合預期結果。
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '39377'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: intentional-goose-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjango"
web: true
不過需要注意的是在with
宣告的範圍內,此時將無法從父範圍訪問到其他物件了,比如下面的模板渲染的時候將會報錯,因為顯然.Release
根本就不在當前的.
範圍內,當然如果我們最後兩行交換下位置就正常了,因為{{- end }}
之後範圍就被重置了:
{{- with .Values.course }}
k8s: {{ .k8s | upper | quote }}
python: {{ .python | repeat 3 | quote }}
release: {{ .Release.Name }}
{{- end }}
5.4 range 迴圈
如果大家對程式語言熟悉的話,幾乎所有的程式語言都支援類似於for
、foreach
或者類似功能的迴圈機制,在 Helm 模板語言中,是使用range
關鍵字來進行迴圈操作。
我們在values.yaml
檔案中新增上一個課程列表:
[root@node01 ~]# vim mychart/values.yaml
[root@node01 ~]# cat mychart/values.yaml
course:
k8s: devops
python: django
courselist:
- k8s
- python
- search
- golang
現在我們有一個課程列表,修改 ConfigMap 模板檔案來迴圈打印出該列表:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: {{ .Values.hello | default "Hello World" | quote }}
{{- with .Values.course }}
k8s: {{ .k8s | upper | quote }}
python: {{ .python | repeat 3 | quote }}
{{- if eq .python "django" }}
web: true
{{- end }}
{{- end }}
courselist:
{{- range .Values.courselist }}
- {{ . | title | quote }}
{{- end }}
可以看到最下面我們使用了一個range
函式,該函式將會遍歷{{ .Values.courselist }}
列表,迴圈內部我們使用的是一個.
,這是因為當前的作用域就在當前迴圈內,這個.從列表的第一個元素一直遍歷到最後一個元素,然後在遍歷過程中使用了title
和quote
這兩個函式,前面這個函式是將字串首字母變成大寫,後面就是加上雙引號變成字串,所以按照上面這個模板被渲染過後的結果為:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '39867'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: banking-cow-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjango"
web: true
courselist:
- "K8s"
- "Python"
- "Search"
- "Golang"
- "Golang"
我們可以看到courselist
按照我們的要求迴圈出來了。除了 list 或者 tuple,range 還可以用於遍歷具有鍵和值的集合(如map 或 dict),這個就需要用到變數的概念了。
5.5 變數
前面我們已經學習了函式、管理以及控制流程的使用方法,我們知道程式語言中還有一個很重要的概念叫:變數,在 Helm 模板中,使用變數的場合不是特別多,但是在合適的時候使用變數可以很好的解決我們的問題。如下面的模板:
{{- with .Values.course }}
k8s: {{ .k8s | upper | quote }}
python: {{ .python | repeat 3 | quote }}
release: {{ .Release.Name }}
{{- end }}
我們在with
語句塊內添加了一個.Release.Name
物件,但這個模板是錯誤的,編譯的時候會失敗,這是因為.Release.Name
不在該with
語句塊限制的作用範圍之內,我們可以將該物件賦值給一個變數可以來解決這個問題:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- $releaseName := .Release.Name -}}
{{- with .Values.course }}
k8s: {{ .k8s | upper | quote }}
python: {{ .python | repeat 3 | quote }}
release: {{ $releaseName }}
{{- end }}
我們可以看到我們在with
語句上面增加了一句{{- $releaseName := .Release.Name -}}
,其中$releaseName
就是後面的物件的一個引用變數,它的形式就是$name
,賦值操作使用:=
,這樣with
語句塊內部的$releaseName
變數仍然指向的是.Release.Name
,同樣,我們 DEBUG 下檢視結果:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '45820'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: musty-cheetah-configmap
data:
k8s: "DEVOPS"
python: "djangodjangodjango"
release: musty-cheetah
可以看到已經正常了,另外變數在range
迴圈中也非常有用,我們可以在迴圈中用變數來同時捕獲索引的值:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
courselist:
{{- range $index, $course := .Values.courselist }}
- {{ $index }}: {{ $course | title | quote }}
{{- end }}
例如上面的這個列表,我們在range
迴圈中使用$index
和$course
兩個變數來接收後面列表迴圈的索引和對應的值,最終可以得到如下結果:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '46112'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: hasty-pug-configmap
data:
k8s: "DEVOPS"
python: "djangodjangodjango"
release: hasty-pug
courselist:
- 0: "K8s"
- 1: "Python"
- 2: "Search"
- 3: "Golang"
我們可以看到 courselist 下面將索引和對應的值都打印出來了,實際上具有鍵和值的資料結構我們都可以使用range
來迴圈獲得二者的值,比如我們可以對.Values.course
這個字典來進行迴圈:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- range $key, $value := .Values.course }}
{{ $key }}: {{ $value | quote }}
{{- end }}
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '34481'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: gilded-seagull-configmap
data:
k8s: "devops"
python: "django"
直接使用range
迴圈,用變數$key
、$value
來接收欄位.Values.course
的鍵和值。這就是變數在 Helm 模板中的使用方法。
6. Helm模板之命名模板
前面我們學習了一些 Helm 模板中的一些常用使用方法,但是我們都是操作的一個模板檔案,在實際的應用中,很多都是相對比較複雜的,往往會超過一個模板,如果有多個應用模板,我們應該如何進行處理呢?這就需要用到新的概念:命名模板。
命名模板我們也可以稱為子模板,是限定在一個檔案內部的模板,然後給一個名稱。在使用命名模板的時候有一個需要特別注意的是:模板名稱是全域性的,如果我們聲明瞭兩個相同名稱的模板,最後載入的一個模板會覆蓋掉另外的模板,由於子 chart 中的模板也是和頂層的模板一起編譯的,所以在命名的時候一定要注意,不要重名了。
為了避免重名,有個通用的約定就是為每個定義的模板新增上 chart 名稱:{{define "mychart.labels"}}
,define
關鍵字就是用來宣告命名模板的,加上 chart 名稱就可以避免不同 chart 間的模板出現衝突的情況。
6.1 宣告和使用命名模板
使用define
關鍵字就可以允許我們在模板檔案內部建立一個命名模板,它的語法格式如下:
{{ define "ChartName.TplName" }}
# 模板內容區域
{{ end }}
比如,現在我們可以定義一個模板來封裝一個 label 標籤:
{{- define "mychart.labels" }}
labels:
from: helm
date: {{ now | htmlDate }}
{{- end }}
然後我們可以將該模板嵌入到現有的 ConfigMap 中,然後使用template
關鍵字在需要的地方包含進來即可:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
{{- define "mychart.labels" }}
labels:
from: helm
date: {{ now | htmlDate }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
data:
{{- range $key, $value := .Values.course }}
{{ $key }}: {{ $value | quote }}
{{- end }}
我們這個模板檔案被渲染過後的結果如下所示:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '40254'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: singed-cardinal-configmap
labels:
from: helm
date: 2021-09-21
data:
k8s: "devops"
python: "django"
我們可以看到define
區域定義的命名模板被嵌入到了template
所在的區域,但是如果我們將命名模板全都寫入到一個模板檔案中的話無疑也會增大模板的複雜性。
還記得我們在建立 chart 包的時候,templates 目錄下面預設會生成一個_helpers.tpl
檔案嗎?我們前面也提到過 templates 目錄下面除了NOTES.txt
檔案和以下劃線_
開頭命令的檔案之外,都會被當做 kubernetes 的資源清單檔案,而這個下劃線開頭的檔案不會被當做資源清單外,還可以被其他 chart 模板中呼叫,這個就是 Helm 中的partials
檔案,所以其實我們完全就可以將命名模板定義在這些partials
檔案中,預設就是_helpers.tpl
檔案了。
現在我們將上面定義的命名模板移動到 templates/_helpers.tpl 檔案中去:
{{/* 生成基本的 labels 標籤 */}}
{{- define "mychart.labels" }}
labels:
from: helm
date: {{ now | htmlDate }}
{{- end }}
一般情況下面,我們都會在命名模板頭部加一個簡單的文件塊,用/**/
包裹起來,用來描述我們這個命名模板的用途的。
現在我們講命名模板從模板檔案 templates/configmap.yaml 中移除,當然還是需要保留 template 來嵌入命名模板內容,名稱還是之前的 mychart.lables,這是因為模板名稱是全域性的,所以我們可以能夠直接獲取到。我們再用 DEBUG 模式來除錯下是否符合預期?
6.2 模板範圍
上面我們定義的命名模板中,沒有使用任何物件,只是使用了一個簡單的函式,如果我們在裡面來使用 chart 物件相關資訊呢:
[root@node01 ~]# vim mychart/templates/_helpers.tpl
[root@node01 ~]# cat mychart/templates/_helpers.tpl
{{/* 生成基本的 labels 標籤 */}}
{{- define "mychart.labels" }}
labels:
from: helm
date: {{ now | htmlDate }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
{{- end }}
當命名模板被渲染時,它會接收由 template 呼叫時傳入的作用域,我們還要在 template 後面加上作用域範圍即可:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" . }}
data:
{{- range $key, $value := .Values.course }}
{{ $key }}: {{ $value | quote }}
{{- end }}
我們在 template 末尾傳遞了.
,表示當前的最頂層的作用範圍,如果我們想要在命名模板中使用.Values
範圍內的資料,當然也是可以的,現在我們再來渲染下我們的模板:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '34355'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: crazy-lionfish-configmap
labels:
from: helm
date: 2021-09-21
chart: mychart
version: 0.1.0
data:
k8s: "devops"
python: "django"
我們可以看到 chart 的名稱和版本號都已經被正常渲染出來了。
6.3 include 函式
假如現在我們將上面的定義的 labels 單獨提取出來放置到 _helpers.tpl 檔案中:
[root@node01 ~]# vim mychart/templates/_helpers.tpl
[root@node01 ~]# cat mychart/templates/_helpers.tpl
{{/* 生成基本的 labels 標籤 */}}
{{- define "mychart.labels" }}
from: helm
date: {{ now | htmlDate }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
{{- end }}
現在我們將該命名模板插入到 configmap 模板檔案的 labels 部分和 data 部分:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{- template "mychart.labels" . }}
data:
{{- range $key, $value := .Values.course }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- template "mychart.labels" . }}
然後同樣的檢視下渲染的結果:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '44078'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: incendiary-lionfish-configmap
labels:
from: helm
date: 2021-09-21
chart: mychart
version: 0.1.0
data:
k8s: "devops"
python: "django"
from: helm
date: 2021-09-21
chart: mychart
version: 0.1.0
我們可以看到渲染結果是有問題的,不是一個正常的 YAML 檔案格式,這是因為template
只是表示一個嵌入動作而已,不是一個函式,所以原本命名模板中是怎樣的格式就是怎樣的格式被嵌入進來了,比如我們可以在命名模板中給內容區域都空了兩個空格,再來檢視下渲染的結構:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mortal-cricket-configmap
labels:
from: helm
date: 2021-09-21
chart: mychart
version: 0.1.0
data:
k8s: "devops"
python: "django"
from: helm
date: 2021-09-21
chart: mychart
version: 0.1.0
我們可以看到 data 區域裡面的內容是渲染正確的,但是上面 labels 區域是不正常的,因為命名模板裡面的內容是屬於 labels 標籤的,是不符合我們的預期的,但是我們又不可能再去把命名模板裡面的內容再增加兩個空格,因為這樣的話 data 裡面的格式又不符合預期了。
為了解決這個問題,Helm 提供了另外一個方案來代替template
,那就是使用include
函式,在需要控制空格的地方使用indent
管道函式來自己控制,比如上面的例子我們替換成include
函式:
[root@node01 ~]# vim mychart/templates/configmap.yaml
[root@node01 ~]# cat mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{- include "mychart.labels" . | indent 4 }}
data:
{{- range $key, $value := .Values.course }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- include "mychart.labels" . | indent 2 }}
在 labels 區域我們需要4個空格,所以在管道函式indent
中,傳入引數4就可以,而在 data 區域我們只需要2個空格,所以我們傳入引數2即可以,現在我們來渲染下我們這個模板看看是否符合預期呢:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '39001'
...
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ugly-mouse-configmap
labels:
from: helm
date: 2021-09-21
chart: mychart
version: 0.1.0
data:
k8s: "devops"
python: "django"
from: helm
date: 2021-09-21
chart: mychart
version: 0.1.0
可以看到是符合我們的預期,所以在 Helm 模板中我們使用 include 函式要比 template 更好,可以更好地處理 YAML 檔案輸出格式。
7. Helm模板之其他注意事項
上節課我們學習了命名模板的使用,命名模板是 Helm 模板中非常重要的一個功能,在我們實際開發 Helm Chart 包的時候非常有用,到這裡我們基本上就把 Helm 模板中經常使用到的一些知識點和大家介紹完了。但是仍然還是有一些在開發中值得我們注意的一些知識點,比如 NOTES.txt 檔案的使用、子 Chart 的使用、全域性值的使用,這節課我們就來和大家一起了解下這些知識點。
7.1 NOTES.txt 檔案
我們前面在使用 helm install 命令的時候,Helm 都會為我們打印出一大堆介紹資訊,這樣當別的使用者在使用我們的 chart 包的時候就可以根據這些註釋資訊快速瞭解我們的 chart 包的使用方法,這些資訊就是編寫在 NOTES.txt 檔案之中的,這個檔案是純文字的,但是它和其他模板一樣,具有所有可用的普通模板函式和物件。
現在我們在前面的示例中 templates 目錄下面建立一個 NOTES.txt 檔案:
[root@node01 ~]# vim mychart/templates/NOTES.txt
[root@node01 ~]# cat mychart/templates/NOTES.txt
Thank you for installing {{ .Chart.Name }}.
Your release is named {{ .Release.Name }}.
To learn more about the release, try:
$ helm status {{ .Release.Name }}
$ helm get {{ .Release.Name }}
我們可以看到我們在 NOTES.txt 檔案中也使用 Chart 和 Release 物件,現在我們在 mychart 包根目錄下面執行安裝命令檢視是否能夠得到上面的註釋資訊:
[root@node01 ~]# helm install ./mychart
NAME: eloping-fly
LAST DEPLOYED: Tue Sep 21 23:07:31 2021
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
eloping-fly-configmap 6 0s
NOTES:
Thank you for installing mychart.
Your release is named eloping-fly.
To learn more about the release, try:
$ helm status eloping-fly
$ helm get eloping-fly
現在已經安裝成功了,而且下面的註釋部分也被渲染出來了,我們可以看到 NOTES.txt 裡面使用到的模板物件都被正確渲染了。
為我們建立的 chart 包提供一個清晰的 NOTES.txt 檔案是非常有必要的,可以為使用者提供有關如何使用新安裝 chart 的詳細資訊,這是一種非常友好的方式方法。
7.2 子 chart 包
我們到目前為止都只用了一個 chart,但是 chart 也可以有 子 chart 的依賴關係,它們也有自己的值和模板,在學習字 chart 之前,我們需要了解幾點關於子 chart 的說明:
- 子 chart 是獨立的,所以子 chart 不能明確依賴於其父 chart
- 子 chart 無法訪問其父 chart 的值
- 父 chart 可以覆蓋子 chart 的值
- Helm 中有全域性值的概念,可以被所有的 chart 訪問
7.3 建立子 chart
現在我們就來建立一個子 chart,還記得我們在建立 mychart 包的時候,在根目錄下面有一個空資料夾 charts 目錄嗎?這就是我們的子 chart 所在的目錄,在該目錄下面新增一個新的 chart:
[root@node01 ~]# cd mychart/charts/
[root@node01 charts]# helm create mysubchart
Creating mysubchart
[root@node01 charts]# rm -rf mysubchart/templates/*.*
[root@node01 charts]# tree ..
..
├── charts
│ └── mysubchart
│ ├── charts
│ ├── Chart.yaml
│ ├── templates
│ └── values.yaml
├── Chart.yaml
├── templates
│ ├── configmap.yaml
│ ├── config.yaml
│ ├── _helpers.tpl
│ └── NOTES.txt
└── values.yaml
5 directories, 8 files
同樣的,我們將子 chart 模板中的檔案全部刪除了,接下來,我們為子 chart 建立一個簡單的模板和 values 檔案了。
[root@node01 charts]# cat > mysubchart/values.yaml <<EOF
> in: mysub
> EOF
[root@node01 charts]# cat mysubchart/values.yaml
in: mysub
[root@node01 charts]# cat > mysubchart/templates/configmap.yaml <<EOF
> apiVersion: v1
> kind: ConfigMap
> metadata:
> name: {{ .Release.Name }}-configmap2
> data:
> in: {{ .Values.in }}
> EOF
[root@node01 charts]# cat mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap2
data:
in: {{ .Values.in }}
我們上面已經提到過每個子 chart 都是獨立的 chart,所以我們可以單獨給 mysubchart 進行測試:
[root@node01 charts]# helm install --dry-run --debug ./mysubchart
[debug] Created tunnel using local port: '42049'
...
---
# Source: mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cold-peahen-configmap2
data:
in: mysub
我們可以看到正常渲染出了結果。
7.4 值覆蓋
現在 mysubchart 這個子 chart 就屬於 mychart 這個父 chart 了,由於 mychart 是父級,所以我們可以在 mychart 的 values.yaml 檔案中直接配置子 chart 中的值,比如我們可以在 mychart/values.yaml 檔案中新增上子 chart 的值:
[root@node01 ~]# vim mychart/values.yaml
[root@node01 ~]# cat mychart/values.yaml
course:
k8s: devops
python: django
courselist:
- k8s
- python
- search
- golang
mysubchart:
in: parent
注意最後兩行,mysubchart 部分內的任何指令都會傳遞到 mysubchart 這個子 chart 中去的,現在我們在 mychart 根目錄中執行除錯命令,可以檢視到子 chart 也被一起渲染了:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '44314'
...
---
# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: hazy-albatross-configmap2
data:
in: parent
---
# Source: mychart/templates/configmap.yaml
...
我們可以看到子 chart 中的值已經被頂層的值給覆蓋了。但是在某些場景下面我們還是希望某些值在所有模板中都可以使用,這就需要用到全域性 chart 值了。
7.5 全域性值
全域性值可以從任何 chart 或者子 chart中進行訪問使用,values 物件中有一個保留的屬性是Values.global
,就可以被用來設定全域性值,比如我們在父 chart 的 values.yaml 檔案中新增一個全域性值:
[root@node01 ~]# vim mychart/values.yaml
[root@node01 ~]# cat mychart/values.yaml
course:
k8s: devops
python: django
courselist:
- k8s
- python
- search
- golang
mysubchart:
in: parent
global:
allin: helm
我們在 values.yaml 檔案中添加了一個 global 的屬性,這樣的話無論在父 chart 中還是在子 chart 中我們都可以通過{{ .Values.global.allin }}
來訪問這個全域性值了。比如我們在 mychart/templates/configmap.yaml 和 mychart/charts/mysubchart/templates/configmap.yaml 檔案的 data 區域下面都新增上如下內容:
...
data:
allin: {{ .Values.global.allin }}
...
現在我們在 mychart 根目錄下面執行 debug 除錯模式:
[root@node01 ~]# helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '32908'
...
---
# Source: mychart/charts/mysubchart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: joyous-lambkin-configmap
data:
allin: helm
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: joyous-lambkin-configmap
data:
allin: helm
我們可以看到兩個模板中都輸出了allin: helm
這樣的值,全域性變數對於傳遞這樣的資訊非常有用,不過也要注意我們不能濫用全域性值。
另外值得注意的是我們在學習命名模板的時候就提到過父 chart 和子 chart 可以共享模板。任何 chart 中的任何定義塊都可用於其他 chart,所以我們在給命名模板定義名稱的時候添加了 chart 名稱這樣的字首,避免衝突。
8. Helm Hooks
和 Kubernetes 裡面的容器一樣,Helm 也提供了 Hook 的機制,允許 chart 開發人員在 release 的生命週期中的某些節點來進行干預,比如我們可以利用 Hooks 來做下面的這些事情:
- 在載入任何其他 chart 之前,在安裝過程中載入 ConfigMap 或 Secret
- 在安裝新 chart 之前執行作業以備份資料庫,然後在升級後執行第二個作業以恢復資料
- 在刪除 release 之前執行作業,以便在刪除 release 之前優雅地停止服務
值得注意的是 Hooks 和普通模板一樣工作,但是它們具有特殊的註釋,可以使 Helm 以不同的方式使用它們。
Hook 在資源清單中的 metadata 部分用 annotations 的方式進行宣告:
apiVersion: ...
kind: ....
metadata:
annotations:
"helm.sh/hook": "pre-install"
# ...
接下來我們就來和大家介紹下 Helm Hooks 的一些基本使用方法。
8.1 Hooks
在 Helm 中定義瞭如下一些可供我們使用的 Hooks:
- 預安裝
pre-install
:在模板渲染後,kubernetes 建立任何資源之前執行- 安裝後
post-install
:在所有 kubernetes 資源安裝到集群后執行- 預刪除
pre-delete
:在從 kubernetes 刪除任何資源之前執行刪除請求- 刪除後
post-delete
:刪除所有 release 的資源後執行- 升級前
pre-upgrade
:在模板渲染後,但在任何資源升級之前執行- 升級後
post-upgrade
:在所有資源升級後執行- 預回滾
pre-rollback
:在模板渲染後,在任何資源回滾之前執行- 回滾後
post-rollback
:在修改所有資源後執行回滾請求crd-install
:在執行其他檢查之前新增 CRD 資源,只能用於 chart 中其他的資源清單定義的 CRD 資源。
8.2 生命週期
Hooks 允許開發人員在 release 的生命週期中的一些關鍵節點執行一些鉤子函式,我們正常安裝一個 chart 包的時候的生命週期如下所示:
- 使用者執行
helm install foo
- chart 被載入到服務端 Tiller Server 中
- 經過一些驗證,Tiller Server 渲染 foo 模板
- Tiller 將產生的資源載入到 kubernetes 中去
- Tiller 將 release 名稱和其他資料返回給 Helm 客戶端
- Helm 客戶端退出
如果開發人員在 install 的生命週期中定義了兩個 hook:pre-install
和post-install
,那麼我們安裝一個 chart 包的生命週期就會多一些步驟了:
- 使用者執行
helm install foo
- chart 被載入到服務端 Tiller Server 中
- 經過一些驗證,Tiller Server 渲染 foo 模板
- Tiller 將 hook 資源載入到 kubernetes 中,準備執行
pre-install
hook- Tiller 會根據權重對 hook 進行排序(預設分配權重0,權重相同的 hook 按升序排序)
- Tiller 然後載入最低權重的 hook
- Tiller 等待,直到 hook 準備就緒
- Tiller 將產生的資源載入到 kubernetes 中
- Tiller 執行
post-install
hook- Tiller 等待,直到 hook 準備就緒
- Tiller 將 release 名稱和其他資料返回給客戶端
- Helm 客戶端退出
等待 hook 準備就緒,這是一個阻塞的操作,如果 hook 中宣告的是一個 Job 資源,那麼 Tiller 將等待 Job 成功完成,如果失敗,則釋出失敗,在這個期間,Helm 客戶端是處於暫停狀態的。
對於所有其他型別,只要 kubernetes 將資源標記為載入(新增或更新),資源就被視為就緒狀態,當一個 hook 聲明瞭很多資源是,這些資源是被序列執行的。
另外需要注意的是 hook 建立的資源不會作為 release 的一部分進行跟蹤和管理,一旦 Tiller Server 驗證了 hook 已經達到了就緒狀態,它就不會去管它了。
所以,如果我們在 hook 中建立了資源,那麼不能依賴helm delete
去刪除資源,因為 hook 建立的資源已經不受控制了,要銷燬這些資源,需要在pre-delete
或者post-delete
這兩個 hook 函式中去執行相關操作,或者將helm.sh/hook-delete-policy
這個 annotation 新增到 hook 模板檔案中。
8.3 寫一個 hook
上面我們也說了 hook 和普通模板一樣,也可以使用普通的模板函式和常用的一些物件,比如Values
、Chart
、Release
等等,唯一和普通模板不太一樣的地方就是在資源清單檔案中的 metadata 部分會有一些特殊的註釋 annotation。
例如,現在我們來建立一個 hook,在前面的示例 templates 目錄中新增一個 post-install-job.yaml 的檔案,表示安裝後執行的一個 hook:
[root@node01 ~]# vim mychart/templates/post-install-job.yaml
[root@node01 ~]# cat mychart/templates/post-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-post-install-job
lables:
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
annotations:
# 注意,如果沒有下面的這個註釋的話,當前的這個Job就會被當成release的一部分
"helm.sh/hook": post-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
metadata:
name: {{ .Release.Name }}
labels:
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
spec:
restartPolicy: Never
containers:
- name: post-install-job
image: alpine
command: ["/bin/sleep", "{{ default "10" .Values.sleepTime }}"]
上面的 Job 資源中我們新增一個 annotations,要注意的是,如果我們沒有新增下面這行註釋的話,這個資源就會被當成是 release 的一部分資源:
annotations:
"helm.sh/hook": post-install
當然一個資源中我們也可以同時部署多個 hook,比如我們還可以新增一個post-upgrade
的鉤子:
annotations:
"helm.sh/hook": post-install,post-upgrade
另外值得注意的是我們為 hook 定義了一個權重,這有助於建立一個確定性的執行順序,權重可以是正數也可以是負數,但是必須是字串才行。
annotations:
"helm.sh/hook-weight": "-5"
最後還添加了一個刪除 hook 資源的策略:
annotations:
"helm.sh/hook-delete-policy": hook-succeeded
刪除資源的策略可供選擇的註釋值:
hook-succeeded
:表示 Tiller 在 hook 成功執行後刪除 hook 資源hook-failed
:表示如果 hook 在執行期間失敗了,Tiller 應該刪除 hook 資源before-hook-creation
:表示在刪除新的 hook 之前應該刪除以前的 hook
當 helm 的 release 更新時,有可能 hook 資源已經存在於群集中。預設情況下,helm 會嘗試建立資源,並丟擲錯誤"... already exists"。
我們可以選擇 "helm.sh/hook-delete-policy": "before-hook-creation",取代 "helm.sh/hook-delete-policy": "hook-succeeded,hook-failed" 因為:
例如為了手動除錯,將錯誤的 hook 作業資源儲存在 kubernetes 中是很方便的。 出於某種原因,可能有必要將成功的 hook 資源保留在 kubernetes 中。同時,在 helm release 升級之前進行手動資源刪除是不可取的。 "helm.sh/hook-delete-policy": "before-hook-creation" 在 hook 中的註釋,如果在新的 hook 啟動前有一個 hook 的話,會使 Tiller 將以前的release 中的 hook 刪除,而這個 hook 同時它可能正在被其他一個策略使用。
作者:退役小學生 出處:https://www.cnblogs.com/ywb123 本文版權歸作者和部落格園共有,轉載請註明出處,素質很重要!!!