1 Chart 结构
testapi-chart
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── NOTES.txt
│ └── service.yaml
└── values.yaml
Chart.yaml用于描述这个 chart,包括名字,描述信息以及版本。values.yaml用于存储 templates 目录中模板文件中用到的变量。模板文件一般是 Go 模板。如果你需要了解更多关于 Go 模板的相关信息,可以查看 Hugo (https://gohugo.io) 的一个关于 Go 模板的介绍 (https://gohugo.io/templates/go-templates/)。NOTES.txt用于向部署该 chart 的用于介绍 chart 部署后的一些信息。例如介绍如何使用这个 chart,列出缺省的设置等。charts目录可以包含其他的 chart(称之为子 chart)。helpers.tpl放置可以通过 chart 复用的模板辅助对象。
Helm 文件的命名惯例需要注意:
templates/中的大多数文件被视为包含 Kubernetes 清单NOTES.txt是个例外- 命名以下划线(
_)开始的文件则假定 没有 包含清单内容。这些文件不会渲染为 Kubernetes 对象定义,但在其他 chart 模板中都可用。
这些文件用来存储局部和辅助对象,实际上当我们第一次创建 mychart 时,会看到一个名为 _helpers.tpl 的文件,这个文件是模板局部的默认位置。
2 Chart.yaml
文档:Helm | Chart - Chart.yaml 文件
Chart.yaml 文件是 chart 必需的。包含了以下字段:
apiVersion: chart API 版本 (必需)
name: chart名称 (必需)
version: 语义化2 版本(必需)
kubeVersion: 兼容Kubernetes版本的语义化版本(可选)
description: 一句话对这个项目的描述(可选)
type: chart类型 (可选)
keywords:
- 关于项目的一组关键字(可选)
home: 项目home页面的URL (可选)
sources:
- 项目源码的URL列表(可选)
dependencies: # chart 必要条件列表 (可选)
- name: chart名称 (nginx)
version: chart版本 ("1.2.3")
repository: (可选)仓库URL ("https://example.com/charts") 或别名 ("@repo-name")
condition: (可选) 解析为布尔值的yaml路径,用于启用/禁用chart (e.g. subchart1.enabled )
tags: # (可选)
- 用于一次启用/禁用 一组chart的tag
import-values: # (可选)
- ImportValue 保存源值到导入父键的映射。每项可以是字符串或者一对子/父列表项
alias: (可选) chart中使用的别名。当你要多次添加相同的chart时会很有用
maintainers: # (可选)
- name: 维护者名字 (每个维护者都需要)
email: 维护者邮箱 (每个维护者可选)
url: 维护者URL (每个维护者可选)
icon: 用做icon的SVG或PNG图片URL (可选)
appVersion: 包含的应用版本(可选)。不需要是语义化,建议使用引号
deprecated: 不被推荐的chart (可选,布尔值)
annotations:
example: 按名称输入的批注列表 (可选).- apiVersion 对于至少需要 Helm 3 的 chart,
apiVersion字段应该是v2。 - appVersion 应用程序的版本
- dependencies Helm 中,chart 可能会依赖其他任意个 chart。这些依赖可以使用
Chart.yaml文件中的dependencies字段动态链接,或者被带入到charts/目录并手动配置。一旦定义好了依赖,运行helm dependency update就会使用你的依赖文件下载所有你指定的 chart 到你的charts/目录。
3 chart 内置对象
官方文档链接:Helm | 内置对象。内置对象主要包含几个部分:
Release:描述版本发布本身,包含如Release.Name、Release.Revision等对象。Values:从 values.yaml 或用户提供的文件中传入模板的。Chart:Chart.yaml 中的文件内容。Files:在 chart 中提供访问所有的非特殊文件的对象。Capabilities:提供关于 Kubernetes 集群支持功能的信息。Template:包含当前被执行的当前模板信息。
4 Values 对象
其内容来自于多个位置(从上到下优先级递增):
- chart 中的
values.yaml文件 - 如果是子 chart,就是父 chart 中的
values.yaml文件 - 使用
-f参数(helm install -f myvals.yaml ./mychart)传递到helm install或helm upgrade的 values 文件 - 使用
--set(比如helm install --set foo=bar ./mychart)传递的单个参数
5 模板函数和流水线
Helm 有超过 60 个可用函数。其中有些通过 Go 模板语言 本身定义。其他大部分都是 Sprig 模板库。当前的模板函数可以查阅 Helm | 模板函数列表 文档。
模板函数的语法是 functionName arg1 arg2...。quote .Values.favorite.drink 调用了 quote 函数并传递了一个参数(.Values.favorite.drink)。
模板语言其中一个强大功能是 管道 概念,例如下面形式:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | quote }}
food: {{ .Values.favorite.food | upper | quote }}模板中频繁使用的一个函数是 default:
drink: { { .Values.favorite.drink | default "tea" | quote } }对于模板来说,运算符(eq, ne, lt, gt, and, or 等等) 都是作为函数来实现的。在管道符中,操作可以按照圆括号分组。
include 和 required 两个特殊的模板方法
Helm 中也额外添加了两个特殊的模板方法:include 和 required。include 方法允许你引入另一个模板,并将结果传递给其他模板方法。
比如,这个模板片段包含了一个叫 mytpl 的模板,然后将其转成小写,并使用双引号括起来。
value: { { include "mytpl" . | lower | quote } }下面这个包含的模板称为 toYaml,传值给 $value,然后将这个模板的输出传给 indent 方法。
{ { include "toYaml" $value | indent 2 } }假设我们有一个模板文件 _helpers.tpl,其中定义了一些辅助函数:
# _helpers.tpl
{{- define "mychart.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "mychart.labels" -}}
app: {{ include "mychart.fullname" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- end -}}# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: { { include "mychart.fullname" . } }
labels: { { include "mychart.labels" . | nindent 4 } }
spec:
replicas: { { .Values.replicas } }
template:
metadata:
labels: { { include "mychart.labels" . | nindent 8 } }
spec:
containers:
- name: { { .Chart.Name } }
image: { { .Values.image } }
ports:
- containerPort: { { .Values.port } }required 方法可以让你声明模板渲染所需的特定值。如果这个值是空的,模板渲染会出错并打印用户提交的错误信息。使用方法如下:
{ { required "error_message" .Values.variable } }下面这个 required 方法的例子声明了一个 .Values.who 需要的条目,并且当这个条目不存在时会打印错误信息:
value: { { required "A valid .Values.who entry required!" .Values.who } }6 命名模板
这一节属于对 include 模板方法的补充,相关文档:Helm | 命名模板。
命名模板时要记住一个重要细节:模板名称是全局的。如果您想声明两个相同名称的模板,哪个最后加载就使用哪个。因为在子 chart 中的模板和顶层模板一起编译,命名时要注意 chart 特定名称。
一个常见的命名惯例是用 chart 名称作为模板前缀:{{ define "mychart.labels" }}。使用特定 chart 名称作为前缀可以避免可能因为两个不同 chart 使用了相同名称的模板而引起的冲突。这个规则同样适用于不同的版本,在 chart 名称中添加版本来解决这个问题: {{ define "mychart.v1.labels" }} 和 {{ define "mychart.v2.labels" }}。
声明和使用模板
使用 define 来声明模板
{{- define "MY.NAME" }}
# body of template here
{{- end }}使用 template 来引入模板
{{- 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"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}一般 Helm chart 将这些模板放置在局部文件中,一般是 _helpers.tpl。
模板中使用内置对象
下面是一个包含 chart 名称和版本号的模板。
{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
{{- end }}但是在使用的时候会报错:
$ helm install --dry-run moldy-jaguar ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [unknown object type "nil" in ConfigMap.metadata.labels.chart, unknown object type "nil" in ConfigMap.metadata.labels.version]要查看渲染了什么,可以用 --disable-openapi-validation 参数重新执行: helm install --dry-run --disable-openapi-validation moldy-jaguar ./mychart。结果并不是我们想要的:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: moldy-jaguar-configmap
labels:
generator: helm
date: 2021-03-06
chart:
version:模板中使用 chart 内置对象的时候,需要在 template 调用时传入对应内容。比如,{{- template "mychart.labels" }} 没有传入任何内容,所以渲染的时候无法获得 . 中的对象。
这个的解决办法是传入一个范围给模板就好:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" . }}需要注意的是,传入的内容必须是顶层范围的。
用 include 替代 template 进行管道处理
被替换的模板中文本是左对齐的。由于 template 是一个行为,不是方法,无法将 template 调用的输出传给其他方法,数据只是简单地按行插入。例如下面的模板内容在实际渲染的效果和预期不同。
{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}
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"
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
app_name: mychart
app_version: "0.1.0"更改方法,使用 include + indent 来解决这个问题:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{ include "mychart.app" . | indent 4 }}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
{{ include "mychart.app" . | indent 2 }}7 在模板内部访问文件
有时想导入的是不是模板的文件并注入其内容,而无需通过模板渲染发送内容。文档:Helm | 在模板内部访问文件。
8 流控制
If/Else 基本的条件结构看起来像这样:
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}被设置为 false 的情况:布尔 false、数字 0、空字符串、nil、空集合。
不过,配置 if 语句的时候,可能会出现空白行。关于管理空白语句,可以参考 Helm | 流控制 文档中的说明。
with 操作用来控制变量范围。
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 }}使用 range 操作循环:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
toppings: |-
{{- range .Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}9 变量
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 }}变量在 range 循环中特别有用。可以用于类似列表的对象,以捕获索引和值:
toppings: |-
{{- range $index, $topping := .Values.pizzaToppings }}
{{ $index }}: {{ $topping }}
{{- end }}对于数据结构有 key 和 value,可以使用 range 获取 key 和 value。比如,可以通过 .Values.favorite 进行循环:
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}10 子 chart 和全局变量
子 chart 是指当前 chart 依赖于其他的 chart。例如,Wordpress chart 同时有 mysql 和 apache 作为依赖。有点类似于 Python 的 requirements,可能存在 A 依赖于 B,B 依赖于 C 的情况。
子 chart 有两种配置方式:
- 在 Chart.yaml 中配置 dependencies 属性,并通过 helm dependency update 来下载依赖。该命令会将依赖的 chart 包下载到 charts/ 目录下,为 tar.gz 文件。
- 另一种方式是,直接在 charts/ 目录下配置依赖的 chart。
子 chart 下的 values.yaml 文件配置方式:
- values 文件可以为 chart 及其任何依赖项提供值。比如,WordPress chart 同时有
mysql和apache作为依赖。values 文件可以为以下所有这些组件提供依赖:
title: "My WordPress Site" # Sent to the WordPress template
mysql:
max_connections: 100 # Sent to MySQL
password: "secret"
apache:
port: 8080 # Passed to Apache-
更高阶的 chart 可以访问下面定义的所有变量。因此 WordPress chart 可以用
.Values.mysql.password访问 MySQL 密码。但是低阶的 chart 不能访问父级 chart,所以 MySQL 无法访问title属性。同样也无法访问apache.port。 -
Values 被限制在命名空间中,但是命名空间被删减了。因此对于 WordPress chart,它可以用
.Values.mysql.password访问 MySQL 的密码字段。但是对于 MySQL chart,值的范围被缩减了且命名空间前缀被移除了,因此它把密码字段简单地看作.Values.password。 -
为了让某些 values 可以在多个 chart 内共同访问,Helm 支持了 global 关键字,即全局 values。如下所示,.Values.global.app 可以被子 chart 访问到。
title: "My WordPress Site" # Sent to the WordPress template
global:
app: MyWordPress
mysql:
max_connections: 100 # Sent to MySQL
password: "secret"
apache:
port: 8080 # Passed to Apache- 对于全局变量的作用域:如果子 chart 声明了一个全局变量,那这个变量会向下传递(到子 chart 的子 chart),但不会向上传递到父级 chart。子 chart 无法影响父 chart 的值。并且,父 chart 的全局变量优先于子 chart 中的全局变量。
11 调试模板
调试模板可能很棘手,因为渲染后的模板发送给了 Kubernetes API server,可能会以格式化以外的原因拒绝 YAML 文件。
helm lint是验证 chart 是否遵循最佳实践的首选工具。helm template --debug在本地测试渲染 chart 模板。helm install --dry-run --debug [release] [chart]:我们已经看到过这个技巧了,这是让服务器渲染模板的好方法,然后返回生成的清单文件。helm get manifest [release]: 这是查看安装在服务器上的模板的好方法。helm status [release]追踪 release 的状态helm show chart bitnami/mysqlhelm show values bitnami/wordpress查看 chart 可配置项helm list
12 用户自定义资源??
13 Chart Hook
Helm 提供了一个 hook 机制允许 chart 开发者在发布生命周期的某些点进行干预。比如你可以使用 hook 用于:
- 安装时在加载其他 chart 之前加载配置映射或密钥
- 安装新 chart 之前执行备份数据库的任务,然后在升级之后执行第二个任务用于存储数据。
- 在删除发布之前执行一个任务以便在删除服务之前退出滚动。
可用的钩子:pre/post-install/delete/upgrade/rollback + test
14 Chart Test
如其名,就是用来测试使用 chart 时是否按照预期工作。
15 库类型 Chart
定义了可以在 chart 中使用、由其他用户通过 chart 分享的、可复用的代码片段,从而避免重复并保持 chart 干燥。
使用库 chart 而非维护一个通用 chart,作为一个 chart 类型引入,可以提供:
- 一种明确区分通用和应用 chart 的方法
- 逻辑上阻止安装通用 chart
- 通用 chart 中的未渲染模板可以包含版本组件
- 允许依赖的 chart 使用导入的上下文
16 创建一个 NOTES.txt 文件
17 Helm ignore
18 下一步
Helm | Helm 来源和完整性:Helm 有一个来源工具帮助 chart 用户检测包的完整性和来源。使用基于 PKI,GnuPG 及流行包管理器的行业标准工具,Helm 可以生成和检测签名文件。
Helm | Chart 仓库指南:如何创建和使用 chart 仓库。
Helm | 使用基于 OCI 的注册中心:可以使用具有 OCI 支持的容器注册中心来存储和共享 chart 包。
Helm | Helm 高级技术:后端渲染 + Go SDK + 后端存储
Helm | Kubernetes 分发指南:不同的 K8S 环境(如各公司的 K8S 平台)下使用 Helm 情况。
Helm | 基于角色的访问控制:基于角色的访问控制
Helm | Helm 插件指南:Helm 插件。
Helm | Helm 版本支持策略:Helm 与 K8S 的版本兼容性。
Helm | SQL 存储后端的权限管理:提供用户使用 SQL 存储后端时设置和管理权限的指导。
这里有一些有用的文档链接帮助你创建新的 chart:
- CNCF 的 Artifact Hub 是 chart 不可或缺的资源。
- Kubernetes 文档 提供各种能用到的资源种类的详细案例,从配置映射和密钥到 DaemonSet 和工作负载。
- Helm 的 Chart 指南 阐述了使用 chart 的工作流。
- Helm 的 Chart 钩子指南 说明了如何创建生命周期的钩子。
- Helm 的 Chart 提示和技巧 提供了编写 chart 时的一些有用提示。
- Sprig 文档 提供了超过 60 个模板函数。
- Go 模板文档 说明了模板语法的细节。
- Schelm 工具 用于调试 chart 的辅助工具。
有时候从有经验的开发者那里问问题和获取答案更加容易。最佳方式是访问 Kubernetes Slack 的 Helm 频道: