1.概述
Helm是k8s的包管理工具,類似Linux系統常用的 apt、yum等包管理工具。
使用helm可以簡化k8s應用部署
2.基本概念
- Chart:一個 Helm 包,其中包含了運行一個應用所需要的鏡像、依賴和資源定義等,還可能包含 Kubernetes 集群中的服務定義,類似 Homebrew 中的 formula、APT 的 dpkg 或者 Yum 的 rpm 文件。
- Release:在 Kubernetes 集群上運行的 Chart 的一個實例。在同一個集群上,一個 Chart 可以安裝很多次。每次安裝都會創建一個新的 release。例如一個 MySQL Chart,如果想在服務器上運行兩個數據庫,就可以把這個 Chart 安裝兩次。每次安裝都會生成自己的 Release,會有自己的 Release 名稱。
- Repository:用於發布和存儲 Chart 的存儲庫。
3.架構
Chart Install 過程:
- Helm從指定的目錄或者tgz文件中解析出Chart結構信息
- Helm將指定的Chart結構和Values信息通過gRPC傳遞給Tiller
- Tiller根據Chart和Values生成一個Release
- Tiller將Release發送給Kubernetes運行。
Chart Update過程:
- Helm從指定的目錄或者tgz文件中解析出Chart結構信息
- Helm將要更新的Release的名稱和Chart結構,Values信息傳遞給Tiller
- Tiller生成Release並更新指定名稱的Release的History
- Tiller將Release發送給Kubernetes運行
4.安裝helm
helm主要包括helm客戶端和Tiller服務端兩部分,Tiller部署在k8s集群中。
ps: 如果使用阿里雲容器服務kubernetes版,默認已經安裝了helm的服務端(Tiller),只要安裝helm客戶端即可。
可以根據自己的環境從github地址下載對應的安裝包:
下載地址:https://github.com/helm/helm/releases
- windows 64位版: https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-windows-amd64.zip
- linux 64位版:https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-arm64.tar.gz
下載后解壓到自己喜歡的目錄,然后配置下對應的PATH環境變量。
默認情況helm操作k8s集群,需要借助kubectl命令的集群配置,可以參考這里配置kubectl命令-(k8s應用配置詳解),當然也可以直接給helm命令指定--kubeconfig 參數指定k8s集群證書路徑。
#這是通過--kubeconfig參數指定k8s證書的方式操作k8s集群 #下面命令是部署一個名字叫app-demo的應用,helm包在./chart目錄中 /alidata/server/helm-v2.13.1/helm --kubeconfig ./config/test-k8s.conf install app-demo ./chart |
安裝服務端:
使用helm init 命令,可以一鍵安裝。
ps: 關於chart倉庫(Repository),通過helm命令:helm serve 就可以啟動倉庫服務,但是通常很多時候我們每個項目自己的chart包都跟着源碼一起提交到git倉庫,所以這里的chart倉庫不是必須的。
5.基本用法
這里以制作一個簡單的網站應用chart包為例子介紹helm的基本用法。
ps: 這里跳過docker鏡像制作過程,鏡像制作可以參考:Docker基礎教程
5.1.創建chart包
通過helm create命令創建一個新的chart包
例子:
#在當前目錄創建一個myapp chart包
$ helm create myapp
創建完成后,得到的目錄結構如下:
myapp - chart 包目錄名
├── charts - 依賴的子包目錄,里面可以包含多個依賴的chart包
├── Chart.yaml - chart定義,可以定義chart的名字,版本號信息。
├── templates - k8s配置模版目錄, 我們編寫的k8s配置都在這個目錄, 除了NOTES.txt和下划線開頭命名的文件,其他文件可以隨意命名。
│ ├── deployment.yaml
│ ├── _helpers.tpl - 下划線開頭的文件,helm視為公共庫定義文件,主要用於定義通用的子模版、函數等,helm不會將這些公共庫文件的渲染結果提交給k8s處理。
│ ├── ingress.yaml
│ ├── NOTES.txt - chart包的幫助信息文件,執行helm install命令安裝成功后會輸出這個文件的內容。
│ └── service.yaml
└── values.yaml - chart包的參數配置文件,模版可以引用這里參數。
我們要在k8s中部署一個網站應用,需要編寫deployment、service、ingress三個配置文件,剛才通過helm create命令已經創建好了。
5.2.編寫k8s應用部署配置文件
為了演示chart包模版的用法,我們先把deployment、service、ingress三個配置文件的內容清空,重新編寫k8s部署文件。
deployment.yaml 配置文件定義如下:
apiVersion: apps/v1beta2 kind: Deployment metadata: name: myapp #deployment應用名 labels: app: myapp #deployment應用標簽定義 spec: replicas: 1 #pod副本數 selector: matchLabels: app: myapp #pod選擇器標簽 template: metadata: labels: app: myapp #pod標簽定義 spec: containers: - name: myapp #容器名 image: registry-vpc.cn-hangzhou.aliyuncs.com/zhipuzi/cysystem:1.7.9 #鏡像地址 ports: - name: http containerPort: 80 protocol: TCP |
service.yaml定義如下:
apiVersion: v1 kind: Service metadata: name: myapp-svc #服務名 spec: selector: #pod選擇器定義 app: myapp ports: - protocol: TCP port: 80 targetPort: 80 |
ingress.yaml定義如下:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: myapp-ingress #ingress應用名 spec: rules: - host: www.zhipuzi.com #域名 http: paths: - path: / backend: serviceName: myapp-svc #服務名 servicePort: 80 |
5.3.提取k8s應用部署配置文件中的參數,作為chart包參數。
上面已經完成k8s應用部署配置文件的編寫。
為什么要提取上面配置文件中的參數,作為chart包的參數?
思考下面的問題:
我們制作好一個chart包之后,如實現chart包更具有通用性,我們如何換域名?換鏡像地址?改一下應用部署的名字? 部署多套環境(例如:dev環境、test環境分別以不同的應用名字部署一套)
5.2定義的k8s配置文件還不能稱之為模版,都是固定的配置。(這里所說的模版就類似大家平時做前端開發的時候用的模版技術是一個概念)
我們通過提取配置中的參數,注入模版變量,模版表達式將配置文件轉化為模版文件,helm在運行的時候根據參數動態的將模版文件渲染成最終的配置文件。
下面將deployment、service、ingress三個配置文件轉換成模版文件。
ps: {{ }} 兩個花括號包裹的內容為模版表達式,具體含義,后面會說明,這里不用理會。
deployment.yaml 配置模版如下:
apiVersion: apps/v1beta2 kind: Deployment metadata: name: {{ .Release.Name }} #deployment應用名 labels: app: {{ .Release.Name }} #deployment應用標簽定義 spec: replicas: {{ .Values.replicas}} #pod副本數 selector: matchLabels: app: {{ .Release.Name }} #pod選擇器標簽 template: metadata: labels: app: {{ .Release.Name }} #pod標簽定義 spec: containers: - name: {{ .Release.Name }} #容器名 image: {{ .Values.image }}:{{ .Values.imageTag }} #鏡像地址 ports: - name: http containerPort: 80 protocol: TCP |
service.yaml定義如下:
apiVersion: v1 kind: Service metadata: name: {{ .Release.Name }}-svc #服務名 spec: selector: #pod選擇器定義 app: {{ .Release.Name }} ports: - protocol: TCP port: 80 targetPort: 80 |
ingress.yaml定義如下:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: {{ .Release.Name }}-ingress #ingress應用名 spec: rules: - host: {{ .Values.host }} #域名 http: paths: - path: / backend: serviceName: {{ .Release.Name }}-svc #服務名 servicePort: 80 |
values.yaml chart包參數定義:
#域名 host: www.zhipuzi.com #鏡像參數 image: registry-vpc.cn-hangzhou.aliyuncs.com/zhipuzi/cysystem imageTag: 1.7.9 #pod 副本數 replicas:1 |
5.4.通過helm命令安裝/更新應用
安裝應用:
#命令格式: helm install chart包目錄
$ helm install ./myapp
通過命令注入參數
#命令格式: helm install --set key=value chart包目錄
#–set 參數可以指定多個參數,他的值會覆蓋values.yaml定義的值,對象類型數據可以用 . (點)分割屬性名,例子: --set apiAppResources.requests.cpu=1
$ helm install \
--set replicas=2 \
--set host=www.lewaimai.com \
./myapp
更新應用:
#命令格式: helm upgrade release名字 chart包目錄
$ helm upgrade myapp ./myapp
#也可以指定–set參數
$ helm upgrade \
--set replicas=2 \
--set host=www.ltest.com \
myapp ./myapp
#默認情況下,如果release名字不存在,upgrade會失敗,可以加上-i 參數當release不存在的時候則安裝,存在則更新,將install和uprade命令合並。
$ helm upgrade -i \
--set replicas=2 \
myapp ./myapp
6.模版語法
6.1.表達式
模版表達式: {{ 模版表達式 }}
模版表達式: {{- 模版表達式 -}} , 表示去掉表達式輸出結果前面和后面的空格,去掉前面空格可以這么寫{{- 模版表達式 }}, 去掉后面空格 {{ 模版表達式 -}}
6.2.變量
默認情況點( . ), 代表全局作用域,用於引用全局對象。
例子:
#這里引用了全局作用域下的Values對象中的key屬性。
{{ .Values.key }}
helm全局作用域中有兩個重要的全局對象:Values和Release
Values代表的就是values.yaml定義的參數,通過.Values可以引用任意參數。
例子:
{{ .Values.replicaCount }}
#引用嵌套對象例子,跟引用json嵌套對象類似
{{ .Values.image.repository }}
Release代表一次應用發布,下面是Release對象包含的屬性字段:
- Release.Name - release的名字,一般通過Chart.yaml定義,或者通過helm命令在安裝應用的時候指定。
- Release.Time - release安裝時間
- Release.Namespace - k8s名字空間
- Release.Revision - release版本號,是一個遞增值,每次更新都會加一
- Release.IsUpgrade - true代表,當前release是一次更新.
- Release.IsInstall - true代表,當前release是一次安裝
例子:
{{ .Release.Name }}
除了系統自帶的變量,我們自己也可以自定義模版變量。
#變量名以$開始命名, 賦值運算符是 := (冒號+等號)
{{- $relname := .Release.Name -}}
引用自定義變量:
#不需要 . 引用
{{ $relname }}
6.3.函數&管道運算符
調用函數的語法:{{ functionName arg1 arg2... }}
例子:
#調用quote函數,將結果用“”引號包括起來。
{{ quote .Values.favorite.food }}
管道(pipelines)運算符 |
類似linux shell命令,通過管道 | 將多個命令串起來,處理模版輸出的內容。
例子:
#將.Values.favorite.food傳遞給quote函數處理,然后在輸出結果。
{{ .Values.favorite.food | quote }}
#先將.Values.favorite.food的值傳遞給upper函數將字符轉換成大寫,然后專遞給quote加上引號包括起來。
{{ .Values.favorite.food | upper | quote }}
#如果.Values.favorite.food為空,則使用default定義的默認值
{{ .Values.favorite.food | default "默認值" }}
#將.Values.favorite.food輸出5次
{{ .Values.favorite.food | repeat 5 }}
#對輸出結果縮進2個空格
{{ .Values.favorite.food | nindent 2 }}
常用的關系運算符>、 >=、 <、!=、與或非在helm模版中都以函數的形式實現。
關系運算函數定義:
eq 相當於 =
ne 相當於 !=
lt 相當於 <=
gt 相當於 >=
and 相當於 &&
or 相當於 ||
not 相當於 !
例子:
#相當於 if (
.Values.fooString && (.Values.fooString == "foo")
)
{{ if and .Values.fooString (eq .Values.fooString "foo") }}
{{ ... }}
{{ end }}
6.4.流程控制語句
6.4.1. IF/ELSE
語法:
{{ if 條件表達式 }}
# Do something
{{ else if 條件表達式 }}
# Do something else
{{ else }}
# Default case
{{ end }}
例子:
apiVersion: v1 kind: ConfigMap metadata: name: {{ .Release.Name }}-configmap data: myvalue: "Hello World" drink: {{ .Values.favorite.drink | default "tea" | quote }} food: {{ .Values.favorite.food | upper | quote }} {{if eq .Values.favorite.drink "coffee"}} mug: true {{end}} |
6.4.2. with
with主要就是用來修改 . 作用域的,默認 . 代表全局作用域,with語句可以修改.的含義.
語法:
{{ with 引用的對象 }}
這里可以使用 . (點), 直接引用with指定的對象
{{ end }}
例子:
#.Values.favorite是一個object類型
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }} #相當於.Values.favorite.drink
food: {{ .food | upper | quote }}
{{- end }}
ps: 不能在with作用域內使用 . 引用全局對象, 如果非要在with里面引用全局對象,可以先在with外面將全局對象復制給一個變量,然后在with內部使用這個變量引用全局對象。
例子:
{{- $release:= .Release.Name -}} #先將值保存起來
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }} #相當於.Values.favorite.drink
food: {{ .food | upper | quote }}
release: {{ $release }} #間接引用全局對象的值
{{- end }}
6.4.3. range
range主要用於循環遍歷數組類型。
語法1:
#遍歷map類型,用於遍歷鍵值對象
#變量$key代表對象的屬性名,$val代表屬性值
{{- range $key, $val := 鍵值對象 }}
{{ $key }}: {{ $val | quote }}
{{- end}}
語法2:
{{- range 數組 }}
{{ . | title | quote }} # . (點),引用數組元素值。
{{- end }}
例子:
#values.yaml定義 #map類型 favorite: drink: coffee food: pizza #數組類型 pizzaToppings: - mushrooms - cheese - peppers - onions map類型遍歷例子: {{- range $key, $val := .Values.favorite }} {{ $key }}: {{ $val | quote }} {{- end}} 數組類型遍歷例子: {{- range .Values.pizzaToppings}} {{ . | quote }} {{- end}} |
6.5.子模版定義
我們可以在_(下划線)開頭的文件中定義子模版,方便后續復用。
helm create默認為我們創建了_helpers.tpl 公共庫定義文件,可以直接在里面定義子模版,也可以新建一個,只要以下划線開頭命名即可。
子模版語法:
定義模版
{{ define "模版名字" }} 模版內容 {{ end }}
引用模版:
{{ include "模版名字" 作用域}}
例子:
#模版定義 {{- define "mychart.app" -}} app_name: {{ .Chart.Name }} app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}" {{- end -}} apiVersion: v1 kind: ConfigMap metadata: name: {{ .Release.Name }}-configmap labels: {{ include "mychart.app" . | nindent 4 }} #引用mychart.app模版內容,並對輸出結果縮進4個空格 data: myvalue: "Hello World" |
6.6.調試
編寫好chart包的模版之后,我們可以給helm命令加上--debug --dry-run 兩個參數,讓helm輸出模版結果,但是不把模版輸出結果交給k8s處理。
例子:
#helm install命令類似,加上--debug --dry-run兩個參數即可
$ helm upgrade --debug --dry-run -i \
--set replicas=2 \
myapp ./myapp