内置对象
作者: ryan 发布于: 2025/8/13 更新于: 2025/8/13 字数: 0 字 阅读: 0 分钟
前面我们介绍了 Helm Chart 的一些基本概念和使用,接下来我们重点介绍下 Chart 模板的编写。模板会渲染成 Kubernetes 的资源清单文件,下面我们将来学习下模板的结构,如何使用它们,如何编写 Go 模板以及如何调试。
对象从模板引擎传递到模板中,在代码中可以传递对象,也有一种方法可以在模板宏创建新的对象,比如 tuple 函数。对象可以很简单,也可以包含其他对象或函数。
例如,Release 对象就包含几个对象(比如 Release.Name
),Files 对象就包含几个函数。
常用内置对象
前面提到过我们可以在模板中使用 {{ .Release.Name }}
获取 release 的名称,Release 是我们可以在模板中访问的几个顶级对象之一:
- `Release`:该对象描述了 release 本身的相关信息,它内部有几个对象:
- `Release.Name`:release 名称
- `Release.Namespace`:release 安装到的命名空间
- `Release.IsUpgrade`:如果当前操作是升级或回滚,则该值为 true
- `Release.IsInstall`:如果当前操作是安装,则将其设置为 true
- `Release.Revision`:release 的 revision 版本号,在安装的时候,值为1,每次升级或回滚都会增加
- `Release.Service`:渲染当前模板的服务,在 Helm 上,实际上该值始终为 Helm
- `Values`:从 `values.yaml `文件和用户提供的 values 文件传递到模板的 Values 值,默认情况下,Values 是空的。
- `Chart`:获取 `Chart.yaml` 文件的内容,该文件中的任何数据都可以访问,例如 `.Chart.Name .Chart.Version `可以渲染成 `mychart-0.1.0`,该对象下面可用的字段前面我们已经提到过了。
- `Files`:可以访问 chart 中的所有非特殊文件,虽然无法使用它来访问模板文件,但是可以来访问 chart 中的其他文件。
- `Files.Get`:用于根据名称获取文件(比如 .Files.Get config.ini)
- `Files.GetBytes`:用于以 bytes 数组而不是字符串的形式来获取文件内容的函数,这对于类似于图片之类的东西很有用
- `Files.Glob`:用于返回名称于给定的 shell glob 模式匹配的文件列表
- `Files.Lines`:可以逐行读取文件的函数,对于遍历文件中的每行内容很有用
- `Files.AsSecrets`:将文件内容以 Base64 编码的字符串返回的函数
- `Files.AsConfig`:将文件正文作为 YAML 字典返回的函数
- `Capabilities`:提供了获取有关 Kubernetes 集群支持功能的信息的对象
- `Capabilities.APIVersions`:支持的版本集合
- `Capabilities.APIVersions.Has $version`:判断一个版本(比如 batch/v1)或资源(比如 apps/v1/Deployment)是否可用
- `Capabilities.Kube.Version`:Kubernetes 的版本
- `Capabilities.Kube`:是 Kubernetes 版本的缩写
- `Capabilities.Kube.Major`:Kubernetes 主版本
- `Capabilities.Kube.Minor`:Kubernetes 的次版本
Template:包含当前正在执行的模板的相关信息
Name:当前模板的命名空间文件路径(比如 mychart/templates/mytemplate.yaml)
BaePath:当前 chart 的模板目录的命名空间路径(比如 mychart/templates)
需要注意的是内置的对象始终是以大写字母开头的,这也是符合 Go 的命名约定的,创建自己的名称的时候,可以自由使用适合你团队的约定,一些团队,比如 Kubernetes Charts 团队,选择仅使用首字母小写,以区分本地名称和内置名称,这里我们也会遵循该约定。
Values 文件
前面我们介绍了 Helm 模板提供的内置对象,其中就有一个内置对象 Values,该对象提供对传递到 chart 中的 values 值的访问,其内容主要有4个来源:
- chart 文件中的
values.yaml
文件 - 如果这是子 chart,父 chart 的
values.yaml
文件 - 用
-f
参数传递给helm install
或helm upgrade
的 values 值文件(例如helm install -f myvals.yaml ./mychart
) - 用
--set
传递的各个参数(例如helm install --set foo=bar ./mychart
)
values.yaml
文件是默认值,可以被父 chart 的 values.yaml
文件覆盖,而后者又可以由用户提供的 values 值文件覆盖,而该文件又可以被 --set
参数覆盖。
配置 Values
helm create mychart
values 值文件是纯 YAML 文件,我们可以来编辑 mychart/values.yaml
文件然后编辑 ConfigMap 模板。
删除values.yaml
中的默认设置后,我们将只设置一个参数:
favoriteDrink: coffee
现在我们可以在模板中直接使用它:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favoriteDrink }}
可以看到在最后一行我们将 favoriteDrink
作为 Values 的属性进行访问:{{ .Values.favoriteDrink }}。
我们可以来看看是如何渲染的:
$ helm install --generate-name --dry-run --debug ./mychart
install.go:148: [debug] Original chart version: ""
install.go:165: [debug] CHART PATH: /Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
NAME: mychart-1575963545
LAST DEPLOYED: Tue Dec 10 15:39:06 2019
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
favoriteDrink: coffee
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575963545-configmap
data:
myvalue: "Hello World"
drink: coffee
由于在默认的 values.yaml
文件中将 favoriteDrink
设置为了 coffee
,所以这就是模板中显示的值,我们可以通过在调用 helm install
的过程中添加 --set
参数来覆盖它:
$ helm install --generate-name --dry-run --debug --set favoriteDrink=slurm ./mychart
install.go:148: [debug] Original chart version: ""
install.go:165: [debug] CHART PATH: /Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
NAME: mychart-1575963760
LAST DEPLOYED: Tue Dec 10 15:42:43 2019
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
favoriteDrink: slurm
COMPUTED VALUES:
favoriteDrink: slurm
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575963760-configmap
data:
myvalue: "Hello World"
drink: slurm
因为--set
的优先级高于默认的 values.yaml
文件,所以我们的模板会生成drink: slurm
。Values 值文件也可以包含更多结构化的内容,例如我们可以在 values.yaml
文件中创建一个 favorite 的部分,然后在其中添加几个 keys:
favorite:
drink: coffee
food: pizza
现在我们再去修改下我们的模板:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink }}
food: {{ .Values.favorite.food }}
虽然我们可以通过这种方式来构造数据,但是还是建议你将 values 树保持更浅,这样在使用的时候更加简单。
当我们考虑为子 chart 分配 values 值的时候,我们就可以看到如何使用树形结构来命名 values 值了。
删除默认 KEY
如果你需要从默认值中删除 key,则可以将该 key 的值覆盖为 null,在这种情况下,Helm 将从覆盖的 values 中删除该 key。
例如,在 Drupal chart 中配置一个 liveness 探针:
livenessProbe:
httpGet:
path: /user/login
port: http
initialDelaySeconds: 120
如果你想使用 --set livenessProbe.exec.command=[cat, docroot/CHANGELOG.txt]
将 livenessProbe 的处理程序覆盖为 exec 而不是 httpGet,则 Helm 会将默认键和覆盖键合并在一起,如下所示:
livenessProbe:
httpGet:
path: /user/login
port: http
exec:
command:
- cat
- docroot/CHANGELOG.txt
initialDelaySeconds: 120
但是,这样却有一个问题,因为你不能声明多个 livenessProbe 处理程序,为了解决这个问题,你可以让 Helm 通过将 livenessProbe.httpGet
设置为 null 来删除它:
$helm install stable/drupal --set image=my-registry/drupal:0.1.0 --set livenessProbe.exec.command=[cat, docroot/CHANGELOG.txt] --set livenessProbe.httpGet=null
到这里我们已经了解到了几个内置对象,并利用它们将信息注入到了模板中,现在我们来看看模板引擎的另外方面:函数和管道。
函数和管道
现在我们已经了解了如何将信息加入到模板中,但是这些信息都是直接原样的放置过去的,有时候,我们希望以一种对我们更有用的方式来转换提供的数据。
下面让我们从一个最佳实践开始:将 .Values
对象中的字符串注入模板时,我们应该引用这些字符串,我们可以通过在 template 指令中调用 quote 函数来实现,比如:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ quote .Values.favorite.drink }} #{{ 函数名 参数}}
food: {{ quote .Values.favorite.food }}
#执行渲染
helm install --generate-name --dry-run ./mychart --set favorite.food-rice
模板函数遵循的语法规则是 functionName arg1 arg2...
,在上面的代码片段中,quote .Values.favorite.drink
会调用 quote 函数并传递一个单个参数。
Helm 有60多种可用的函数,其中一些是由 Go 模板语言本身定义的,其他大多数都是 Sprig 模板库提供的,接下来我们会通过部分示例来逐步介绍其中的一些功能函数。
Helm 模板 当我们谈论 Helm 模板语言 的时候,就好像是特定于 Helm 一样,但实际上它是 Go 模板语言加上一些额外的函数以及各种封装程序的组合,以将某些对象暴露给模板。当我们需要学习模板的时候,Go 模板上有许多资源会对我们有所帮助的。
管道
模板语言有一个强大的功能就是管道(Pipeline)概念,管道利用 UNIX 的概念,将一系列模板命令链接在一起,一起对外提供服务,换句话说,管道是按顺序完成多项工作的有效方式,我们来使用管道重写上面的示例模板:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | quote | upper }} # {{ 参数 | 函数名1 | 函数名2 }}
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 }}
"管道顺序"
反转顺序是模板中常见的做法,我们会看到 .val | quote 比 quote .val 用法更多,虽然两种方法都是可以的。
最后,模板渲染后,会产生如下所示的结果:
$ helm install --generate-name --dry-run --debug ./mychart
install.go:148: [debug] Original chart version: ""
install.go:165: [debug] CHART PATH: /Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
NAME: mychart-1575966483
LAST DEPLOYED: Tue Dec 10 16:28:04 2019
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
favorite:
drink: coffee
food: pizza
favoriteDrink: coffee
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575966483-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
我们可以看到 values 中的 pizza 值已经被转换成了 "PIZZA"。当这样传递参数的时候,第一个求值结果(.Values.favorite.drink
)会作为一个参数发送给函数,我们可以修改上面的 drink 示例,用一个带有两个参数的函数进行说明:repeat COUNT STRING
。 作用是把后面的字符串重复 COUNT 次
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 }}
repeat 函数将重复字符串给定的次数,渲染后我们可以得到如下的输出结果:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575966939-configmap
data:
myvalue: "Hello World"
drink: "coffeecoffeecoffeecoffeecoffee"
food: "PIZZA"
default 函数
在模板中经常会使用到的一个函数是 default 函数:default DEFAULT_VALUE GIVEN_VALUE
,该函数允许你在模板内部指定默认值,我们来修改上面示例中的模板:
food: {{ .Values.favorite.food | default "rice" | upper | quote }}
正常运行,我们还是可以得到values.yaml
文件中定义的 pizza
:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575966939-configmap
data:
myvalue: "Hello World"
drink: "coffeecoffeecoffeecoffeecoffee"
food: "PIZZA"
现在我们从 values.yaml
文件中移除 food 的定义:
favorite:
drink: coffee
# food: pizza
现在我们重新运行 helm install --generate-name --dry-run --debug ./mychart
将渲染成如下的 YAML 文件:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575967394-configmap
data:
myvalue: "Hello World"
drink: "coffeecoffeecoffeecoffeecoffee"
food: "RICE"
在一个真实的 chart 模板中,所有的静态默认值都应位于 values.yaml
文件中,并且不应该重复使用 default 函数,但是,默认命令非常适合计算不能在 values.yaml
文件中声明的 values 值,例如:
food: {{ .Values.favorite.food | default (printf "%s-rice" (include "fullname" .)) }}
不过在有些地方,if 条件语句可能比 default 函数更合适,我们会在后面了解到。
模板函数和管道是将数据转换后然后将其插入到 YAML 文件中的一种强大方法,但是有的时候有必要添加一些模板逻辑,这些逻辑比仅仅插入字符串要复杂得多,下面我们将来了解模板语言中提供的控制流程。
运算符函数
另外需要注意的是在模板中,运算符(eq、ne、lt、gt、and、or
等等)均实现为函数,在管道中,运算符可以用括号()进行分割。
接下来我们可以去了解控制流程条件语句、循环和作用域修饰符的使用。
示例
{{ if eq .Values.service.type "ClusterIP" }}
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
spec:
type: ClusterIP
ports:
- port: {{ .Values.service.port }}
{{ else if eq .Values.service.type "NodePort" }}
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
spec:
type: NodePort
ports:
- port: {{ .Values.service.port }}
nodePort: {{ .Values.service.nodePort }}
{{ else }}
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}
spec:
type: LoadBalancer
ports:
- port: {{ .Values.service.port }}
{{ end }}
流程控制
控制流程为模板作者提供了控制模板生成流程的功能,Helm 的模板语言提供了以下一些流程控制:
if/else
条件语句with
指定一个作用域范围range
提供类似于 for each 这样的循环样式
除此之外,还提供了一些声明和使用命名模板的操作:
define
在模板内部声明一个新的命名模板template
导入一个命名模板block
声明了一种特殊的可填充模板区域。
这里我们先来了解 if、with、range
语句的使用,其他将在后面的命名模板部分介绍。
if else
首先我们先来了解下有条件地在模板中包含一个文本区域,就是 if/else ,这个条件判断的基本结构如下所示:
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
可以看到我们这里判断的是管道而不是一个 values 值,这是因为控制结构可以执行整个管道,而不仅仅是判断值。如果值为以下的一些内容,则将管道判断为 false:
- 布尔 false
- 数字零
- 一个空字符串
- nil(empty 或者 null)
- 一个空集合(map、slice、tuple、dict、array)
在其他条件下,条件都为真。
现在我们在上面的示例模板 ConfigMap 中添加一个简单的条件,如果 drink 设置为 coffee,我们就添加另外一个设置:
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 }}
我们把 values.yaml 文件内容设置成下面的样子:
favorite:
# drink: coffee
food: pizza
由于我们注释掉了 drink: coffee,所以渲染后输出不会包含 mug: true 的标志,但是如果我们把注释取消掉,则应该输出如下所示的内容:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575970308-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
这是因为上面模板中我们添加了if eq .Values.favorite.drink "coffee"
这样的条件判断,相当于是判断 .Values.favorite.drink
值是否等于 "coffee"
,如果相等则渲染 mug: true
。
空格控制
还有一个非常重要的功能点就是关于空格的控制,因为空格对于 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 install --generate-name --dry-run --debug ./mychart
install.go:148: [debug] Original chart version: ""
install.go:165: [debug] CHART PATH: /Users/ych/devs/workspace/yidianzhishi/course/k8strain/content/helm/manifests/mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key
这是因为我们在模板中添加了空格,生成了不正确的 YAML 文件:
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575970308-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
我们可以看到 mug: true 的缩进是有问题的,不符合 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 }}
重新渲染模板,然后可以发现已经可以正常通过了,但是渲染出来的 YAML 文件格式看上去还是有点奇怪:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575971172-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
我们可以看到得到的 YAML 文件中多了一些空行,这是因为模板引擎渲染的时候它会删除{{ 和 }}之间的内容,但是会完全保留其余的空格。
我们知道在 YAML 文件中空格是有意义的,所以管理空格就变得非常重要了,不过 Helm 模板也提供了一些工具来帮助我们管理空格。
首先可以使用特殊字符修改模板声明的花括号语法,以告诉模板引擎去掉空格。{{- 添加了破折号和空格表示应将左边的空格移除,-}}表示将右边的空格移除,另外也需要注意的是,换行符也是空格。
空格
需要注意的时候要确保 `-` 和指令的其余部分之间要有空格,{{- 3 }} 表示删除左边的空格并打印3,但是 {{-3 }}表示打印-3。
使用这个语法,我们可以修改上面的模板来移除多余的空行:
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 }}
渲染后可以看到空行被移除掉了:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575972373-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
为了更加清楚地说明这个问题,我们用*
来代替将要删除的每个空格,行尾的*
表示被删除的换行符:
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 }}
所以我们这里用 {{- 表示的就是删除本行开头的两个空格以及上一行的换行符,这样是不是就将空行都删除了啊。
在使用移除空格的时候还需要小心,比如下面的操作:
food: {{ .Values.favorite.food | upper | quote }}
{{- if eq .Values.favorite.drink "coffee" -}}
mug: true
{{- 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 -}}
第一个 {{- 会删除前面的空格和前面的换行符,然后后面的 -}} 会删除当前行的换行符,这样就会把 mug: true
移动到 food: "PIZZA" 后面去了,最终渲染过后就会变成:food: "PIZZA"mug: true,因为在两侧都去掉换行符。
有关模板中空格控制的详细信息,可以查看 Go 模板官方文档介绍。
不过有时候告诉模板系统如何缩进比起去控制模板指令的间距更加容易,所以,有时候你会发现缩进函数({{ indent 2 "mug: true" }})更有用。
使用 with 修改作用域
接下来需要了解的是 with 操作,它可以控制变量的作用域,然后重新用 .
调用就表示对当前作用域的引用,所以,.Values
是告诉模板引擎在当前作用域下内查找 Values 对象。
.
表示的是作用域
with 语句的语法和 if 语句比较类似:
{{ with PIPELINE }}
# 限制范围
{{ end }}
范围可以更改,可以让你将当前范围 . 设置为特定的对象,例如,我们一直在使用 .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 }}
我们这里将前面练习的 if 条件语句删除了,在模板中我们添加了一个 {{- with .Values.favorite }} 的语句,
意思就是说在 with 语句的作用范围内可以用 . 表示 .Values.favorite 了,所以我们可以引用 .drink 和.food 了,但是在 {{ end }} 之后就会重置为之前的作用域了。
不过需要注意得是,在受限的作用域范围内,你无法从父级范围访问到其他对象,比如,下面得模板会失败:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }}
{{- end }}
因为Release.Name
并不在 .
的限制范围内,所以会产生错误,但是,如果我们交换最后两行,则就可以正常工作了,因为end之后会重置作用域。
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
release: {{ .Release.Name }}
下面我先来了解下 range,然后我们再去了解下模板变量,它可以为上面得这个范围问题提供一种解决方案。
range 循环操作
我们知道许多编程语言都支持使用 for 循环、foreach 循环或者类似功能机制进行循环迭代,在 Helm 得模板语言中,迭代集合得方法是使用 range 运算符。
比如首先我们在 values.yaml
文件中添加一份 pizza
馅料列表:
favorite:
drink: coffee
food: pizza
pizzaToppings: #数组
- mushrooms
- cheese
- peppers
- onions
现在我们有了pizzaToppings
列表(在模板中称为切片),我们可以来修改下模板将列表打印到 ConfigMap 中:
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 }}
我们仔细观察下模板中的toppings:
列表,range 函数将遍历 Values.pizzaToppings
列表,我们看到里面使用了一个 .
,类似于上面我们用 with 设置范围一样,运算符也是这样的,每次循环,. 都会被设置为当前的 pizzaTopping
,也就是说第一次设置为mushrooms
,第二次迭代设置为cheese
,依次类推。
我们可以直接传递 . 这个值到管道上,所以我们这里 {{ . | title | quote }} 就相当于发送 . 给title(标题大小写函数)函数,然后发送给 quote 函数,我们渲染这个模板,会输出如下的内容:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1575975849-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
toppings: |-
- "Mushrooms"
- "Cheese"
- "Peppers"
- "Onions"
在上面模板中,我们做了一些小小的特殊处理,toppings: |-
行表示声明一个多行字符串,所以其实我们的 toppings 列表不是一个 YAML 列表,而是一个比较大的字符串,这是因为 ConfigMap 中的数据由 key/value
对组成,所有 key 和 value 都是简单的字符串,要了解为什么是这样的,可以查看 Kubernetes ConfigMap 文档,不过这个细节对我们这里不重要。
YAML
多行字符串可以使用 | 保留换行符,也可以使用 > 折叠换行,如:
this: |
Foo
Bar
that: >
Foo
Bar
对应的意思就是:{this: 'Foo\nBar\n', that: 'Foo Bar\n'}
- 表示保留文字块末尾的换行,- 表示删除字符串末尾的换行,如:
s1: |
Foo
s2: |+
Foo
s3: |-
Foo
对应的意思就是:{s1: 'Foo\n', s2: 'Foo\n\n\n', s3: 'Foo'}
有时候,在模板中快速创建一个列表,然后遍历该列表很有用,Helm 模板具有简化该功能的函数:tuple
。
元组是固定大小的列表集合,但是具有任意数据类型,下面是元组的大概使用方法:
sizes: |-
{{- range tuple "small" "medium" "large" }}
- {{ . }}
{{- end }}
上面的模板最终会被渲染成如下的 YAML:
sizes: |-
- small
- medium
- large
除了列表和元组之外,range 还可以用于遍历字典,我们在下一节介绍模板变量的时候再来了解这个用法吧。