Helm最核心的就是模板,即模板化的Kubernetes清單文件,模板經過渲染後會被提交到Kubernetes中,本質上就是Go語言的template模板,模板文件位於template/目錄中。
將Kubernetes清單文件中可能經常變動的字段,通過指定一個變量,在安裝的過程中該變量將被值value動態替換掉,這個過程就是模板的渲染。
變量的值定義在values.yaml文件中,該文件中定義了變量的缺省值,但可以在helm install命令中配置新的值來覆蓋缺省值。
對象從模板引擎傳遞到模板中。如使用{{.Release.Name}}將Release的名稱插入到模板中。Release是可以在模板中訪問的頂級對象之一。
對象可以很簡單,只有一個值。或者他們可以包含其他對象或函數。例如,Release對象包含多個對象(如Release.Name),並且Files對象具有一些函數。
Helm內置了多個頂級對象:
- Release:這個對象描述了Release本身,它裏面有幾個對象:
- Release.Name:Release名稱。
- Release.Namespace:Release的Namespace。
- Release.IsUpgrade:如果當前操作是升級或回滾,則將其設置爲true。
- Release.IsInstall:如果當前操作是安裝,則設置爲true。
- Release.Revision:此Release 的修訂版本號。
- Release.Service:渲染此模板的服務,Helm中總是“Helm”。
- Values:從values.yaml文件和用戶提供的文件傳入模板的值。默認情況下,Values是空的。
- Chart:Chart.yaml文件的內容。
- Files:提供對Chart中所有非特殊文件的訪問。雖然您不能使用它來訪問模板,但是可以使用它來訪問Chart圖表中的其他文件。
- Files.Get是用於按名稱獲取文件。
- Files.GetBytes是用於以字節數組而不是字符串的形式獲取文件內容的函數。 這對於諸如圖像之類的東西很有用。
- Files.Glob是一個函數,該函數返回名稱與給定的Shell Glob模式匹配的文件列表。
- Files.Lines是一項功能,可逐行讀取文件。 這對於遍歷文件中的每一行很有用。
- Files.AsSecrets是一個函數,以Base 64編碼的字符串形式返回文件主體。
- Files.AsConfig是一個將文件正文作爲YAML映射返回的函數。
- Capabilities:提供了有關Kubernetes集羣支持哪些功能的信息。
- Capabilities.APIVersions是一組版本。
- Capabilities.APIVersions.Has:$version指示羣集上是否有版本(例如,batch/v1)或資源(例如,apps/v1/Deployment)。
- Capabilities.KubeVersion是Kubernetes版本。
- Capabilities.KubeVersion.Version是Kubernetes版本。
- Capabilities.KubeVersion.Major是Kubernetes的主要版本。
- Capabilities.KubeVersion.Minor是Kubernetes的次要版本。
- Template:包含有關正在執行的當前模板的信息。
- Name:當前模板的命名空間文件路徑(例如mychart/templates/mytemplate.yaml)
- BasePath:當前Chart的模板目錄的命名空間路徑(例如mychart/templates)。
這些值可用於任何頂級模板。內置值始終以大寫字母開頭,這符合Go的命名約定。
四個內置對象之一的Values,該對象提供對傳入Chart的值的訪問。其內容來自四個來源:
- Chart中的values.yaml文件。
- 如果這是一個子Chart,來自父Chart的values.yaml文件。
- values文件通過helm install或helm upgrade的-f標誌傳入文件(helm install -f myvals.yaml ./mychart)。
- 通過–set(helm install --set foo=bar ./mychart)。
上面的列表按照特定的順序排列:values.yaml是默認級別,父級Chart的可以覆蓋該默認級別,而該values.yaml又可以被用戶提供的values文件覆蓋,而該文件又可以被–set參數覆蓋。
我們先創建一個Chart。
helm create mychart
Creating mychart
我們將默認的values.yaml文件內容刪除,並設置一個參數:
vi mychart/values.yaml
favoriteDrink: coffee
爲了簡單起見,刪除templates/目錄中所有文件,並創建configmap.yaml模板文件。
模板指令放在{{和}}塊之間,模板指令{{ .Release.Name }}表示將Release名稱注入模板。可以將傳遞到模板中的值視爲命名空間對象,其中句號".“用來分割每個命名空間元素,前導”."表示當前的作用域,此處表示頂級命名空間。{{ .Release.Name }}表示從頂級命名空間開始,找到Release對象,然後在其中查找名爲Name的對象。{{ .Values.favoriteDrink }}表示從頂級命名空間開始,找到Values對象,然後在其中查找名爲favoriteDrink的對象。
rm -rf mychart/templates/*
vi mychart/templates/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favoriteDrink }}
我們看看渲染的效果。
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: coffee
參數favoriteDrink在默認values.yaml文件中設置爲coffee,但可以在helm template命令中通過加一個–set添標誌來覆蓋,因爲–set的優先級更高。
helm template myrelease mychart --set favoriteDrink=tea
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: tea
values文件也可以包含更多結構化內容。在values.yaml文件中可以創建favorite添加幾個鍵:
vi mychart/values.yaml
favorite:
drink: coffee
food: pizza
vi mychart/templates/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink }}
food: {{ .Values.favorite.food }}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: coffee
food: pizza
在模板文件中有時不想直接引用對象,而是需要對對象做一些轉換,比如將對象作爲字符串引用,可以使用函數quote。
vi mychart/templates/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ quote .Values.favorite.drink }}
food: {{ quote .Values.favorite.food }}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
模板函數遵循語法functionName arg1 arg2…。在上面的代碼段中,quote .Values.favorite.drink調用quote函數並將其傳遞給單個參數。
模板語言的強大功能之一是其管道概念。像Linux中的管道一樣,是將一系列模板命令鏈接在一起以緊湊表示一系列轉換的工具。管道是按順序完成多項工作的有效方式。
vi mychart/templates/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | quote }}
food: {{ .Values.favorite.food | quote }}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
管道符號"|"將前面的對象作爲後面函數的參數,上面的例子我們將.Values.favorite.drink對象通過管道傳遞給quote函數。
管道方便的地方在可以順序將對象在函數中傳遞。
vi mychart/templates/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | repeat 5 | quote }}
food: {{ .Values.favorite.food | upper | quote }}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffeecoffeecoffeecoffeecoffee"
food: "PIZZA"
上例將.Values.favorite.drink對象作爲repeat函數的第二個參數,而第一個參數爲5。
模板中經常需要用到缺省值功能:default DEFAULT_VALUE GIVEN_VALUE,此功能允許在模板內部指定缺省值,沒有定義該對象或者對象爲空時有缺省值對應。
vi mychart/templates/configmap.yaml
---
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 }}
helm template myrelease mychart --set favorite.drink=null
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "tea"
food: "PIZZA"
Helm通過If/Else塊有條件地控制模板中是否包含文本塊。
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
注意if後面是管道,當值爲以下情況時,管道視爲false。
- a boolean false:邏輯值false
- a numeric zero:數字值0
- an empty string:空字符串
- a nil (empty or null)-值爲空或者null
- an empty collection (map, slice, tuple, dict, array):一個空的集合
以下模板根據條件顯示mug: true。
vi mychart/templates/configmap.yaml
---
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 }}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
以上的方式控制mug: true的顯示不易閱讀,嘗試使用縮進。
vi mychart/templates/configmap.yaml
---
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}}
helm template myrelease mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key
在渲染時報錯,實際上模板被渲染爲了如下清單文件,多縮進了兩個空格。
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
重新嘗試縮進設置。
vi mychart/templates/configmap.yaml
---
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}}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
結果發現縮進正確,但卻多出了一個空行。原因是模板渲染時,渲染引擎會直接替換到{{ … }},然後原樣保留之外的所有字符。所以{{if eq .Values.favorite.drink “coffee”}}後面的換行符被保留了,導致了一個空行。
可以使用特殊字符修改模板聲明的大括號語法,以告訴模板引擎填充空白。"{{- “表示刪除左邊的所有空格,直到非空格字符,而” -}}"表示刪除右邊的所有空格。注意,換行符也是空格!當然還包括空格,TAB字符。
vi mychart/templates/configmap.yaml
---
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 }}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
這次縮進完全控制正確了,上面由於空格不顯示出來不容易理解,我們可以使用*替換空格,使用$替換換行符,這樣看的更清楚一些。{{- … }}表示將左邊的$和**刪除。{{ … -}}表示將右邊的$和**刪除。這樣就剩下了中間的$和**,換行符$恰好就是food和mug兩行之間的換行符。
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 }}
當然控制縮進更推薦使用函數indent,更容易理解。
vi mychart/templates/configmap.yaml
---
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" | indent 2 }}
{{- end }}
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
with用來控制變量作用域。前面提過,前導"."是對當前作用域的引用。.Values告訴模板在當前範圍中查找Values對象。
語法with類似於一個簡單的if語句:
{{ with PIPELINE }}
# restricted scope
{{ end }}
作用域是可以更改的,可以將當前作用域"."設置爲特定對象。
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
由於with將當前作用域指定爲.Values.favorite,所以在with塊中,我們直接使用.drink和.food。
注意:在with受限作用域中,是無法父作用域或者頂級作用域訪問其他對象。
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }}
{{- end }}
在with塊中不能引用{{ .Release.Name }},因爲當前作用域不是頂級作用域,是無法找到頂級對象Release的。
在Helm的模板語言中,使用rang來迭代集合。我們在values.yaml中定義一個列表。
vi mychart/values.yaml
favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions
我們將列表展示在configmap中。使用range來迭代列表的值,"."表示迭代的當前值,通過管道傳遞給title函數,將首字母大寫,然後再通過管道傳遞給quote,轉爲字符串。
vi mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
toppings: |-
{{- range .Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}
渲染模板之後可以看出確實迭代List。
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
toppings: |-
- "Mushrooms"
- "Cheese"
- "Peppers"
- "Onions"
順便看看如何在命令行中設置數組參數。values.yaml文件可以不配置任何參數。
vi mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- if .Values.pizzaToppings }}
toppings:
{{ toYaml .Values.pizzaToppings | indent 4 }}
{{ end }}
helm template myrelease mychart \
--set pizzaToppings[0]=chili \
--set pizzaToppings[1]=pork \
--set pizzaToppings[2]=bamboo \
--set pizzaToppings[3]=pineapple
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
toppings: |-
- chili
- pork
- bamboo
- pineapple
再看看另一種數組。values.yaml文件可以不配置任何參數。
vi mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- if .Values.favorites }}
favorites:
{{ toYaml .Values.favorites | indent 4 }}
{{ end }}
helm template myrelease mychart \
--set favorites[0].drink=coffee \
--set favorites[0].food=pizza \
--set favorites[1].drink=tea \
--set favorites[1].food=hotpot
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
favorites:
- drink: coffee
food: pizza
- drink: tea
food: hotpot
參數值中的逗號","需要轉義。
vi mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink }}
helm template myrelease mychart \
--set favorite.drink="coffee\,tea"
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: coffee,tea
在Helm模板中,變量是對另一個對象的命名引用。它遵循$name格式,變量使用特殊的賦值運算符進行賦值":="。
vi mychart/values.yaml
favorite:
drink: coffee
food: pizza
vi mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- $relname := .Release.Name -}}
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $relname }}
{{- end }}
因爲.Release.Name不能在with塊中直接使用,所以在with之前將.Release.Name賦值給變量relname。
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
release: myrelease
Helm支持在一個文件中定義命名模板,然後在其他模板中使用命名模板。命名模板使用define聲明,使用template使用模板。
我們可以在模板文件中使用define創建命名模板,語法如下:
{{ define "MY.NAME" }}
# body of template here
{{ end }}
模板名稱是全局的,如果聲明兩個具有相同名稱的模板,則只會使用的是最後定義的模板。因爲Sub Chart中的模板是與父Chart模板一起編譯的,所以應謹慎命名命名模板。一種流行的命名約定是在每個命名模板前添加Chart名稱{{define “mychart.labels”}},可以避免由於兩個不同的Chart定義了相同名稱的模板而引起的任何衝突。
可以在configmap.yaml中直接定義命名模板,然後使用template使用該命名模板。
vi mychart/templates/configmap.yaml
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
data:
myvalue: "Hello World"
渲染效果如下。
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
labels:
generator: helm
date: 2020-03-29
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
我們先看一下templates/目錄下的文件命名約定:
- NOTES.txt是一個例外,改文件是安裝後的說明文件,不是Kubernetes清單文件。
- 名稱以下劃線"_"開頭的文件不是Kubernetes清單文件。_helpers.tpl就是缺省的命名模板定義文件。
- 除此之外都是Kubernetes清單文件,在安裝時會自動渲染並加載到Kubernetes中。
我們將以上的命名模板移到_helpers.tpl文件中。
vi mychart/templates/_helpers.tpl
{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
刪除configmap.yaml中的命名模板。
vi mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
有相同的渲染效果。
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
labels:
generator: helm
date: 2020-03-29
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
我們也可以使用include使用命名模板。
vi mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- include "mychart.labels" . }}
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
也有相同的渲染效果。
helm template myrelease mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myrelease-configmap
labels:
generator: helm
date: 2020-03-29
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"