上節課和大家一起學習了 Helm的一些常用操作方法,這節課來和大家一起定義一個 chart包,了解 Helm 中模板的一些使用方法。
定義 chartHelm 的 github 上面有一個比較完整的文檔,建議大家好好閱讀下該文檔,這裡我們來一起創建一個 chart包。
一個 chart 包就是一個文件夾的集合,文件夾名稱就是 chart 包的名稱,比如創建一個 mychart 的 chart 包:
$ helm create mychart
Creating mychart
$ 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 目錄下面所有文件全部刪除掉,這裡我們自己來創建模板文件:
$ rm -rf mychart/templates/*.*
創建模板這裡我們來創建一個非常簡單的模板 ConfigMap,在 templates 目錄下面新建一個 configmap.yaml文件:
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
實際上現在我們就有一個可安裝的 chart 包了,通過 helm install命令來進行安裝:
$ helm install ./mychart/
NAME: ringed-lynx
LAST DEPLOYED: Fri Sep 7 22:59:22 2018
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
mychart-configmap 1 0s
在上面的輸出中,我們可以看到我們的 ConfigMap 資源對象已經創建了。然後使用如下命令我們可以看到實際的模板被渲染過後的資源文件:
$ helm get manifest ringed-lynx
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
現在我們看到上面的 ConfigMap 文件是不是正是我們前面在模板文件中設計的,現在我們刪除當前的 release:
$ helm delete ringed-lynx
release "ringed-lynx" deleted
添加一個簡單的模板我們可以看到上面我們定義的 ConfigMap 的名字是固定的,但往往這並不是一種很好的做法,我們可以通過插入 release 的名稱來生成資源的名稱,比如這裡 ConfigMap 的名稱我們希望是:ringed-lynx-configmap,這就需要用到 Chart 的模板定義方法了。
Helm Chart 模板使用的是 Go語言模板編寫而成,並添加了 Sprig庫中的50多個附件模板函數以及一些其他特殊的函。
需要注意的是 kubernetes資源對象的 labels 和 name 定義被限制 63個字符,所以需要注意名稱的定義。
現在我們來重新定義下上面的 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 資源對象的名稱:
$ helm install ./mychart
helm install ./mychart/
NAME: quoting-zebra
LAST DEPLOYED: Fri Sep 7 23:20:12 2018
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
quoting-zebra-configmap 1 0s
可以看到現在生成的名稱變成了quoting-zebra-configmap,證明已經生效了,當然我們也可以使用命令 helmgetmanifest quoting-zebra查看最終生成的清單文件的樣子。
調試我們用模板來生成資源文件的清單,但是如果我們想要調試就非常不方便了,不可能我們每次都去部署一個 release實例來校驗模板是否正確,所幸的時 Helm 為我們提供了 --dry-run--debug這個可選參數,在執行 helm install的時候帶上這兩個參數就可以把對應的 values 值和生成的最終的資源清單文件列印出來,而不會真正的去部署一個 release實例,比如我們來調試上面創建的 chart 包:
$ helm install . --dry-run --debug ./mychart
[debug] Created tunnel using local port: '35286'
[debug] SERVER: "127.0.0.1:35286"
[debug] Original chart version: ""
[debug] CHART PATH: /root/course/kubeadm/helm/mychart
NAME: wrapping-bunny
REVISION: 1
RELEASED: Fri Sep 7 23:23:09 2018
CHART: mychart-0.1.0
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
...
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: wrapping-bunny-configmap
data:
myvalue: "Hello World"
現在我們使用 --dry-run就可以很容易地測試代碼了,不需要每次都去安裝一個 release 實例了,但是要注意的是這不能確保 Kubernetes 本身就一定會接受生成的模板,在調試完成後,還是需要去安裝一個實際的 release 實例來進行驗證的。
內置對象剛剛我們使用 {{.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 中的其他文件。請參閱 "訪問文件" 部分。
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的命名約定。當你創建自己的名字時,你可以自由地使用適合你的團隊的慣例。
values 文件上面的內置對象中有一個對象就是 Values,該對象提供對傳入 chart 的值的訪問,Values 對象的值有4個來源:
chart 的 values.yaml 提供的值可以被用戶提供的 values 文件覆蓋,而該文件同樣可以被 --set提供的參數所覆蓋。
這裡我們來重新編輯 mychart/values.yaml 文件,將默認的值全部清空,添加一個新的數據:(values.yaml)
course: k8s
然後我們在上面的 templates/configmap.yaml 模板文件中就可以使用這個值了:(configmap.yaml)
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
course: {{ .Values.course }}
可以看到最後一行我們是通過 {{.Values.course}}來獲取 course 的值的。現在我們用 debug 模式來查看下我們的模板會被如何渲染:
$ helm install --dry-run --debug ./mychart
helm install --dry-run --debug .
[debug] Created tunnel using local port: '33509'
[debug] SERVER: "127.0.0.1:33509"
[debug] Original chart version: ""
[debug] CHART PATH: /root/course/kubeadm/helm/mychart
NAME: nasal-anaconda
REVISION: 1
RELEASED: Sun Sep 9 17:37:52 2018
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: nasal-anaconda-configmap
data:
myvalue: "Hello World"
course: k8s
我們可以看到 ConfigMap 中 course 的值被渲染成了 k8s,這是因為在默認的 values.yaml 文件中該參數值為 k8s,同樣的我們可以通過 --set參數來輕鬆的覆蓋 course 的值:
$ helm install --dry-run --debug --set course=python ./mychart
[debug] Created tunnel using local port: '44571'
.
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: named-scorpion-configmap
data:
myvalue: "Hello World"
course: python
由於 --set 比默認 values.yaml 文件具有更高的優先級,所以我們的模板生成為 course: python。
values 文件也可以包含更多結構化內容,例如,我們在 values.yaml 文件中可以創建 course 部分,然後在其中添加幾個鍵:
course:
k8s: devops
python: django
現在我們稍微修改模板:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
k8s: {{ .Values.course.k8s }}
python: {{ .Values.course.python }}
同樣可以使用 debug 模式查看渲染結果:
$ helm install --dry-run --debug ./mychart
[debug] Created tunnel using local port: '33801'
.
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: exhaling-turtle-configmap
data:
myvalue: "Hello World"
k8s: devops
python: django
可以看到模板中的參數已經被 values.yaml 文件中的值給替換掉了。雖然以這種方式構建數據是可以的,但我們還是建議保持 value 樹淺一些,平一些,這樣維護起來要簡單一點。
到這裡,我們已經看到了幾個內置對象的使用方法,並用它們將信息注入到了模板之中。接下來我們來看看模板引擎中的其他用法,比如函數、管道、控制結構等等的用法。
上面我們學習了如何將信息渲染到模板之中,但是這些信息都是直接傳入模板引擎中進行渲染的,有的時候我們想要轉換一下這些數據才進行渲染,這就需要使用到 Go 模板語言中的一些其他用法。
模板函數比如我們需要從 .Values中讀取的值變成字符串的時候就可以通過調用 quote模板函數來實現:(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函數並將後面的值作為一個參數傳遞給它。最終被渲染為:
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '39405'
.
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: masked-saola-configmap
data:
myvalue: "Hello World"
k8s: "devops"
python: django
我們可以看到 .Values.course.k8s被渲染成了字符串 devops。上節課我們也提到過 Helm 是一種 Go 模板語言,擁有超過60多種可用的內置函數,一部分是由Go 模板語言本身定義的,其他大部分都是 Sprig模板庫提供的一部分,我們可以前往這兩個文檔中查看這些函數的用法。
比如我們這裡使用的 quote函數就是 Sprig模板庫提供的一種字符串函數,用途就是用雙引號將字符串括起來,如果需要雙引號 ",則需要添加 \來進行轉義,而 squote函數的用途則是用雙引號將字符串括起來,而不會對內容進行轉義。
所以在我們遇到一些需求的時候,首先要想到的是去查看下上面的兩個模板文檔中是否提供了對應的模板函數,這些模板函數可以很好的解決我們的需求。
管道模板語言除了提供了豐富的內置函數之外,其另一個強大的功能就是管道的概念。和 UNIX中一樣,管道我們通常稱為 Pipeline,是一個鏈在一起的一系列模板命令的工具,以緊湊地表達一系列轉換。簡單來說,管道是可以按順序完成一系列事情的一種方法。比如我們用管道來重寫上面的 ConfigMap 模板:(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)
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模式來查看下上面的模板最終會被渲染成什麼樣子:
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '46651'
.
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: maudlin-labradoodle-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: django
我們可以看到之前我們的 devops已經被渲染成了 "DEVOPS"了,要注意的是使用管道操作的時候,前面的操作結果會作為參數傳遞給後面的模板函數,比如我們這裡希望將上面模板中的 python 的值渲染為重複出現3次的字符串,則我們就可以使用到 Sprig模板庫提供的 repeat函數,不過該函數需要傳入一個參數 repeat COUNT STRING表示重複的次數:(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次返回,所以我們將得到這個輸出:
helm install --dry-run --debug .
[debug] Created tunnel using local port: '39712'
.
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: piquant-butterfly-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "django""django""django"
我們可以看到上面的輸出中 python 對應的值變成了3個相同的字符串,這顯然是不符合我們預期的,我們的預期是形成一個字符串,而現在是3個字符串了,而且上面還有錯誤信息,根據管道處理的順序,我們將 quote函數放到 repeat函數後面去是不是就可以解決這個問題了:(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函數:
helm install --dry-run --debug .
[debug] Created tunnel using local port: '33837'
.
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: braided-manatee-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjango"
現在是不是就正常了,也得到了我們的預期結果,所以我們在使用管道操作的時候一定要注意是按照從前到後一步一步順序處理的。
default 函數另外一個我們會經常使用的一個函數是 default函數: defaultDEFAULT_VALUE GIVEN_VALUE。該函數允許我們在模板內部指定默認值,以防止該值被忽略掉了。比如我們來修改上面的 ConfigMap 的模板:(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}}的值的,這裡我們為該值定義了一個默認值: HelloWorld,所以現在如果在 values.yaml文件中沒有定義這個值,則我們也可以得到默認值:
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '42670'
.
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: orbiting-hog-configmap
data:
myvalue: "Hello World"
k8s: "DEVOPS"
python: "djangodjangodjangodjangodjango"
我們可以看到 myvalue值被渲染成了Hello World,證明我們的默認值生效了。
下節課我們再來學習 Helm 模板的其他用法,盡請期待.
掃描下面的二維碼(或微信搜索 k8s技術圈)關注我們的微信公眾帳號,在微信公眾帳號中回復 加群 即可加入到我們的 kubernetes 討論群裡面共同學習。