3.1 為什么需要Helm
K8S上的應用對象,都是由特定的資源描述組成,包括deployment、service等。都保存各自文件中或者集中寫到一個配置文件。然后kubectl apply –f 部署。
如果應用只由一個或幾個這樣的服務組成,上面部署方式足夠了。 而對於一個復雜的應用,會有很多類似上面的資源描述文件,例如微服務架構應用,組成應用的服務可能多達十個,幾十個。如果有更新或回滾應用的需求,可能要修改和維護所涉及的大量資源文件,而這種組織和管理應用的方式就顯得力不從心了。 且由於缺少對發布過的應用版本管理和控制,使Kubernetes上的應用維護和更新等面臨諸多的挑戰,主要面臨以下問題:
(1) 如何將這些服務作為一個整體管理 (2) 這些資源文件如何高效復用 (3) 不支持應用級別的版本管理
3.2 Helm介紹
Helm是一個Kubernetes的包管理工具,就像Linux下的包管理器,如yum/apt等,可以很方便的將之前打包好的yaml文件部署到kubernetes上。Helm有3個重要概念如下: (1) helm # 一個命令行客戶端工具,主要用於Kubernetes應用chart的創建、打包、發布和管理。 (2) Chart # 應用描述,一系列用於描述 k8s 資源相關文件的集合。 (3) Release # 基於Chart的部署實體,一個 chart 被 Helm 運行后將會生成對應的一個 release;將在k8s中創建出真實運行的資源對象。
3.3 Helm v3 變化
Helm目前有兩個大版本:v2和v3,2019年11月,Helm團隊發布 v3 版本,v3版本相比v2版本最主要變化如下。
1 架構變化
最明顯的變化是 Tiller
的刪除,並大部分代碼重構。
2 Release
名稱可以在不同命名空間重用
3 支持將 Chart 推送至 Docker 鏡像倉庫中
4 使用JSONSchema驗證chart values
5 其他
(1) 為了更好地協調其他包管理者的措施 Helm CLI
個別更名
helm delete 更名為 helm uninstall
helm inspect 更名為 helm show
helm fetch 更名為 helm pull
但以上舊的命令當前仍能使用。 (2) 移除了用於本地臨時搭建 Chart Repository
的 helm serve
命令。 (3) 自動創建名稱空間 在不存在的命名空間中創建發行版時,Helm 2創建了命名空間。Helm 3遵循其他Kubernetes對象的行為,如果命名空間不存在則返回錯誤。 (4) 不再需要requirements.yaml
, 依賴關系是直接在chart.yaml
中定義。
3.4 Helm客戶端
1 部署Helm客戶端
使用helm很簡單,你只需要下載一個二進制客戶端包即可,會通過kubeconfig配置(通常$HOME/.kube/config)來連接Kubernetes。 Helm客戶端下載地址:https://github.com/helm/helm/releases/
# 下載后解壓移動到/usr/bin/目錄即可
# wget https://get.helm.sh/helm-v3.7.1-linux-amd64.tar.gz
# tar -zxf helm-v3.7.1-linux-amd64.tar.gz
# mv linux-amd64/helm /usr/bin/
2 Helm常用命令
命令 | 描述 |
---|---|
completion | 命令補全,source <(helm completion bash) |
create | 創建一個chart並指定名字 |
dependency | 管理chart依賴 |
get | 下載一個release。可用子命令:all、hooks、manifest、notes、values |
history | 獲取release歷史 |
install | 安裝一個chart |
list | 列出release |
package | 將chart目錄打包到chart存檔文件中 |
pull | 從遠程倉庫中下載chart並解壓到本地 # helm pull stable/mysql --untar |
repo | 添加,列出,移除,更新和索引chart倉庫。可用子命令:add、index、list、remove、update |
rollback | 從之前版本回滾 |
search | 根據關鍵字搜索chart。可用子命令:hub、repo |
show | 查看chart詳細信息。可用子命令:all、chart、readme、values |
status | 顯示已命名版本的狀態 |
template | 本地呈現模板 |
uninstall | 卸載一個release |
upgrade | 更新一個release |
version | 查看helm客戶端版本 |
3 配置Chart倉庫
(1) 常用倉庫 1)官方倉庫網址: https://hub.kubeapps.com/ 2)其它倉庫網址: https://artifacthub.io/
(2) 添加存儲庫並更新
# 添加阿里雲chart存儲倉庫,版本比較老
# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# 更新倉庫
# helm repo update
(3) 查看配置的存儲庫
# helm repo list
(4) 查找chart應用
helm search repo <應用名>
(5) 刪除存儲庫
# helm repo remove <倉庫名>
3.5 Helm基本使用
1 Helm管理應用生命周期
(1) 創建Chart應用示例
# helm create
(2) 部署
# helm install
(3) 更新
# helm upgrade
(4) 回滾
# helm rollback
(5) 卸載
# helm uninstall
2 使用chart部署一個應用
(1) 查找chart
# helm search repo mysql
NAME CHART VERSIONAPP VERSIONDESCRIPTION
aliyun/mysql 0.3.5 Fast, reliable, scalable, and easy to use open-...
aliyun/percona 0.3.0 free, fully compatible, enhanced, open source d...
aliyun/percona-xtradb-cluster0.0.2 5.7.19 free, fully compatible, enhanced, open source d...
aliyun/gcloud-sqlproxy 0.2.3 Google Cloud SQL Proxy
aliyun/mariadb 2.1.6 10.1.31 Fast, reliable, scalable, and easy to use open-...
# 為什么mariadb也在列表中,因為他和mysql有關。
(2) 查看chart的基本信息
# helm show chart aliyun/mysql
(3) 安裝chart應用
# helm install db aliyun/mysql -n default
(4) 安裝后查看chart幫助信息
# helm status db -n default
3 安裝前自定義chart配置選項
(1) chart 上面部署的mysql並沒有成功,這是因為並不是所有的 chart 都能按照默認配置運行成功,可能會需要一些環境依賴,例如PV。 所以我們需要自定義 chart 配置選項(覆蓋values.yaml),安裝過程中有兩種方法可以傳遞配置數據: --values 或 -f # 指定帶有覆蓋的YAML文件,這可以多次指定,最右邊的文件優先。 --set # 在命令行上指定替代,如果兩者都用,--set優先級高
。
# 把chart包下載下來查看詳情
# helm pull aliyun/mysql --untar
# tree mysql/
mysql/
├── Chart.yaml
├── README.md
├── templates
│ ├── configmap.yaml
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── NOTES.txt
│ ├── pvc.yaml
│ ├── secrets.yaml
│ └── svc.yaml
└── values.yaml
1 directory, 10 files
# helm show values ./mysql <=> # cat mysql/values.yaml # 查看yaml模板變量
# helm show chart ./mysql/ <=> # cat mysql/Chart.yaml # 查看chart基本信息
# 由於我這里使用的是k8s版本為1.20,Deployment yaml配置文件需要進行修改,否則無法部署
# vim mysql/templates/deployment.yaml
# cat > config.yaml << EOF
persistence:
enabled: true
accessMode: ReadWriteOnce
size: 8Gi
mysqlUser: "k8s"
mysqlPassword: "123456"
mysqlDatabase: "k8s"
EOF
# kubectl create namespace mysql-db
# 創建MySQL用戶k8s,並授予此用戶訪問新創建的k8s數據庫的權限,並指定資源所在的名稱空間,
# --set使用命令行替代變量,未指定的使用默認值。
# helm install db -f config.yaml -n mysql-db --set persistence.storageClass="managed-nfs-storage" ./mysql/
# kubectl get pod -n mysql-db
NAME READY STATUS RESTARTS AGE
db-mysql-5c695fcdfb-g6jt6 1/1 Running 0 77s
# helm list -n mysql-db
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
db mysql-db 1 2021-10-22 17:17:32.401870823 +0800 CST deployed mysql-0.3.5
# kubectl get svc -n mysql-db
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db-mysql ClusterIP 172.28.104.81 <none> 3306/TCP 2m38s
# yum install mysql -y
# mysql -uk8s -p123456 -h172.28.104.81 k8s
MySQL [k8s]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| k8s |
+--------------------+
2 rows in set (0.00 sec)
# 卸載helm應用(pvc,pv,存儲資源被刪除)
# helm uninstall db -n mysql-db
(2) values.yaml參數格式對應set變量格式表
helm install命令可以從多個來源安裝:chart存儲庫、本地chart存檔(helm install mysql-0.3.5.tgz)、chart目錄(helm install path/to/mysql)、完整的URL(helm install https://example.com/charts/mysql-0.3.5.tgz)
4 構建一個Helm Chart
(1) 自動生成目錄
# helm create mychart
# tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
3 directories, 10 files
1)charts # 目錄里存放這個chart依賴的所有子chart。 2)Chart.yaml # 用於描述這個Chart的基本信息,包括名字、描述信息以及版本等。
3)Templates # 目錄里面存放所有yaml模板文件。
4)NOTES.txt # 用於介紹Chart幫助信息,helm install 部署后展示給用戶。例如:如何使用這個 Chart、列出缺省的設置等。 5)_helpers.tpl # 放置模板助手的地方,可以在整個 chart 中重復使用。 6)values.yaml # 用於存儲 templates 目錄中模板文件中用到變量的值。
(2) 創建Chart后,接下來就是將其部署
# 修改service的type為NodePort,方便實驗
# vim mychart/values.yaml
service:
type: NodePort
port: 80
# helm install web -n default mychart/
(3) 查看Release
# helm list -n default
NAMENAMESPACEREVISIONUPDATED STATUS CHART APP VERSION
web default 1 2021-10-23 23:11:12.33497844 +0800 CSTdeployedmychart-0.1.01.16.0
(4) 查看部署的pod
# kubectl get pod,svc -n default
NAME READY STATUS RESTARTS AGE
pod/nfs-client-provisioner-666969576d-k2pgw 1/1 Running 1 31h
pod/web-mychart-5b58c47cd6-nsk4r 1/1 Running 0 57s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 172.28.0.1 <none> 443/TCP 33d
service/web-mychart NodePort 172.28.48.236 <none> 80:31644/TCP 57s
(5) 打包推送到chart倉庫共享給別人使用
# helm package mychart/
Successfully packaged chart and saved it to: /root/helm_dir/mychart-0.1.0.tgz
5 升級、回滾和刪除
(1) 升級
1)使用Chart升級應用有兩種方法: --values,-f # 指定yaml文件覆蓋values.yaml文件中的值,沒有覆蓋的保持默認 --set # 在命令行上指定覆蓋values.yaml文件中的值,沒有覆蓋的保持默認 注: 如果兩種方法一起使用,--set優先級高 2)發布新版本的chart時,或者當您要更改發布的配置時,可以使用該helm upgrade
命令
# 第一種方式,將nginx服務升級到1.18版本
# vim values.yaml
image:
tag: "1.18"
# helm upgrade -f values.yaml web mychart/ -n default
# 第二種方式,將nginx服務升級到1.19版本
# helm upgrade --set image.tag=1.19 web mychart/ -n default
(2) 回滾 如果在發布后沒有達到預期的效果,則可以使用helm rollback
回滾到之前的版本。 1)查看歷史版本
# helm history web -n default
REVISIONUPDATED STATUS CHART APP VERSIONDESCRIPTION
1 Sat Oct 23 23:28:45 2021supersededmychart-0.1.01.16.0 Install complete
2 Sat Oct 23 23:30:22 2021supersededmychart-0.1.01.16.0 Upgrade complete
3 Sat Oct 23 23:33:15 2021deployed mychart-0.1.01.16.0 Upgrade complete
注:
REVISION 1 表示的是nginx:1.16版本
REVISION 2 表示的是nginx:1.18版本
REVISION 3 表示的是nginx:1.19版本
2)將應用回滾到指定版本
# helm rollback web 2 -n default
Rollback was a success! Happy Helming!
注: 回滾到上一個版本使用如下命令
helm rollback web -n default
(3) 卸載chart應用
# helm uninstall web -n default
release "web" uninstalled
6 Helm工作流程
3.6 Chart模板
1 介紹 Helm最核心的就是模板,即模板化的K8S YAML文件,它本質上就是一個Go的template模板,Helm在Go template模板的基礎上,還會增加 很多東西,如一些自定義的元數據信息、擴展的庫以及一些類似於編程形式的工作流,例如條件語句、管道等等,這些東西都會使得我們的 模板變得更加豐富。 通過模板實現Chart的高效復用,當部署多個應用時,可以將差異化的字段進行模板化,在部署時使用 -f 或者 --set 動態覆蓋默認值,從而適配多個應用。
# cd /root/helm_dir/mychart/
# cat > values.yaml << EOF
replicas: 1
label:
project: ms
app: nginx
image: nginx
imageTag: "1.20"
EOF
# rm -rf templates/*
# cat > templates/deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-deployment
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
project: {{ .Values.label.project }}
app: {{ .Values.label.app }}
template:
metadata:
labels:
project: {{ .Values.label.project }}
app: {{ .Values.label.app }}
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
EOF
2 內置對象
Release對象獲取發布記錄信息。 在上面示例中,模板文件中.Release
、.Values
是Helm內置對象,頂級開頭寫。
(1) Release對象 使用 {{.Release.Name}}
將 release 的名稱插入到模板中,下面是一些常用的內置對象。
Release.Name | release 名稱 |
---|---|
Release.Name | release 的名稱 |
Release.Time | release 的時間 |
Release.Namespace | release 的命名空間 |
Release.Service | release 的服務名稱 |
Release.Revision | release 的修訂版本號,從1開始累加 |
(2) Values對象
1)Values對象是為Chart模板提供值,這個對象的值有3個來源: A、chart 包中的 values.yaml 文件 B、通過 helm install 或者 helm upgrade 的 -f
或者 --values
參數傳入的自定義的 yaml 文件 C、通過 --set
參數傳入的值 2)chart 的 values.yaml 提供的值可以被用戶提供的 values 文件覆蓋,而該文件同樣可以被 --set
提供的參數所覆蓋。
(3) Chart對象
可以通過Chart對象訪問Chart.yaml文件的內容,例如: {{ .Chart.AppVersion }}
3 調試
Helm也提供了--dry-run --debug
調試參數,幫助你驗證模板正確性,在執行helm install
時候帶上這兩個參數就可以把對應的 values值和渲染的資源清單打印出來,而不會真正的去部署一個release。
比如我們來調試上面創建的 chart 包:
# helm install --dry-run --debug web ./mychart/ -n default
install.go:178: [debug] Original chart version: ""
install.go:199: [debug] CHART PATH: /root/helm_dir/mychart
NAME: web
LAST DEPLOYED: Sun Oct 24 19:46:42 2021
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
image: nginx
imageTag: "1.20"
label:
app: nginx
project: ms
replicas: 1
HOOKS:
MANIFEST:
---
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 1
selector:
matchLabels:
project: ms
app: nginx
template:
metadata:
labels:
project: ms
app: nginx
spec:
containers:
- image: nginx:1.20
name: nginx
4 函數與管道
前面講的模塊,其實就是將變量值傳給模板引擎進行渲染,模板引擎還支持對拿到數據進行二次處理。 (1) 常用函數
quote # 將值轉換為字符串,即加雙引號
default # 設置默認值,如果獲取的值為空則為默認值
indent、nindent# 縮進字符串
toYaml # 引用一塊YAML內容
其他函數:
upper、title等
(2) quote,將值轉換為字符串,即加雙引號
例如:從 .Values 中讀取的值變成字符串,可以使用quote
函數實現。 nodeSelector標簽的值用了true正常使用會報錯,這是因為它是關鍵字,需要加引號才可以。
# vim values.yaml
nodeSelector:
gpu: true
# vim templates/deployment.yaml
......
nodeSelector:
disktype: {{ quote .Values.nodeSelector.gpu }}
(3) default,設置默認值,如果獲取的值為空則為默認值 示例:以防止忘記定義而導致模板文件缺少字段無法創建資源,這時可以為字段定義一個默認值。
# vim templates/deployment.yaml
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
注:
這里用到了管道符"|",前面的值傳遞后函數驗證是否為空。
(4) indent和nindent函數都是縮進字符串,主要區別在於nindent會在縮進前多添加一個換行符 示例:
# vim templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: {{ .Release.Name | indent 0 }}
......
(5) toYaml,引用一塊YAML內容
示例:在values.yaml里寫結構化數據,引用內容塊
# vim values.yaml
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
# vim templates/deployment.yaml
......
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
resources: {{ toYaml .Values.resources | nindent 10 }}
(6) 其他函數
# vim templates/deployment.yaml
......
{{ upper .Values.resources }}# 大寫
{{ title .Values.resources }}# 首字母大寫
5 流程控制
流程控制是為模板提供了一種能力,滿足更復雜的數據邏輯處理,Helm模板語言提供的控制語句有,if/else
條件判斷、range
循環、with
指定變量作用域。
(1) 流程控制之 if/else 1)if/else 塊是用於在模板中有條件的包含文本塊的方法
條件判斷,根據不同的條件做不同的行為,語法如下:
{{ if <表達式> }}
# 做某事
{{ else if <表達式> }}
# 做某事
{{ else }}
# 默認
{{ end }}
2)示例 部署一個應用,在沒明確啟用ingress時,默認情況下不啟用。
# vim values.yaml
ingress:
enabled: false
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
hosts:
host: www.lc.com
path: /
tls:
secretName: chart-example-tls
hosts: www.lc.com
# vim templates/ingress.yaml
{{ if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
labels: {{ include "labels" . | nindent 4 }}
name: {{ include "fullname" . }}
annotations: {{ toYaml .Values.ingress.annotations | nindent 4 }}
spec:
{{- if .Values.ingress.tls }}
tls:
- hosts:
- {{ .Values.ingress.tls.hosts }}
secretName: {{ .Values.ingress.tls.secretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.hosts.host }}
http:
paths:
- path: {{ .Values.ingress.hosts.path }}
pathType: Prefix
backend:
service:
name: {{ include "fullname" . }}
port:
number: {{ .Values.service.port }}
{{ end }}
# 測試
# helm install test --set ingress.enabled=true --dry-run ./mychart/
# 如果值為以下幾種情況則為false
一個布爾類型 false
一個數字 0
一個空的字符串
一個 nil(空或 null)
一個空的集合( map、 slice、 tuple、 dict、 array)
除了上面的這些情況外,其他所有條件都為真。
# 條件表達式也支持操作符
eq 等於
ne 不等於
lt 小於
gt 大於
and 邏輯與
or 邏輯或
3)消除判斷空格
示例:如果是一個空的集合則不啟用資源配額
# vim values.yaml
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
# vim templates/deployment.yaml
......
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
{{ if .Values.resources }}
resources: {{ toYaml .Values.resources | nindent 10 }}
{{ end }}
驗證渲染結果:
# helm install test --dry-run ./mychart/
渲染結果會發現有多余的空行,這是因為模板渲染時會將指令刪除,所以原有的位置就空白了,可以使用橫杠"-"消除空行。
"{{-" 表示刪除左邊的所有空格,直到非空格字符,"-}}" 表示刪除右邊的所有空格,還包括換行符、TAB字符。
示例:
# vim templates/deployment.yaml
......
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
{{- if .Values.resources }}
resources: {{ toYaml .Values.resources | nindent 10 }}
{{- end }}
4)判斷一個空的數組
# vim values.yaml
resources: {}
# vim templates/deployment.yaml
......
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
{{- if .Values.resources }}
resources:
{{ toYaml .Values.resources | indent 10 }}
{{- end }}
(2) range
循環: 一般用於遍歷序列結構的數據,例如序列、鍵值。
語法:
{{ range <值> }}
# 引用內容
{{ end }}
示例: 遍歷數據
在 Helm 模板語言中,使用 range 關鍵字來進行循環操作,我們在 values.yaml 文件中添加上一個變量列表
# cat values.yaml
env:
- 1
- 2
- 3
# vim templates/deployment.yaml
......
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
env:
{{- range $k,$v := .Values.env }}
- name: {{ $k }}
value: {{ quote $v }}
{{- end }}
# 循環打印該列表
# vim templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}
data:
test: |
{{- range .Values.env }}
{{ . }}
{{- end }}
注:
循環內部我們使用的是一個".",這是因為當前的作用域就在當前循環內,這個"."引用的是當前讀取的元素。
(3) with with: 控制變量作用域 之前我們的 {{ .Release.xxx }}
或者 {{ .Values.xxx }}
,其中的 .
就是表示對當前范圍的引用, .Values
就是告訴模板在當 前范圍中查找 Values
對象的值,而 with
語句就可以來控制變量的作用域范圍。
語法:
{{ with <值> }}
# 限制范圍
{{ end }}
with
語句可以允許將當前范圍 .
設置為特定的對象,比如使用.Values.nodeSelecotr
,我們可以使用 with
來將.
范圍指向 .Values.nodeSelecotr
。
# vim values.yaml
nodeSelector:
team: a
gpu: yes
# vim templates/deployment.yaml
......
spec:
{{- $ReleaseName := .Release.Name -}}
{{- with .Values.nodeSelector }}
nodeSelector:
team: {{ .team }}
gpu: {{ .gpu }}
test: {{ $ReleaseName }}
{{- end }}
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
......
注:
上面增加了一個"{{- with .Values.label }} xxx {{- end }}"的一個塊,這樣的話就可以在當前的塊里面直接引用".team"
和".gpu"了。"with"是一個循環構造,使用".Values.nodeSelector"中的值,將其轉換為Yaml。
with 塊限制了變量作用域,也就是無法直接引用模板對象,例如".Values"、".Release",with 語句塊內不能再使用
".Release.Name"對象,否則報錯,我們可以將該對象賦值給一個變量來解決這個問題,可以在 with 語句上面增加一句
"{{- $ReleaseName := .Release.Name -}}",其中"$ReleaseName"就是后面的對象的一個引用變量,它的形式就
是"$name",賦值操作使用":=",這樣 with 語句塊內部的"$ReleaseName"變量仍然指向的是".Release.Name"。
# 優化一下
# vim templates/deployment.yaml
......
spec:
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
......
注: toYaml之后的點是循環中".Values.nodeSelector"的當前值
6 變量 變量在實際應用中不多,但有時候結合with
、range
能更好的處理數據。
獲取列表鍵值
# vim values.yaml
env:
NAME: "gateway"
JAVA_OPTS: "-Xmx1G"
hello: world
# vim templates/deployment.yaml
......
spec:
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
env:
{{- range $k,$v := .Values.env }}
- name: {{ $k }}
value: {{ quote $v }}
{{- end }}
注: 上面在 range 循環中使用 "$key" 和 "$value" 兩個變量來接收后面列表循環的鍵和值。
# 輸出結果
env:
- name: JAVA_OPTS
value: "-Xmx1G"
- name: NAME
value: "gateway"
# vim templates/deployment.yaml
......
containers:
- image: {{ .Values.image }}:{{ .Values.imageTag }}
name: nginx
env:
- name: NAME
{{- if eq .Values.env.hello "world" }}
value: true
{{- else }}
value: false
{{- end }}
# 輸出結果
env:
- name: NAME
value: true
7 命名模板 命名模板類似於開發語言中的函數,指一段可以直接被另一段程序或代碼引用的程序或代碼。在編寫chart時,可以將一些重復使用的內容 寫在命名模板文件中供公共使用,這樣可減少重復編寫程序段和簡化代碼結構。命名模塊使用define字段定義,模板中使用template字段 或include字段引入,在templates目錄中默認下划線開頭的文件為公共模板(_helpers.tpl)。
示例: 將資源名稱生成指令放到公共模板文件中作為所有資源名稱
# vim templates/_helpers.tpl
{{- define "fullname" -}}
{{- .Chart.Name }}-{{ .Release.Name }}
{{- end }}
{{- define "labels" -}}
app: {{ template "fullname" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
release: {{ .Release.Name }}
{{- end }}
# vim templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ template "fullname" . }}
labels:
{{- include "labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicas }}
......
# 得到的結果為
apiVersion: apps/v1
kind: Deployment
metadata:
name: mychart-test
labels:
app: mychart-test
chart: mychart-0.1.0
release: test
spec:
replicas: 1
......
template指令是將一個模板包含在另一個模板中的方法,但是template函數不能用於Go模板管道,為了解決該問題,引入了 include 指令。
上面包含一個名為 labels 的模板,然后將值 "." 傳遞給模板,最后將該模板的輸出傳遞給 nindent 函數。
3.7 寫一個通用的Chart
1 創建步驟
(1) 先創建模板示例 helm create demo
(2) 修改Chart.yaml,Values.yaml,參考示例預留變動的字段值
(3) 在templates目錄下准備部署應用所需的yaml文件,並添加指令引用Values.yaml字段
(4) 將重復使用的內容放到命名模板文件中
(5) 使用Chart結合參數部署多個同類服務
2 查看Chart的目錄結構
# tree demo
demo
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ ├── NOTES.txt
│ └── service.yaml
└── values.yaml
1 directory, 7 files
3 Chart.yaml
# cat demo/Chart.yaml
apiVersion: v2
appVersion: 0.1.0
description: java demo
name: demo
type: application
version: 0.1.0
創建Chart時的默認配置:
# egrep -v "^$|^#" mychart/Chart.yaml
apiVersion: v2
# Chart的api版本
name: mychart
# Chart的名稱
description: A Helm chart for Kubernetes
# Chart的描述信息
type: application
# Chart可以是"應用程序"或"庫"Chart。
# 應用程序Chart是一組模板,可以打包到要部署的版本化歸檔中。
# 庫為Chart開發人員提供了有用的實用程序或函數。
# 它們作為應用程序Chart的依賴項包含在其中,用於將這些實用程序和函數注入渲染管道。
# 庫Chart不定義任何模板,因此無法部署。
version: 0.1.0
# 這是Chart版本。
# 每次更改Chart及其模板(包括應用程序版本)時,此版本號應增加。
# 版本應遵循語義版本控制(https://semver.org/)。
appVersion: "1.16.0"
# 這是正在部署的應用程序的版本號。
# 每次更改應用程序時,此版本號都應遞增。
# 版本不應遵循語義版本控制。
# 它們應該反映應用程序正在使用的版本。
# 建議將其與引號一起使用。
4 values.yaml
# cat demo/values.yaml
image:
pullPolicy: IfNotPresent
repository: lizhenliang/java-demo
tag: latest
imagePullSecrets: []
ingress:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 100m
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
enabled: true
host: example.lc.com
tls:
secretName: example-lc-com-tls
nodeSelector: {}
replicaCount: 3
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 100m
memory: 128Mi
service:
port: 80
type: ClusterIP
tolerations: []
5 templates
(1) deployment.yaml
# cat demo/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "demo.fullname" . }}
labels:
{{- include "demo.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "demo.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "demo.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
(2) _helpers.tpl
# cat demo/templates/_helpers.tpl
{{- define "demo.fullname" -}}
{{- .Chart.Name -}}-{{ .Release.Name }}
{{- end -}}
{{/*
公用標簽
*/}}
{{- define "demo.labels" -}}
app: {{ template "demo.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
{{- end -}}
{{/*
標簽選擇器
*/}}
{{- define "demo.selectorLabels" -}}
app: {{ template "demo.fullname" . }}
release: "{{ .Release.Name }}"
{{- end -}}
(3) ingress.yaml
# cat demo/templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: {{ include "demo.fullname" . }}
labels:
{{- include "demo.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
- hosts:
- {{ .Values.ingress.host }}
secretName: {{ .Values.ingress.tls.secretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: /
backend:
serviceName: {{ include "demo.fullname" . }}
servicePort: {{ .Values.service.port }}
{{- end }}
(4) NOTES.txt
# cat demo/templates/NOTES.txt
訪問地址:
{{- if .Values.ingress.enabled }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .Values.ingress.host }}
{{- end }}
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "demo.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- end }}
(5) service.yaml
# cat demo/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: {{ include "demo.fullname" . }}
labels:
{{- include "demo.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "demo.selectorLabels" . | nindent 4 }}