3、Helm模板开发
Helm模板开发
目录
[toc]
1、内置对象
前面我们介绍了 Helm Chart 的一些基本概念和使用,接下来我们重点介绍下 Chart 模板的编写。模板会渲染成 Kubernetes 的资源清单文件,下面我们将来学习下模板的结构,如何使用它们,如何编写 Go 模板以及如何调试。
对象从模板引擎传递到模板中,在代码中可以传递对象。也有一种方法可以在模板宏创建新的对象,比如 tuple
函数。对象可以很简单,也可以包含其他对象或函数,例如,Release 对象就包含几个对象(比如 Release.Name),Files 对象就包含几个函数。
前面提到过我们可以在模板中使用 {{ .Release.Name }}
获取 release 的名称,Release 是我们可以在模板中访问的几个顶级对象之一:
-
Release
:该对象描述了 release 本身的相关信息,它内部有几个对象: -
Release.Name
:release 名称Release.Namespace
:release 安装到的命名空间Release.IsUpgrade
:如果当前操作是升级或回滚,则该值为 trueRelease.IsInstall
:如果当前操作是安装,则将其设置为 trueRelease.Revision
:release 的 revision 版本号,在安装的时候,值为1,每次升级或回滚都会增加Reelase.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 团队,选择仅使用首字母小写,以区分本地名称和内置名称,这里我们也会遵循该约定。
2、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
参数覆盖。
1.chart模板编写
💘 演示:chart模板编写-2022.4.5(测试成功)
1️⃣ 创建charts包
➜ ~ helm create mychart
Creating mychart
➜ ~ tree mychart
mychart
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
3 directories, 10 files
我们清空values.yaml
文件内容,删除templates目录
下内容:
2️⃣ 创建模板文件
常规yaml文件如下:
在mychart/templates目录
下创建ConfigMap.yaml文件
:
apiVersion: v1
kind: ConfigMap
metadata:
name: myconfigmap
data:
test: helm-chart
然 后直接使用helm template mychart1 ./mychart
命令直接渲染,我们看下结果:
3️⃣ 编辑模板文件
- values 值文件是纯 YAML 文件,我们可以来编辑
mychart/values.yaml
文件然后编辑ConfigMap
模板。删除values.yaml
中的默认设置后,我们将只设置一个参数:
favoriteDrink: coffee
- 现在我们可以在模板中直接使用它,这里编辑下
ConfigMap.yaml文件
:
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
➜ helm install --generate-name --dry-run ./mychart #注意这里的--dry-run参数:simulate an install
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/hg/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/hg/.kube/config
NAME: mychart-1649168183
LAST DEPLOYED: Tue Apr 5 22:16:24 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649168183-configmap
data:
myvalue: "Hello World"
drink: coffee
注意:这里如果使用template命令
的话,同时使用--generate-name
选项,会没有效果,建议直接使用--dry-run
选项;
因为--dry-run
是模拟安装,而template命令
只渲染,可能会有点问题!
4️⃣ 使用--set
参数来覆盖默认vaulues值
- 由于在默认的
values.yaml
文件中将 favoriteDrink 设置为了 coffee,所以这就是模板中显示的值,我们可以通过在调用helm install
的过程中添加--set
参数来覆盖它:
#查看下values.yaml文件内容
favoriteDrink: coffee
#使用`--set` 参数来覆盖默认vaulues值
➜ helm install --generate-name --set favoriteDrink=xyy --dry-run ./mychart
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/hg/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/hg/.kube/config
NAME: mychart-1649205876
LAST DEPLOYED: Wed Apr 6 08:44:37 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649205876-configmap
data:
myvalue: "Hello World"
drink: xyy
因为 --set
的优先级高于默认的 values.yaml
文件,所以我们的模板会生成 drink: slurm
。
5️⃣ Values 值文件也可以包含更多结构化的内容
- Values 值文件也可以包含更多结构化的内容,例如我们可以在
values.yaml
文件中创建一个 favorite 的部分,然后在其中添加几个 keys:
favorite:
drink: coffee
food: pizza
- 现在我们再去修改下我们的模板
ConfigMap.yanl
:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink }}
food: {{ .Values.favorite.food }}
- 验证
➜ local-vscode helm install --generate-name --set favoriteDrink=xyy --dry-run ./mychart
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/hg/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/hg/.kube/config
NAME: mychart-1649206046
LAST DEPLOYED: Wed Apr 6 08:47:27 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649206046-configmap
data:
myvalue: "Hello World"
drink: coffee
food: pizza
虽然我们可以通过这种方式来构造数据,但是还是建议你将 values 树保持更浅,这样在使用的时候更加简单。当我们考虑为子 chart 分配 values 值的时候,我们就可以看到如何使用树形结构来命名 values 值了。
测试结束。😋
2.删除默认 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
到这里我们已经了解到了几个内置对象,并利用它们将信息注入到了模板中,现在我们来看看模板引擎的另外方面:函数和管道。
3、函数和管道
现在我们 已经了解了如何将信息加入到模板中,但是这些信息都是直接原样的放置过去的,有时候,我们希望以一种对我们更有用的方式来转换提供的数据。
下面让我们从一个最佳实践开始:将 .Values
对象中的字符串注入模板时,我们应该引用这些字符串,我们可以通过在 template 指令中调用 quote
函数来实现,比如:
💘 演示:测试quote函数-2022.4.5(测试成功)
- 编辑
ConfigMap.yaml
:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ quote .Values.favorite.drink }}
food: {{ .Values.favorite.food }}
模板函数遵循的语法规则是 functionName arg1 arg2...
,在上面的代码片段中,quote .Values.favorite.drink
会调用 quote
函数并传递一个单个参数。
- 测试
➜ local-vscode helm install --generate-name --dry-run ./mychart
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/hg/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/hg/.kube/config
NAME: mychart-1649221072
LAST DEPLOYED: Wed Apr 6 12:57:53 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649221072-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: pizza
测试结束。😘
🍀 Helm模板语言
Helm 有60多种可用的函数,其中一些是由 Go 模板语言本身定义的,其他大多数都是 Sprig 模板库提供的,接下来我们会通过部分示例来逐步介绍其中的一些功能函数。
Helm 模板
当我们谈论 Helm 模板语言
的时候,就好像是特定于 Helm 一样,但实际上它是 Go 模板语言加上一些额外的函数以及各种封装程序的组合,以将某些对象暴露给模板。当我们需要学习模板的时候,Go 模板上有许多资源会对我们有所帮助的。
1.管道
模板语言有一个强大的功能就是**管道(Pipeline)**概念,管道利用 UNIX 的概念,将一系列模板命令链接在一起,一起对外提供服务,换句话说,管道是按顺序完成多项工作的有效方式。
💘 演示:测试管道-2022.4.5(测试成功)
1️⃣ 我们来使用管道重写上面的示例模板:编辑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 }}
2️⃣ 在这里我们没有调用 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
用法更多,虽然两种方法都是可以的。
3️⃣ 最后,模板渲染后,会产生如下所示的结果:
➜ local-vscode helm install --generate-name --dry-run ./mychart
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/hg/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/hg/.kube/config
NAME: mychart-1649225993
LAST DEPLOYED: Wed Apr 6 14:19:54 2022
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649225993-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
我们可以看到 values 中的 pizza
值已经被转换成了 "PIZZA"
。当这样传递参数的时候,第一个求值结果(.Values.favorite.drink
)会作为一个参数发送给函数。
4️⃣ 我们可以修改上面的 drink
示例,用一个带有两个参数的函数进行说明:repeat COUNT STRING
。
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
函数将重复字符串给定的次数,渲染后我们可以得到如下的输出结果:
➜ local-vscode helm install --generate-name --dry-run ./mychart
……
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649226152-configmap
data:
myvalue: "Hello World"
drink: "coffeecoffeecoffeecoffeecoffee"
food: "PIZZA"
这里需要注意下:这个repeat 5
要放在前面吗,不会会报错,放在后面的话,因为是不满足yaml格式的。
测试结束。😘
2.default 函数
在模板中经常会使用到的一个函数是 default
函数:default DEFAULT_VALUE GIVEN_VALUE
,该函数允许你在模板内部指定默认值。
💘 演示:测试default函数-2022.4.5(测试成功)
1️⃣ 我们来修改上面示例中的模板:
#这里添加deafult "rice"
food: {{ .Values.favorite.food | default "rice" | upper | quote }}
#完整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 | default "rice" | upper | quote}}
2️⃣ 正常运行,我们还是可以得到 values.yaml
文件中定义的 pizza:
➜ local-vscode helm install --generate-name --dry-run ./mychart
……
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649226682-configmap
data:
myvalue: "Hello World"
drink: "coffeecoffeecoffeecoffeecoffee"
food: "PIZZA"
3️⃣ 现在我们从 values.yaml
文件中移除 food 的定义:
favorite:
drink: coffee
# food: pizza
4️⃣ 现在我们重新运行 helm install --generate-name --dry-run --debug ./mychart
将渲染成如下的 YAML 文件:
➜ local-vscode helm install --generate-name --dry-run ./mychart
……
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649226830-configmap
data:
myvalue: "Hello World"
drink: "coffeecoffeecoffeecoffeecoffee"
food: "RICE"
5️⃣ 注意
在一个真实的 chart 模板中,所有的静态默认值都应位于 values.yaml
文件中,并且不应该重复使用 default
函数,但是,默认命令非常适合计算不能在 values.yaml
文件中声明的 values 值,例如:
food: {{ .Values.favorite.food | default (printf "%s-rice" (include "fullname" .)) }}
不过在有些地方,if
条件语句可能比 default 函数更合适,我们会在后面了解到。
模板函数和管道是将数据转换后然后将其插入到 YAML 文件中的一种强大方法,但是有的时候有必要添加一些模板逻辑,这些逻辑比仅仅插入字符串要复杂得多,下面我们将来了解模板语言中提供的控制流程。
测试结束。😘
3.运算符函数
另外需要注意的是在模板中,运算符(eq、ne、lt、gt、and、or 等等)均实现为函数;在管道中,运算符可以用括号()
进行分割。
接下来我们可以去了解控制流程条件语句、循环和作用域修饰符的使用。
4、流程控制
控制流程为模板作者提供了控制模板生成流程的功能,Helm 的模板语言提供了以下一些流程控制:
if/else
条件语句with
指定一个作用域范围range
提供类似于for each
这样的循环样式
除此之外,还提供了一些声明和使用命名模板的操作:
define
在模板内部声明一个新的命名模板template
导入一个命名模板block
声明了一种特殊的可填充模板区域。
这里 我们先来了解 if
、with
、range
语句的使用,其他将在后面的命名模板
部分介绍。
1.if/else
首先我们先来了解下有条件地在模板中包含一个文本区域,就是 if/else
,这个条件判断的基本结构如下所示:
{{ if PIPELINE }} #PIPLINE可以认为是一个表达式!
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
#注意:if和else一定是成对存在的。
可以看到我们这里判断的是管道而不是一个 values 值,这是因为控制结构可以执行整个管道,而不仅仅是判断值。如果值为以下的一些内容,则将管道判断为 false:
- 布尔 false
- 数字零
- 一个空字符串
- nil(empty 或者 null)
- 一个空集合(map、slice、tuple、dict、array)
在其他条件下,条件都为真。
💘 示例:if/else测试-2022.4.6(测试成功)
1️⃣ 现在我们在上面的示例模板 ConfigMap.yaml
中添加一个简单的条件,如果 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
2️⃣ 由于我们注释掉了 drink: coffee
,所以渲染后输出不会包含 mug: true
的标志。
这里需要注意下: (用于比较的不兼容类型)
但是如果我们把注释取消掉,则应该输出如下所示的内容:
#values.yaml
favorite:
drink: coffee
food: pizza
➜ helm install --generate-name --dry-run ./mychart
……
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649242370-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
这是因为上面模板中我们添加了 if eq .Values.favorite.drink "coffee"
这样的条件判断,相当于是判断 .Values.favorite.drink
值是否等于 "coffee"
,如果相等则渲染 mug: true
。
3️⃣ 这里我们再很关注一下细节问题
#values.yaml
favorite:
drink: coffee
food: pizza
思考:这个模板里if流程控制语句
为什么不可以竖着写,而要写在一行上?不会不感觉可读性不好!
我们这里尝试这竖着写看下有什么效果:
竖着写,当模板被渲染之后,这里会多出2个空行!
那么,我们可以使用如下方式来解决这个问题:这里添加2个-
即可。
4️⃣ 接下来,我们再使用下if…… esle if ……else
,看下效果
配置文件如下:
#values.yaml
favorite:
drink: coffee
food: pizza
#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" -}}
condiition: true
{{ else if eq .Values.favorite.drink "water" -}}
elsecondition: true
{{ else -}}
defaultcase: true
{{- end }}
我们渲染下,查看效果:
➜ local-vscode helm install --generate-name --dry-run --set favorite.drink=water ./mychart
……
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649243095-configmap
data:
myvalue: "Hello World"
drink: "water"
food: "PIZZA"
elsecondition: true
➜ local-vscode helm install --generate-name --dry-run --set favorite.drink=waterxxx ./mychart
……
# Source: mychart/templates/ConfigMap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649243172-configmap
data:
myvalue: "Hello World"
drink: "waterxxx"
food: "PIZZA"
defaultcase: true
测试结束。😘
2.空格控制
这个需要再重点研究下:还有一个非常重要的功能点就是关于空格的控制,因为空格对于 YAML 文件非常重要的,不是说任意缩进就可以。
总结如下:常见写法如下!
1.如果是个判断语句,前面{{都要带上-,包括end语句也要带上{{-;
2.推荐使用nindent {{- nindent 2也要带上;
💘 示例:空格控制测试-2022.4.7(测试成功)
🍀 依然还是以前面的例子为例,我们来格式化下模板格式以更易于阅读:
#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 }}
#values.yaml
favorite:
drink: coffee
food: pizza
现在我们的模板看上去更易于阅读了,但是我们通过模板引擎来渲染下,却会得到如下的错误信息:
➜ helm install --generate-name --dry-run ./mychart
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/hg/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/hg/.kube/config
Error: INSTALLATION FAILED: YAML parse error on mychart/templates/ConfigMap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key
这是因为我们在模板中添加了空格,生成了不正确的 YAML 文件:
#ConfigMap.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
➜ helm install --generate-name --dry-run ./mychart
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/hg/.kube/config
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-1649321939-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 }}
渲染后可以看到空行被移除掉了:
➜ helm install --generate-name --dry-run ./mychart
# 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 -}}