Helm 强大之处就在于它的模板语言与内置对象,通过这些功能可以配置通用复杂的项目模板并实现与实际的配置的值解耦。
内置对象
对象从模板引擎传递到模板中(template目录下的有效文件)。你的代码可以传递对象。甚至有几种方法在模板中创建新对象。对象可以很简单,只有一个值(如 Release.Name)。或者他们可以包含其他对象或函数。例如Files 对象具有一些函数,Files.get等等。
所有的内置对象查看请参考官方文档 https://helm.sh/docs/chart_template_guide/builtin_objects/
Tips:
内置值始终以大写字母开头。这符合Go的命名约定。当你创建自己的名字时,你可以自由地使用适合你的团队的惯例。一些团队,如Kubernetes chart团队,选择仅使用首字母小写字母来区分本地名称与内置名称。
几个常用的对象
Release
这个对象描述了 release 本身。它里面几个常用子对象
Release.Name release 名称,就是通过helm install -n lamp-server ./lamp-chart 指定的的release 名字 “lamp-server”
Release.Time release 创建的时间
Release.Revision release 版本号
Release.IsUpgrade This is set to true if the current operation is an upgrade or rollback.
Release.IsInstall This is set to true if the current operation is an install.
应用
关于此对象的一个最常用的应用就是资源名称的命名,例如在一个Chart中定义了deployment configmap 等资源他们其中的命名可以通过Release.Name 命名,而不是自定义在values.yaml 中,这样减少配置的同时具有唯一标识性和配置的通用性。
cat configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap #如果是deplyment 资源后缀变为-deployment 相应的service 资源就是-service
data:
myvalue: "Hello World"
Values
从 values.yaml 文件或用户提供的文件传入到模板相应的值,相应的helm 命令行中的--set key=value 配置的值会覆盖values.yaml 中的配置也会通过对象Values 传递到模板中,它们之间有优先级的,在values.yaml 文件介绍中有说明。默认情况下,Values 是空的
应用
在chart 目录下的values.yaml 文件中定义值通过Values对象传递到templates下的k8s 资源的模本文件中
cat values.yaml
replicas: 1
imagePullSecrets: regcred
image:
repository: registry.cn-beijing.aliyuncs.com/kouzf
pullPolicy: Always
...
cat templates/deployment.yaml
...
metadata:
...
spec:
containers:
- name: {{ .Release.Name }}
image: {{ .Values.image.repository }}/{{ .Release.Name }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
相应的service.yaml ingress.yaml 中的值都是统一配置在values.yaml 中通过Values 对象传递的。
Files
这提供对 chart 中所有非特殊文件的访问。虽然无法使用它来访问模板,但可以使用它来访问 chart 中的其他文件。
Files.Get 是一个按名称获取文件的函数(.Files.Get config.ini)
Files.GetBytes 是将文件内容作为字节数组而不是字符串获取的函数。这对于像图片这样的东西很有用。
Files.Glob is a function that returns a list of files whose names match the given shell glob pattern.
Files.Lines is a function that reads a file line-by-line. This is useful for iterating over each line in a file.
Files.AsSecrets is a function that returns the file bodies as Base 64 encoded strings.
Files.AsConfig is a function that returns file bodies as a YAML map.
应用:
在模版文件configmap.yaml 中配置的是nginx 的配置文件数据,这些配置可以直接写入模本文件,也可以通过Files 对象把一个普通文件的内容引入进来
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
nginx.conf: {{.Files.Get "files/nginx.conf" | printf "%s" | indent 4}}
可以配制成一个通用模板文件,引入的nginx 配置文件通过Release.Name传入,这样实现了解耦。
#默认的根目录为chart/,所以对应文件为chart/files/nginx.conf ,indent 4表示空格4个,"|"就是管道,与linux 中的管道意义相仿。
Files.Glob 和 AsConfig 得使用
这是一个configmap 作为filebeat 得input 配置文件得示例
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-filebeat
namespace: asdf
data:
{{ (.Files.Glob "configmap/*").AsConfig | indent 2 }}
每个应用得日志输入配置成一个文件,然后读取这些文件把它们得配置存入configmap 得data 中,文件名字为key 内容为value。
Chart
Chart.yaml 文件的内容。任何在 Chart.yaml 的数据将从这里访问。例如 {{.Chart.Name}}-{{.Chart.Version}} 将打印出来 mychart-0.1.0。chart 指南中 Charts Guide 列出了可用字段
Capabilities
这提供了关于 Kubernetes 集群支持的功能的信息
Template
包含有关正在执行的当前模板的信息
Template.Name 当前使用的模板文件,例如 mychart/templates/configmap.yaml
Template.BasePath 当前 chart 模板目录的 namespace 路径(例如 mychart/templates)。
模板函数与管道
函数
使用示例
apiVersion: v1 kind: ConfigMap metadata: name: {{.Release.Name}}-configmap data: myvalue: "Hello World" drink: {{quote .Values.favorite.drink}} food: {{quote .Values.favorite.food}} #quote 就是一个处理字符串的函数,作用是强制转换为字符串
模板函数遵循语法 functionName arg1 arg2...。在上面的代码片段中,quote .Values.favorite.drink 调用 quote 函数并将一个参数传递给它。
常用函数介绍
quote 函数 作用是强制转换为字符串,如上示例default 函数 格式:default DEFAULT_VALUE GIVEN_VALUE。该功能允许在模板内部指定默认值,以防该值被省略。 让我们用它来修改上面的示例: drink: {{.Values.favorite.drink | default "tea" | quote}} #如果values.yaml 等文件未定义该值默认就是tea indent 函数控制缩进,由于yaml 文件格式有着严格的缩进要求,所有经常用到此函数来控制缩进 {{indent 2 "mug:true"}} #mug:true 缩进2格 {{- include "resources" .| indent 10 }} #整个嵌套模板缩进10格 with 限定范围,如果values 里面配置为多层的格式,为了为了方便引用相关参数可以通过with引用使用示例如下(toYaml 的示例) toYaml 函数 以yaml 格式引入整个配置块 # cat values.yaml ... nodeSelector: team: a gpu: yes # cat templates/deployment.yaml {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} #把team: a gpu:yes 整个引入到模板文件。indent 8 表示缩减8空格,nindent 表示每行后面有一个换行符,否则所有配置都会引入到一行。 {{- end }} 运算符函数 对于模板,运算符(eq,ne,lt,gt,and,or 等等)都是已实现的功能。在管道中,运算符可以用圆括号(( 和 ))分组。 将运算符放到声明的前面,后面跟着它的参数,就像使用函数一样。要多个运算符一起使用,将每个函数通过圆括号分隔。
管道
模板语言的强大功能之一是其管道概念。利用 UNIX 的一个概念,管道是一个链接在一起的一系列模板命令的工具,以紧凑地表达一系列转换。换句话说,管道是按顺序完成几件事情的有效方式。
我们用管道重写上面的例子。
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
data:
myvalue: "Hello World"
drink: {{.Values.favorite.drink | quote}}
food: {{.Values.favorite.food | quote}}
在这个例子中,没有调用 quote ARGUMENT,我们调换了顺序。我们使用管道(|)将 “参数” 发送给函数:.Values.favorite.drink | quote
使用管道,我们可以将几个功能链接在一起:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
data:
myvalue: "Hello World"
drink: {{.Values.favorite.drink | quote}}
food: {{.Values.favorite.food | upper | quote}}
模板中的流程控制
if / else
结构如下
{{if PIPELINE}}
# Do something
{{else if OTHER PIPELINE}}
# Do something else
{{else}}
# Default case
{{end}}
如果值为如下情况,则if条件评估为 false。
一个布尔型的假
一个数字零
一个空的字符串
一个 nil(空或 null)
一个空的集合(map,slice,tuple,dict,array)
使用示例
cat values.yaml
...
nginx:
enabled: false
cat templates/deployment
{{/*如果enabled 为true 那么就启用下面的配置*/}}
{{- if .Values.nginx.enabled }}
- name: {{ .Release.Name }}-nginx
image: {{ .Values.image.repository }}/nginx-qa:{{ .Values.image.tagn }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 80
protocol: TCP
resources:
{{- include "resources" .| indent 10 }}
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
{{- end }}
上面配置是基于同一个pod 部署两个容器,一个是应用一个是nginx 的配置的通用模板。也就是说默认是关闭nginx 配置的,如果启用可以通过--set nginx.enabled=true 方式。注意大括号中的“-”是控制空格的另外控制缩进可以通过函数 indent ,例如 {{indent 2 "mug:true"}} 会很有用。
Tips:
{{- xxx }} 花括号里面的中横行“-” 的用途是清除helm 渲染后的yml 文件里面的空行,由于像if 判断这些语句不是k8s里面yml 文件里的配置信息,所以渲染后的yml 文件中if 等语句的位置会产生空行,所以加上“-”就会清除空行
with 指定范围
它控制着变量作用域。回想一下,"." 是对当前范围的引用。因此 .Values 告诉模板在当前范围中查找 Values 对象。with 可以允许将当前范围(.)设置为特定的对象。例如,我们一直在使用的 .Values.favorites。让我们重写我们的 ConfigMap 来改变 . 范围来指向 .Values.favorites:
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}} 注意,现在我们可以引用 .drink 和 .food 无需对其进行限定。这是因为该 with 声明设置 . 为指向 .Values.favorite。在 {{end}} 后 . 复位其先前的范围。 但是请注意!在受限范围内,此时将无法从父范围访问其他对象。例如,下面会报错: {{- with .Values.favorite}} drink: {{.drink | default "tea" | quote}} food: {{.food | upper | quote}} release: {{.Release.Name}} #此处报错 {{- end}} 解决方式: {{- $releaseName := .Release.Name -}} #在with区域外定义要引入的值,在里面再通过变量引入 {{- with .Values.favorite}} drink: {{.drink | default "tea" | quote}} food: {{.food | upper | quote}} release: {{ $releaseName }} #通过定义的变量引入 {{- end}}
range 循环
许多编程语言都支持使用 for 循环,在 Helm 的模板语言中,遍历集合的方式是使用 range 操作
cat values.yaml
favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions
将这个列表打印到我们的 ConfigMap 中:
cat 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}} #"." 就是循环的对象中的单个元素,由此可见range也具有with 一样的功能,可以限定范围。
{{- end}}
循环自建的元祖
有时能快速在模板中创建一个列表,然后遍历该列表是很有用的。Helm 模板有一个功能可以使这个变得简单:tuple。
sizes: |-
{{- range tuple "small" "medium" "large"}}
- {{.}}
{{- end}}
{{/*输出如下*/}}
sizes: |-
- small
- medium
- large
用于类似列表的对象以同时捕获索引和值:
toppings: |-
{{- range $index, $topping := .Values.pizzaToppings}}
{{$index}}: {{ $topping }}
{{- end}}
注意,range 首先是变量,然后是赋值运算符,然后是列表。这将分配整数索引(从零开始)给 $index,值给 $topping。运行它将产生:
对于同时具有键和值的数据结构,我们可以使用 range 来获得两者
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite}}
{{$key}}: {{ $val | quote }}
{{- end}}
#favorite 定义在 values.yaml中,如上文所示。
模板中的变量
在 Helm 模板中,变量是对另一个对象的命名引用。它遵循这个形式 $name。变量被赋予一个特殊的赋值操作符: :=
变量在模板中,它们使用的频率较低。我们将看到如何使用它们来简化代码,并更好地使用 with 和 range。在前面的例子中,我们看到这段代码会失败:
{{- with .Values.favorite}}
drink: {{.drink | default "tea" | quote}}
food: {{.food | upper | quote}}
release: {{.Release.Name}}
{{- end}}
Release.Name 不在该 with 块中限制的范围内。解决范围问题的一种方法是将对象分配给可以在不考虑当前范围的情况下访问的变量。
我们可以使用变量重写上面的 Release.Name
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}}
注意,在我们开始 with 块之前,我们赋值 $relname :=.Release.Name。现在在 with 块内部,$relname 变量仍然指向发布名称。因为变量不受with 范围限制。
变量在 range 循环中使用
它们可以用于类似列表的对象以同时捕获索引和值
toppings: |-
{{- range $index, $topping := .Values.pizzaToppings}}
{{$index}}: {{ $topping }}
{{- end}}
注意,range 首先是变量,然后是赋值运算符,然后是列表。这将分配整数索引(从零开始)给 $index,值给 $topping。运行它将产生:
toppings: |-
0: mushrooms
1: cheese
2: peppers
3: onions
对于同时具有键和值的数据结构,我们可以使用 range 来获得两者。例如,我们可以对 .Values.favorite 像这样循环:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite}}
{{$key}}: {{ $val | quote }}
{{- end}}
变量通常不是 “全局” 的。它们的范围是它们所在的块。之前,我们在模板的顶层赋值 $relname。该变量将在整个模板的范围内起作用。但在我们的最后一个例子中,$key 和 $val 只会在该 {{range...}}{{end}} 块的范围内起作用。
然而有一个变量是全局 $. 这个变量总是指向根上下文,这是全局变量
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-cm
labels:
data:
{{- range $key,$va := .Values.testdata}}
{{$key}}: {{ $va | quote }}
chartname: {{$.Chart.Name}} #不受区域限制
releasename: {{$.Release.Name}}
{{- end}}
命名模板
目前为止,我们只查看了一个文件中声明的一个模板。但是Helm模板语言的强大功能之一是它能够声明多个模板并将它们一起使用。
在一个文件中定义命名模板,然后在别处使用它们。命名模板(有时称为部分或子模板)是限定在一个文件内部的模板,并起一个名称。我们有两种创建方法,以及几种不同的
使用方法。
Notice:
在命名模板时要注意一个重要的细节:模板名称是全局的。如果声明两个具有相同名称的模板,则最后加载一个模板是起作用的模板。由于子 chart 中的模板与顶级模板一起编译,因此注意小心地使用特定 chart 的名称来命名模板。通用的命名约定是为每个定义的模板添加 chart 名称:{{define "mychart.labels"}}。通过使用特定 chart 名称作为前缀,我们可以避免由于同名模板的两个不同 chart 而可能出现的任何冲突。
define 定义命名模板
可以在/templates/*.yaml 文件中定义命名模板也可以在/templates/_helpers.tpl 定义
template/include 使用命名模板
include与template 区别
include 是可以替代template 的更高级的用法,可以增加缩进功能。例如创建一个configmap 它的lable 和 data 下面的数据缩进是不同的,lable 缩进在metadata 下面,所以lable 下面的数据是相比于顶格是缩进四个空格。data 本身就是顶格写的,它下面的数据缩进两个空格即可。
define 定义的命名模板内容如果同时引入到lable 和 data 下面,那么相同的内容但是缩进不同,如果直接用template 引入那么就会原模原样引入不缩进而是顶格引入到yaml 中
这样不符合configmap 的定义语法可能不报错但是不会生效这两部分内容,即使在模本文件中缩进引用{{- template "mychart.labels" . }},依然不生效,所以在引入过程中需要增加空格所以include 可以替代template 增加空格,语法为:{{- include "mychart.labels" .| indent 4 }} 数字4表示缩进几个空格,data 下面为2,labels下面为4
使用示例
cat _helpers.tpl
{{/* 生成基本的资源配置 */}}
{{- define "resources" }}
{{- with .Values.resources}}
limits:
cpu: {{ .limits.cpu }}
memory: {{ .limits.memory }}
requests:
cpu: {{ .requests.cpu }}
memory: {{ .requests.memory }}
{{- end }}
{{- end }}
cat templates/deployment.yaml
...
- name: {{ .Release.Name }}-nginx
image: {{ .Values.image.repository }}/nginx-qa:{{ .Values.image.tagn }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 80
protocol: TCP
resources:
{{- include "resources" .| indent 10 }} #在此处通过命名模板名字引用
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
...
其他
模板中"-"的使用
示例一
...
name: {{- Values.name -}} #话括号里面的横线左边的会删除括号左边的空格,右边的会删除右边的空格,在此处一般不加这俩“-”
...
示例二
...
{{-if Values.nginx.enable }} #此处作用就是删除if 和 end 逻辑语句所占的空行,因为这些流程控制语句不会渲染成实际的yaml 配置,默认保留空行再yaml 文件中,所以需要用“-”删除
...
{{- end }}
...
参考:http://www.imooc.com/article/291355