Chart 文件结构
一个 Chart 包由以下几个配置文件组成:
wordpress/
Chart.yaml # Yaml文件,用于描述 Chart 的基本信息,包括名称版本等
LICENSE # [可选] 文本格式的协议
README.md # [可选] 应用介绍、使用说明
requirements.yaml # [可选] 用于存放当前 Chart 依赖的其它 Chart 的说明文件
values.yaml # Chart 的默认值配置文件
charts/ # [可选] 该目录中放置当前 Chart 依赖的其它 Chart
templates/ # [可选] 部署文件模版目录,模版填入 values.yaml 中相应值,生成最终的
kubernetes 配置文件
├── NOTES.txt [可选] 使用指南
├── _helpers.tpl 下划线开头的文件,可以被其他模板引用
├── deployment.yaml 创建k8s资源deployment的yaml文件
├── ingress.yaml 创建k8s资源ingress的yaml文件
└── service.yaml 创建k8s资源service的yaml文件
├── .....
Chart.yaml 文件
apiVersion: [必须] Chart API 版本,可用值 v1
name: [必须] Chart 名称
version: [必须] 版本,遵循 [SemVer 2 标准](https://semver.org/)
kubeVersion: [可选] 兼容的 Kubernetes 版本,遵循 [SemVer 2 标准](https://semver.org/)
description: [可选] 一句话的应用描述
keywords:
- [可选] 应用关键字列表
home: [可选] 应用主页 URL
sources:
- [可选] 当前应用下载地址列表
maintainers: [可选]
- name: [必须] name
email: [可选] email
url: [可选] url
engine: [可选] 模板引擎,默认值是 gotpl
icon: [可选] SVG 或者 PNG 格式的图片地址
appVersion: [可选] 应用版本
deprecated: [可选] boolean 类型,是否不建议使用
tillerVersion: [可选] Chart 需要的 Tiller 版本,遵循 [SemVer 2 标准](https://semver.org/),需要 ">2.0.0"
Requirements.yaml 文件和 Charts 目录
Chart支持两种方式表示依赖关系,可以写入 requirements.yaml 文件动态链接[推荐],也可以将依赖的 Chart 放入 charts 目录手动管理。
requirements.yaml 文件样例:
dependencies:
- name: apache
version: 1.2.3
repository: http://example.com/charts
- name: mysql
version: 3.2.1
repository: http://another.example.com/charts
name:Chart 名称
version:Chart 版本
repository: Chart 仓库 URL 地址
有了 requirements.yaml 文件,可以运行 helm dependency update,依赖的 Chart 会被自动的下载到 charts 目录下。
Values.yaml 文件和 Templates 目录
values.yaml 文件中记录了模板中引用的默认值。 templates 目录中存放了 Kubernetes 部署文件的模版,遵循 Go template 语法
templates 中模板文件样例:
apiVersion: v1
kind: ReplicationController
metadata:
name: deis-database
namespace: deis
labels:
app.kubernetes.io/managed-by: deis
spec:
replicas: 1
selector:
app.kubernetes.io/name: deis-database
template:
metadata:
labels:
app.kubernetes.io/name: deis-database
spec:
serviceAccount: deis-database
containers:
- name: deis-database
image: {{.Values.imageRegistry}}/postgres:{{.Values.dockerTag}}
imagePullPolicy: {{.Values.pullPolicy}}
ports:
- containerPort: 5432
env:
- name: DATABASE_STORAGE
value: {{default "minio" .Values.storage}}
上述样例是一个 Kubernetes 中 replication controller 的模板文件定义,其中引用了以下几个值(一般定义在 values.yaml 中)
imageRegistry:Docker 映像仓库
dockerTag: Docker 映像标签
pullPolicy: 下载映像策略
storage: 存储后端,默认值是 "minio"
values.yaml 文件样例:
imageRegistry: "quay.io/deis"
dockerTag: "latest"
pullPolicy: "Always"
storage: "s3"
开始
1 简单示例
1.1 生成案例结构 mycahrt
$ helm create mychart
Creating mychart
1.2 helm 客户端会自动的为我们创建些文件:
mychart
├── Chart.yaml
├── charts
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yaml
└── values.yaml
2 第一个模板
2.1 删除所有文件,我们自己手动,有助于深入理解
rm -rf mychart/templates/*.*
删除掉 Helm 生成的模板文件,我们自己来实现一个模板。
我们的第一个模板是创建一个 ConfigMap。创建一个 mychart/templates/configmap.yaml
文件,并写入如下内容:
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
注意:Template 目录下的文件没有严格的命名规范。但我们推荐用 .yaml
的后缀表示 YAML 文件,用 .tpl
表示帮助文件。
现在我们来部署它:
$ helm install wubo ./mychart
NAME: full-coral
LAST DEPLOYED: Tue Nov 09 17:36:01 2020
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
mychart-configmap 1 1m
我们可以来查看下部署信息
$ helm get manifest wubo
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mychart-configmap
data:
myvalue: "Hello World"
helm get manifest
这条命令可以通过 release 名来打印其相关信息。release的名字此时就是wubo
现在,删除掉刚刚发布的 release:helm delete wubo
3 赋值示例
- Release:用来描述 release 本身
- Release.Name
- Release.Time
- Release.Namespace
- Release.Service:值总是
Tiller 注:在
Helm 2 中,Tiller 是作为一个 Deployment 部署在 kube-system 命名空间中,很多情况下,我们会为 Tiller 准备一个 ServiceAccount ,这个 ServiceAccount 通常拥有集群的所有权限。 用户可以使用本地 Helm 命令,自由地连接到 Tiller 中并通过 Tiller 创建、修改、删除任意命名空间下的任意资源。 在 Helm 3 中,Tiller 被移除了。新的 Helm 客户端会像 kubectl 命令一样,读取本地的 kubeconfig 文件,使用我们在 kubeconfig 中预先定义好的权限来进行一系列操作。 - Release.Revision:release 版本号。从 1 开始,每次执行
helm upgrade
,数加 1 - Release.IsUpgrade:本次操作是否为升级
- Release.IsInstall:本次操作是否为安装
- Values:
- Chart:
Chart.yaml
里的内容 - Files:
File.Get
通过名字获取文件(.Files.Get config.ini
)File.getBytes
以字节流的方式获取,在获取类型图片时比较有用
- Capabilities:
- Template:
3.2 Values Files
前面讲了内置对像 Values
,它的值有四个来源:
values.yaml
文件- 如果这是个子 chart,其父 chart 的
Values.yaml
文件 - 在
helm install
或helm upgrade
时,通过-f
指定的文件 - 通过
--set
指定的参数( 例:helm install --set foo=bar ./mychart )
优先级从上到下依次增加,即 --set
最高。
现在让我们来编辑 mychart/values.yaml
,删除默认值,只写一个参数:
cat values.yaml
favoriteDrink:coffee
在模板中使用刚刚写的参数:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favoriteDrink}}
注意最后一行,我们通过访问 Values 属性的方式 {{ .Values.favoriteDrink}}
来获取 favoriteDrink 值。
来看下渲染的结果:
$ helm install wubo --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
NAME: wubo
TARGET NAMESPACE: default
CHART: mychart 0.1.0
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: geared-marsupi-configmap
data:
myvalue: "Hello World"
drink: coffee
还可以通过 --set
覆盖掉这个值:
helm install wubo --dry-run --debug --set favoriteDrink=slurm ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
NAME: wubo
TARGET NAMESPACE: default
CHART: mychart 0.1.0
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: solid-vulture-configmap
data:
myvalue: "Hello World"
drink: slurm
由于 --set
的优先级高于 values.yaml
,所有我们的模板最终输出为 drink: slurm
。
Values 文件还可以包含结构内容。
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 }}
看下效果2中方式
1 此种方式可以拿到Release相关的内置属性值
root@localhost:~/go/test1/mychart # helm install wubo -f values.yaml --dry-run --debug .
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/go/test1/mychart
NAME: wubo
LAST DEPLOYED: Wed Sep 23 17:07:52 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
favorite:
drink: coffee
food: pizza
COMPUTED VALUES:
favorite:
drink: coffee
food: pizza
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: wubo-configmap
data:
myvalue: "Hello World"
drink: coffee
food: pizza
2 此种方式只是根据values和temp模板生成最终可供k8s的kubectl create -f 执行的一个yaml文件
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
myvalue: "Hello World"
drink: coffee
food: pizza
3.3. 删除默认Key
如果你想从默认值中删除一个key,你可以通过传 null
值,这样 Helm 在合并时就会删除这个 key。
举个例子, 名为 Drupal 的 chart 中配置的有存活检测。下面是它的默认设置:
livenessProbe:
httpGet:
path: /user/login
port: http
initialDelaySeconds: 120
如果你要使用 exec 替换 httpGet,可以通过 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt],Helm 会合并默认值和传进去的值,结果如下:
livenessProbe:
httpGet:
path: /user/login
port: http
exec:
command:
- cat
- docroot/CHANGELOG.txt
initialDelaySeconds: 120
这时,k8s 就会出错,因为你定义了两个 liveness handler
。要解决这个问题,你可以通过给 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
4 模板函数和Pipelines(管道)
Helm包含许多模板函数,可以在模板中使用它们。常用函数:
https://helm.sh/docs/chart_template_guide/function_list/
https://helm.sh/docs/chart_template_guide/builtin_objects/
https://blog.csdn.net/miss1181248983/article/details/108285276
让我们从一个练习开始:当我们往 .Values
里注入一个字符串时,应当用引号将它们括起来,在模板中可以直接使用 quote
函数来实现:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ quote .Values.favorite.drink }}
food: {{ quote .Values.favorite.food }}
模板函数的语法如下:functionName arg1 arg2...
。在上面的小例子中, quote .Values.favorite.drink
, 使用了 quote
函数并传递了一个参数。
4.1. Pipelines(管道)
通过管道可以在一行里干多件事,我们使用 pipeline 重写上面的例子:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | quote }}
food: {{ .Values.favorite.food | quote }}
通过 pipeline(管道),我们可以链式的调用多个函数:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | quote }}
food: {{ .Values.favorite.food | upper | quote }}
4.2 使用 Default 函数
这个函数允许你指定一个默认值:
drink: {{ .Values.favorite.drink | default "tea" | quote }}
4.3. OPERATORS ARE FUNCTIONS
操作符是按照函数的方式实现的,返回一个布尔值。使用 eq, ne, lt, gt, and, or, not
时,要将它们放到句子的最前面,后面跟上对应的参数。多个操作符一起使用时,可以用小括号包起来。
{{/* include the body of this if statement when the variable .Values.fooString exists and is set to "foo" */}}
{{ if and .Values.fooString (eq .Values.fooString "foo") }}
{{ ... }}
{{ end }}
意思就是Values.fooString不能为空且Values.fooString的值等于foo
{{/* do not include the body of this if statement because unset variables evaluate to false and .Values.setVariable was negated with the not function. */}}
{{ if or .Values.anUnsetVariable (not .Values.aSetVariable) }}
{{ ... }}
{{ end }}
4.4控制流
Helm 的模板语言提供下面几种控制结构:
if / else
with
range
提供类型for each
的循环
另外,还提供了几种方式来声明和使用命名模板:
define
template
block
这节只讨论 if
, with
, 和 range
。其它的会在之后的 “Named Templates” 那节介绍。
4.4.1 . IF / ELSE
基本结构如下
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
注意这里我们用 pipelines
面不是 values
,是为了表明这个控制结构是可以运行完全的 pipeline
的,而不仅仅只能放个值。
下面情况其值会被认为是 false:
- bool 型的 false
- 数字 0
- 空字符串
- nil
- 空集合(
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 and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}mug: true{{ end }}
注意mug: true前后不能有空格
注意, .Values.favorite.drink
最好是判断下是否为空,否则当它和 coffee
作比较时会报错。最后的输出就变成:
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: tru
或
root@localhost:~/go/test1/mychart # helm install wubo -f values.yaml --dry-run --debug . 1 ↵
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/go/test1/mychart
NAME: wubo
LAST DEPLOYED: Wed Sep 23 17:18:12 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
favorite:
drink: coffee
food: pizza
COMPUTED VALUES:
favorite:
drink: coffee
food: pizza
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: wubo-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
4.4.2. 空白管理
空白的使用在模板中是受限制的。下面把之前的代码格式修改下,使它更易阅读:
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 --dry-run --debug ./mychart
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key
这就是因为空格导致的:
这是翻译之后的最终数据 发现最后一样有问题 多2个空格
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: eyewitness-elk-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
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}}
再次运行,发现生成的 YAML 格式正确了,但是有点丑:
root@localhost:~/go/test1/mychart # helm install wubo -f values.yaml --dry-run --debug . 20 ↵
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/go/test1/mychart
NAME: wubo
LAST DEPLOYED: Wed Sep 23 17:20:22 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
favorite:
drink: coffee
food: pizza
COMPUTED VALUES:
favorite:
drink: coffee
food: pizza
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: wubo-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: true
注意,YAML 中有行是空的。
{{
- 表示去掉左边或前的空格 }}
{{
表示去掉右边或后的空格 -}}
{{
- 表示去掉左边或前和右边或后的空格 -}}
好好理解一下 案例如下
root@localhost:~/go/test1/mychart # cat templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
namespace: {{ .Release.Namespace }}
time: {{ .Release.Time }}
data:
myvalue: "Hello World"
{{ with .Values.favorite }}
{{ if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }}
mug: true
{{ end }}
name: wubo
{{ end }}
age: 12
{{ if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}
mug: true
{{ end }}
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
执行效果
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml 20 ↵
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
namespace: default
time:
data:
myvalue: "Hello World"
mug: true
name: wubo
age: 12
mug: true
drink: "coffee"
food: "PIZZA"
改成
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
namespace: {{ .Release.Namespace }}
time: {{ .Release.Time }}
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
{{- if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }}
mug: true
{{- end }}
name: wubo
{{- end }}
age: 12
{{- if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}
mug: true
{{- end }}
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }
效果
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml 20 ↵
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
namespace: default
time:
data:
myvalue: "Hello World"
mug: true
name: wubo
age: 12
mug: true
drink: "coffee"
food: "PIZZA"
分析内容如下:
myvalue: "Hello World"
{{- with .Values.favorite }}
{{- if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }}
mug: true
{{- end }}
name: wubo
{{- end }}
age: 12
{{- if and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}
mug: true
{{- end }}
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
解释:
myvalue: "Hello World"和 mug: true之间会有2个空行,因为有两个流程操作{{ with }} 和 {{ if }}
mug: true和 name: wubo之间有一个空行,因为有一个操作{{ end }}
name: wubo和 age: 12之间也有一个操作同上
age: 12和mug: true之间也有一个同上
mug: true和drink: {{ .Values.favorite.drink | default "tea" | quote }}之间也有一个
drink: {{ .Values.favorite.drink | default "tea" | quote }}和 food: {{ .Values.favorite.food | upper | quote }}之间没有
然后就是怎么去除空行
myvalue: "Hello World"
{{- with .Values.favorite }} "-" 写在前代表的是删除上面或左边的所有空行,但是右边或后面还有一个空行没有处理呢
{{- if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }} 同理如上
mug: true
上面这个还可以这样写
myvalue: "Hello World"
{{- with .Values.favorite -}} "-" 写在前和后代表的是删除上面或左边和后面或右边的空行,就是把前后所有的空行都删除了,注意是所有的空行,不是一个空行
{{ if and $.Values.favorite.drink (eq $.Values.favorite.drink "coffee") }} 此时{{- if }} 也是可以的,因为他的前面或左边确实有空行。{{- with -}} 共同删除空行没毛病。但是{{ if -}} 后面加个”-" 就会报错了,因为后面就是 mug: true没有空行了。
mug: true
4.5. 修改作用域
通过 .
可以引用当前的作用域。 .Values
告诉模板在当前作用域上查找 Values
。
可以通过 with
来调整作用域
{{ with PIPELINE }}
# restricted scope
{{ end }}
with 允许你把当前作用域(.)指到一个特定的对象上。举个例子,将 . 指到 .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 }}
警告,在这个特定的作用域中,你不能够访问其父领域的对象。下面的例子,访问会出错:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
wubo: {{ .Values.favorit.food }} 此处获取不到
release: {{ .Release.Name }} 也获取不到
{{- end }}
此时有两中解决办法,定义变量和全局获取
1种解决办法.由于下方的with语句引入相对命令空间,无法通过.Release引入,提前定义relname变量
2种解决办法.者可以使用$符号,引入全局命名空间
{{- $relname := .Values.favorit.food }}
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
wubo: {{ $relname }}
# 或者可以使用$符号,引入全局命名空间
wubo: {{$.Values.favorit.food}}
release: {{ $.Release.Name }}
{{- end }}
4.6. range 操作
Helm 可以通过 range
操作符来迭代集合。
在 values.yaml
中增加列表:
favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions
现在修改下 ConfigMap 的模板,来打印出上面的列表:
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml 20 ↵
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
toppings: |-
- "Mushrooms"
- "Cheese"
- "Peppers"
- "Onions"
和 with
一样,range
也可以设置作用域,所以在这里,.
表示的是 pizzaToppings
这个作用域。我们能把 .
直接传递给管道使用 {{ . | title | quote }}
。
运行上面的模板,结果如下:
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml -f values.yaml 20 ↵
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
toppings: |-
- "Mushrooms"
- "Cheese"
- "Peppers"
- "Onions"
toppings: |-
表示这是一个多行的字符串
4.7 变量
在模板里,变量较少被使用。但通过使用变量,可以使 with
和 range
得到更好的使用。
这是之前的一个例子,会报错:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }}
{{- end }}
在 Helm 模板里,一个变量是对一个对象的引用。格式是 $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
仍然指向 .Release.name
。
5 Named Templates
本节我们学习如何定义一个命名模板,并在别处使用它。
你要注意的是:模板的名字是全局性的。当你定义了两个相同名字的模板时,最后加载的那个将被使用。
推荐的做法是在定义模板名字时,加上 chart:{{define "mychart.labels"}}
。
在开始具体写模板前,有些命令惯例值得提醒下:
template/
中的大部分文件可以认为是 k8s 的资源NOTES.txt
文件除外- 有下划线的除外
5.2. 使用 define
和 template
define
允许我们在模板文件中创建一个命名模板:
{{ define "MY.NAME" }}
# body of template here
{{ end }}
举个例子
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
现在我们把它嵌套到之前的 ConfigMap 上,然后通过 template
来引入:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
最终,经过渲染,文件会变成:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: running-panda-configmap
labels:
generator: helm
date: 2020-09-23
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
习惯上 Helm 把这些模板放在一个特殊的文件里,通常是 _helpers.tpl
。让我们来移到下它吧:
{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
按惯例,define
函数应该有个简单的使用说明,用 {{/* ... */}}
括起来。
虽然是在 _helpers.tpl
中定义的,但仍然可以在 configmap.yaml
中使用:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
再次提醒下,命名模板是全局性的。如果定义了两个相同名字的模板,则后面定义的那个生效。
5.3. 给模板设定作用域
5.4. include
函数
如下,我们定义了个模板:
{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}"
{{- end -}}
现在,我们想把它同时入到 labels
和 data
:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{ template "mychart.app" .}}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
{{ template "mychart.app" . }}
但是结果不是我们预期的:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: measly-whippet-configmap
labels:
app_name: mychart
app_version: "0.1.0+1478129847"
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
app_name: mychart
app_version: "0.1.0+1478129847"
可以看到 app_version
的缩进是不对的。i
通过使用 include 和nindent
来实现正确的缩进,template语句的升级版本include,template是语句无法在后面接管道符来对引入变量做定义,
include实现了此功能.
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{- include "mychart.app" . | nindent 4 }}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
{{- include "mychart.app" . | nindent 2 }}
6 模板中访问文件
在之前的章节中我们学会了创建和访问命令模板,让我们可以方便的在一个模板中导入另一个模板。但是有些时我们要导入一个文件而不需要经过模板的渲染。
Helm 通过 .Files
对象提供了访问文件的功能。在开始例子之前,有些点需要我们注意:
- 在 Helm 中可以增加额处文件,它们也会被发给
Tiller
。但要注意,由于 k8s 存储大小的限制,Charts
的大小不能超过 1M。 - 因为安全的原因,有些文件是不能被
.Files
访问到的templates/
中的文件不能被访问- 在
.helmignore
中的文件不能被访问
6. 1. Basic Example
我们直接在 mychar/
下创建三个文件config1.toml
:
message = Hello from config 1
config2.toml
:
message = Hello from config 2
config3.toml
:
message = Hello from config 3
我们知道它位的名字,所以我们可以使用 range
循环的将它的们内容注入到 ConfigMap
中
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- $files := .Files }}
{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
{{ . }}: |-
{{ $files.Get . }}
{{- end }}
通过运行它,我们能够得到:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: quieting-giraf-configmap
data:
config1.toml: |-
message = Hello from config 1
config2.toml: |-
message = This is config 2
config3.toml: |-
message = Goodbye from config 3
6.2. Path Helpers
6.4. Encoding
使用base-64加密
apiVersion: v1
kind: Secret
metadata:
name: {{ .Release.Name }}-secret
type: Opaque
data:
token: |-
{{ .Files.Get "config1.toml" | b64enc }}
# Source: mychart/templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: lucky-turkey-secret
type: Opaque
data:
token: |-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
======================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{- include "mychart.app" . | nindent 4 }}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
{{- include "mychart.app" . | nindent 2 }}
{{- $files := .Files }}
{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
{{ . }}: |-
{{ $files.Get . | b64enc }}
{{- end }}
root@localhost:~/go/test1/mychart # helm template . -s templates/configmap.yaml 20 ↵
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
labels:
app_name: mychart
app_version: "0.1.0+"
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
app_name: mychart
app_version: "0.1.0+"
config1.toml: |-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
config2.toml: |-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDIK
config3.toml: |-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDMK
7. Lines
创建 NOTES.Txt 文件
这节,来介绍下如何给你的 chart 增加说明。在 helm install
或者 helm upgrade
的最后,会打印一些对用户有用的信息,而这些信息是可以高度定制化的。
创建 templates/NOTES.txt
文件。它是个普通文件,但是可以像模板那样来使用,能够在文件中使用模板函数和亦是。
现在来创建个简单的 NOTES.txt
文件:
Thank you for installing {{ .Chart.Name }}.
Your release is named {{ .Release.Name }}.
To learn more about the release, try wubo:
$ helm status {{ .Release.Name }}
$ helm get {{ .Release.Name }}
root@localhost:~/go/test1/mychart # helm install wubo1 --dry-run --debug . 20 ↵
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/go/test1/mychart
NAME: wubo1
LAST DEPLOYED: Thu Sep 24 11:35:34 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}
COMPUTED VALUES:
favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: wubo1-configmap
labels:
app_name: mychart
app_version: "0.1.0+"
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
app_name: mychart
app_version: "0.1.0+"
config1.toml: |-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
config2.toml: |-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDIK
config3.toml: |-
bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDMK
NOTES:
Thank you for installing mychart.
Your release is named wubo1.
To learn more about the release, try wubo:
$ helm status wubo1
$ helm get wubo1
NOTES.txt
不是必须的,但强烈推荐你创建一个。
8 Subcharts and Global Values
之前我们一直在使用一个 chart,但 chart 之间是可以存在依赖关系的,称之为 subchart,它们可以有自己的值和模板。这节我们将会创建一个 subchart,来看看它是如何获取值的。
在开始前,有几个关于 subchart 的知识点需要大家知道:
- subchart 是可以独立部署的,故而它不能明确的依赖 parent chart。
- subchart 不能获取 parent chart 的值。
- parent chart 可以覆盖 subchart 的值。
- Helm 有一个 global values 的概念,可以被所有 chart 获取。
8 1. Creating A Subchart
$ cd mychart/charts
$ helm create mysubchart
Creating mysubchart
$ rm -rf mysubchart/templates/*.*
8.2. ADDING VALUES AND A TEMPLATE TO THE SUBCHART
Debugging Templates
几种 debug 的方式:
helm lint
:检查你的 chart 是否有可以优化的地方helm install --dry-run --debug
:让 Tiller 渲染模板,并返回其生成的yaml
文件helm get manifest
:查看 k8s 部署的是什么样的模板
附录:YAML 技巧
前面全部关注在模板的书写上,现在让我们来看看 YAML 的格式。
两种类型的集合,map和队列
map:
one: 1
two: 2
three: 3
sequence:
- one
- two
- three
2. Yaml 中的类型
数字类型
count: 1
size: 2.34
如果它们被引号引起来,就变成了字符串
count: "1" # <-- string, not int
size: '2.34' # <-- string, not float
布尔类型也是如此:
isGood: true # bool
answer: "true" # string
空值由 null
表示,而不是 nil
。
需要注意的是,port: "80"
是符合 YAML 语法的,当通过模板引擎传递给 k8s 时,如果 k8s 要求此字段是 int 类型,那么会报错。
可以通过 Yaml 的标记,对类型进行强制的转换:
coffee: "yes, please"
age: !!str 21
port: !!int "80"
上面的代码,!!str
告诉分析器,age
是的 String 类型, port
是个 int
类型。
3. Yaml 中的字符串
因为 Yaml 是 Json 的超集,所以任何符合 JSON 格式的文档都符合 YAML 的规范。
{
"coffee": "yes, please",
"coffees": [
"Latte", "Cappuccino", "Espresso"
]
}
上面内容的另一种表示方式:
coffee: yes, please
coffees:
- Latte
- Cappuccino
- Espresso
两者还能混合书写:
coffee: "yes, please"
coffees: [ "Latte", "Cappuccino", "Espresso"]
上面三种方式表示的内容是一致的。
这表示 values.yaml
里可以包含 JSON 格式的数据。但 Helm 不允许文件后缀名为 .json
。
10 Yaml Anchor
附录: Go 语言数据类型和模板
因为 Helm 的模板编辑是基于 Go 语言的,而 Go 本身就是一种强类型的语言,所以模板中的变量是有类型的。
- string
- bool
- int
- float64
- a byte slice
- struct
- a slice
- a string-kyed map
获取变量类型最简单的方式是 printf "%t"
。也可以使用 typeof
和 kindf
函数来获取。
https://blog.csdn.net/qq_24095941/article/details/88126207#NamedTemplates