- 案例大部分都摘自官网。
- 本文所使用到的文件都在这里 附件.zip 。
第一章:Helm 简介
1.1 为什么需要 Helm ?
- Kubernetes 上的应用对象,都是由特定的资源描述组成,包括 Deployment、Service 等,都保存在各自的文件中或者集中写在一个配置文件,然后通过
kubectl apply -f部署。 - 如果应用只由一个或几个这样的服务组成,上面的部署方式就足够了。
- 但是对于一个复杂的应用,会有很多类似上面的资源描述文件,如:微服务架构应用,组成应用的服务可能多达几十、上百个,如果有更新或回滚应用的需求,可能要修改和维护所涉及到大量的资源文件,而这种组织和管理应用的方式就显得力不从心了。并且由于缺少对发布过的应用进行版本管理和控制,使得 Kubernetes 上的应用维护和更新面临诸多的挑战,主要面临以下的问题:
- ① 如何将这些服务作为一个整体管理?
- ② 这些资源文件如何高效复用?
- ③ 应用级别的版本如何管理?
1.2 Helm 介绍
- Helm 是 Kubernetes 的包管理工具,就像 Linux 下的包管理器,如:yum、apt 等,可以很方便的将之前打包好的 yaml 文件部署到 Kubernetes 上。

1.3 Helm 的三大概念
Chart代表着 Helm 包。它包含在 Kubernetes 集群内部运行应用程序,工具或服务所需的所有资源定义。你可以把它看作是 Homebrew formula,Apt dpkg,或 Yum RPM 在Kubernetes 中的等价物。Repository(仓库)是用来存放和共享 charts 的地方。它就像 Perl 的 CPAN 档案库网络 或是 Fedora 的 软件包仓库,只不过它是供 Kubernetes 包所使用的。Release是运行在 Kubernetes 集群中的 chart 的实例。一个 chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 release 。以 MySQL chart为例,如果你想在你的集群中运行两个数据库,你可以安装该 chart 两次。每一个数据库都会拥有它自己的 release 和 release name 。
- Helm 安装 charts 到 Kubernetes 集群中,每次安装都会创建一个新的 release。你可以在 Helm 的 chart repositories 中寻找新的 chart。
- 可以类比 Docker 来理解,Chart 就类似于 Docker 中的镜像(Docker 中的镜像就是一系列文件的集合,Chart 也是一系列文件的集合),Repository(仓库)就类似于 Docker Hub,Release 就类似于 Docker 中的容器(可以根据镜像 run 多个容器)。
第二章:Helm 的安装
2.1 Helm 安装的先决条件
- 想成功和正确地使用 Helm ,需要以下前置条件:
- ① 一个 Kubernetes 集群。
- ② 确定你安装版本的安全配置。
- ③ 安装和配置 Helm 。
- 安装或者使用现有的 Kubernetes 集群:
- 使用 Helm ,需要一个 Kubernetes 集群。对于 Helm 的最新版本,我们建议使用 Kubernetes 的最新稳定版, 在大多数情况下,它是
倒数第二个次版本。 - 应该有一个本地的
kubectl。
- 使用 Helm ,需要一个 Kubernetes 集群。对于 Helm 的最新版本,我们建议使用 Kubernetes 的最新稳定版, 在大多数情况下,它是
- 查看 Helm 和对应支持的 Kubernetes 版本:

2.2 Helm 的安装
- 下载 helm :
wget https://get.helm.sh/helm-v3.6.3-linux-amd64.tar.gz

- 解压 Helm :
tar -zxvf helm-v3.6.3-linux-amd64.tar.gz

- 移动到指定目录:
mv linux-amd64/helm /usr/local/bin/helm

- helm 命令补全:
helm completion bash | sudo tee /etc/bash_completion.d/helm > /dev/null
source /usr/share/bash-completion/bash_completion

2.3 Helm 的常用命令
| 命令 | 描述 |
|---|---|
| create | 创建一个chart并指定名字 |
| dependency | 管理chart依赖 |
| get | 下载一个release。可用的子命令:all、hooks、manifest、notes、values。 |
| history | 获取release历史。 |
| install | 安装一个chart。 |
| list | 列出release。 |
| package | 将chart目录打包到chart存档文件中。 |
| pull | 从远程仓库中下载chart并解压到本地。比如:helm install stable/mysql —untar。 |
| repo | 添加、列出、移除、更新和索引chart仓库。可用的子命令:add、index、list、remove、update。 |
| rollback | 从之前的版本回退。 |
| search | 根据关键字搜索chart。可用的子命令:all、chart、readme、values。 |
| show | 查看chart的详细信息。可用的子命令:all、chart、readme、values。 |
| status | 显示已命名版本的状态。 |
| template | 本地呈现模板。 |
| uninstall | 卸载一个release。 |
| upgrade | 更新一个release。 |
| version | 查看Helm客户端版本。 |
2.4 Helm 的 chart 仓库
2.4.1 概述
- 微软仓库:http://mirror.azure.cn/kubernetes/charts,推荐。
- 阿里云仓库:https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts。
2.4.2 添加 chart 仓库
- 命令:
helm repo add 仓库名 仓库URL地址
- 示例:
helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

2.4.3 查看 chart 仓库列表
- 命令:
helm repo list
- 示例:
helm repo list

2.4.4 从 chart 仓库中更新本地可用 chart 的信息
- 命令:
helm repo update
更新从各自chart仓库中获取的有关 chart 的最新信息。信息会缓存在本地,被诸如 ‘helm search’ 等命令使用。
- 示例:
helm repo update

git 有对应的代码仓库 github ,docker 有对应的镜像仓库 dockerHub,而 helm 也有自己的制品仓库 ArtifactHub。
2.4.5 删除 Chart 仓库
- 命令:
helm repo remove 仓库名
- 示例:
helm repo remove aliyun

第三章:Helm 的基本使用
3.1 使用 chart 部署一个应用
3.1.1 准备工作
- 安装 nfs :略。
3.1.2 查找 chart
- 命令:
# 在Artifact Hub https://artifacthub.io/ 或自己的hub实例中搜索charthelm search hub xxx
# 在本地 helm 客户端中的仓库中搜索charthelm search repo xxx
- 示例:
helm search repo mysql

3.1.3 查看 chart 信息
- 命令:
# 检查chart(目录、文件或URL)并显示所有的内容(values.yaml, Chart.yaml, README)helm show all [CHART]
# 检查chart(目录、文件或URL)并显示Chart.yaml文件的内容helm show chart [CHART]
# 检查chart(目录、文件或URL)并显示values.yaml文件的内容helm show values [CHART]
# 检查chart(目录、文件或URL)并显示README文件内容helm show readme [CHART] [flags]
- 示例:
helm show chart stable/mysql

3.1.4 安装 chart ,形成 release
- 命令:
helm install [NAME] [CHART]
注意:
- 每执行一次 install 命令,就会形成一个 release 。
- mysql 是有状态的应用,如果要想执行成功,必须设置持久化存储,并设置默认的动态供应。
- 示例:
helm install db1 stable/mysql

3.1.5 查看 release 列表
- 命令:
helm list
状态可能是 unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade 或 pending-rollback 。
- 示例:
helm list

3.1.6 查看 release 状态
- 命令:
helm status RELEASE_NAME
状态包括:
- 最后部署时间
- 发布版本所在的k8s命名空间
- 发布状态(可以是: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade 或 pending-rollback)
- 发布版本修订
- 发布版本描述(可以是完成信息或错误信息,需要用—show-desc启用)
- 列举版本包含的资源,按类型排序
- 最后一次测试套件运行的详细信息(如果使用)
- chart提供的额外的注释
- 示例:
helm status db1

3.2 安装前自定义chart配置选项
3.2.1 概述
- 自定义选项是因为并不是所有的 chart 都能按照默认配置运行成功,可能会需要一些环境依赖,例如 PV 。
- 所以我们需要自定义 chart 配置选项,安装过程中有两种方法可以传递配置数据:
- ①
--values(或-f):指定带有覆盖的 YAML 文件。这里可以多次指定,最右边的文件优先。 - ②
--set:在命令行上指定替代。如果两种都用,那么--set的优先级高。
- ①
3.2.2 —values 的使用
- 先将修改的变量写到一个文件中。
helm show values stable/mysql > config.yaml

- 修改文件:
vi config.yaml
-- 修改部分persistence:enabled: trueaccessMode: ReadWriteOncesize: 8GimysqlUser: "k8s"mysqlPassword: "123456"mysqlDatabase: "k8s"

- 使用 —values 来替换默认的配置:
helm install db stable/mysql -f config.yaml

可以根据提示,去 Pod 中查看。
3.2.3 —set 的使用
- 命令:
helm install db --set persistence.storageClass="nfs-client" stable/mysql

3.2.4 其他技巧
- 其实,也可以将 chart 包下载下来,查看并修改:
# --untar 表示下载并解压helm pull stable/mysql --untar

3.2.5 其他技巧
helm install 命令可以从多个来源安装:
- ① chart 存储库。
- ② 本地 chart 压缩包。
- ③ chart 目录。
- ④ 完整的 URL。
示例:从 chart 存储库安装 chart
helm install db stable/mysql

- 示例:从本地 chart 压缩包安装 chart
helm pull stable/mysql
helm install db mysql-1.6.9.tgz

- 示例:从 chart 目录安装 chart
helm pull stable/mysql --untar

- 示例:从远程 URL 安装 chart
helm install db http://mirror.azure.cn/kubernetes/charts/mysql-1.6.9.tgz

3.3 构建一个 Chart
3.3.1 Chart 的文件结构
- chart 是一个组织在文件目录中的集合。目录名称就是 chart 名称(没有版本信息),因此描述 wordpress 的 chart 可以存储在
wordpress/目录中。
wordpress/├── charts # 包含chart依赖的其他chart├── Chart.yaml # 用于描述这个 Chart 的基本信息,包括名字、描述信息以及版本等。├── templates # 模板目录, 当和 values 结合时,可生成有效的Kubernetes manifest文件│ ├── deployment.yaml│ ├── _helpers.tpl # 放置可以通过 chart 复用的模板辅助对象│ ├── hpa.yaml│ ├── ingress.yaml│ ├── NOTES.txt # 用于介绍 Chart 帮助信息, helm install 部署后展示给用户。例如:如何使用这个 Chart、列出缺省的设置等。│ ├── serviceaccount.yaml│ ├── service.yaml│ └── tests│ └── test-connection.yaml└── values.yaml # chart 默认的配置值
3.3.2 创建自定义 chart
- 命令:
helm create chart的名称
注意:这个命令就如同前端的 vue 、react 生成一个脚手架项目一样,后续我们自定义 chart 就是在这个基础上进行修改。
- 示例:
helm create nginx

3.3.3 安装自定义 chart
- 命令:
helm install [NAME] [CHART]
- 示例:
helm install nginxdemo nginx

3.3.4 对自定义 chart 进行打包
- 命令:
helm package [CHART_PATH]
之所以会对自定义的 chart 进行打包,主要的目的是:方便传输(节省带宽)等。
- 示例:
helm package nginx

3.3.6 查看实际模板被渲染后的文件
- Kubernetes 目前是不支持在 yaml 中定义变量的,这也是 Helm 出现的意义,Helm 可以有效的管理变量,并使用模板渲染技术(目前,各种语言都有提供,如:Java 的 JSP 本质上就是一种模板渲染技术)将变量渲染到文件中。
- 有的时候,我们想知道模板(template 目录中的
*.yaml文件)渲染后的效果是什么?那么,就需要使用下面的命令:
helm get manifest RELEASE_NAME
注意:
- 看到 RELEASE_NAME ,就应该知道该命令需要先执行 helm install 命令。
- 该命令其实是将 value.yaml 中的变量的值填充到 template 目录中的
*.yaml文件对应的变量中。
- 示例:
helm get manifest nginx-demo

3.3.7 升级
- 有的时候,我们修改了 chart 的配置,那么就需要对 chart 进行升级了,可以使用
helm upgrade命令:
helm upgrade --set xxx=xxx [RELEASE] [CHART]
helm upgrade -f values.yaml [RELEASE] [CHART]
注意:这边的 xxx=xxx ,就是就是 chart 目录中的 values.yaml 的值。

- 示例:
helm upgrade --set image.tag=1.17 nginx-demo nginx

- 示例:
helm upgrade -f values.yaml nginx-demo nginx

3.3.8 查看升级的版本
- 示例:
helm history RELEASE_NAME
- 示例:
helm history nginx-demo

3.3.9 回滚
- 命令:
helm rollback <RELEASE> [REVISION]
- 示例:
helm rollback nginx-demo 1

3.3.10 卸载
- 命令:
helm uninstall RELEASE_NAME
- 示例:
helm uninstall nginx-demo

第四章:Chart 模板
4.1 概述
- Helm 最核心的就是 Chart 模板,即模板化的 K8s manifests 文件。
- 它本质上就是一个 Go 的 template 模板。Helm 在 Go template 模板的基础上,增加了很多东西。如一些自定义的元数据信息、扩展的库以及一些类似于编程形式的工作流,例如条件语句、管道等等。这些东西都会使得我们的模板变得更加丰富。
4.2 Chart 入门
- 创建一个 Chart 模板:
helm create mychart

- 现在我们将 templates 目录下的文件全部删除:
rm -rf mychart/templates/*

在实际的生产级别的 chart ,基础版本的 chart 信息还是很有用的,此处只是防止干扰而全部删除。
- 在 template 目录下创建一个 configmap.yaml 文件,用于创建 ConfigMap 对象。
vi mychart/templates/configmap.yaml
kind: ConfigMapapiVersion: v1metadata:name: mychart-configmapnamespace: defaultdata:myvalue: "Hello World"

建议使用
.yaml作为 YAML 文件的后缀,以.tpl作为 helper 文件的后缀。
- 安装 Chart :
helm install demo mychart

这样安装当然没有问题,Helm 读取这个模板的时候会原模原样的传递给 k8s ,毕竟我们之前使用 kubectl apply -f xxx.yaml 也是这么写的。
- 我们可以查看 Helm 实际加载的模板:
helm get manifest demo

- 可以注意到,每个文件都是以
---开头(YAML 文件的开头),然后是自定生成的注释行,表示那个模板文件生成了这个 YAML 文档。- 现在,我们就可以看到 YAML 数据确实是 configmap.yaml 文件中的内容、
- 卸载:
helm uninstall demo

- Helm 的功能当然不能就这么点,模板文件 configmap.yaml 中的
metadata.name目前是硬编码,不是很好:
vi mychart/templates/configmap.yaml
kind: ConfigMapapiVersion: v1metadata:name: {{ .Release.Name }}-configmap # {{ .Release.Name }}namespace: defaultdata:myvalue: "Hello World"
注意:由于 DNS 系统的限制,
name:字段长度限制为 63 个字符,所以 helm install [NAME] [CHART] 中的 NAME 不要太长。

{{ .Release.Name }}-configmap中的{{ xxx }}是模板语法,很像 Vue 中的插值语法。- 模板命令
{{ .Release.Name }}将发布名称注入了模板,值作为了一个名称空间对象传递给了模板,用点(.)分隔每个名称空间的元素。Release前面的点表示从作用域最顶层的命名空间开始(稍后会谈作用域)。这样.Release.Name就可解读为通过顶层名称空间开始查找 Release对象,然后在其中找 Name 对象。Release是一个 Helm 的内置对象。稍后会更深入地讨论。但现在足够说明它可以显示从库中赋值的发布名称。
- 安装 Chart ,查看模板命令的结果:
helm install demo1 mychart

- 查看 Helm 实际加载的模板:
helm get manifest demo1

注意:此时
configmap.yaml文件的metadata.name是demo1-configmap而不是原来的mychart-configmap。
- k8s 还有 dry-run(干跑),Helm 当然也有这个功能,可能加速模板的构建速度(我们想测试模板渲染的内容但是又不想安装实际应用,只会将模板渲染的内容输出到控制台上)。
helm install --dry-run demo3 mychart/

注意:helm 干跑命令仅仅用来查看模板渲染的结果,不能保证 k8s 会正常接收生成的模板。
4.3 内置对象
4.3.1 概述
- 对象可以通过模板引起传递到模板中,当然我们也可以通过代码传递对象(如:
with、range)。 - 对象可以非常简单仅仅有一个值,也可以包含其他对象或方法。如:
Release对象可以包含其他对象(如:Release.Name),File对象有一组方法。
4.3.2 Release 对象
- Release 对象是我们可以在模板中访问的顶层对象之一,Release 对象描述了 版本发布本身,包含如下对象。
| 对象 | 说明 |
| —- | —- |
|
Release.Name| release 名称 | |Release.Namespace| 版本中包含的命名空间(如果 manifest 没有覆盖的话) | |Release.IsUpgrade| 如果当前操作是升级或回滚的话,该值将被设置为true| |Release.IsInstall| 如果当前操作是安装的话,该值将被设置为true| |Release.Revision| 此次修订的版本号。安装时是1,每次升级或回滚都会自增 | |Release.Service| release 服务的名称 |
4.3.3 Value 对象
- Value 对象是从 values.yaml 文件和用户提供的文件传进模板的。如:values.yaml 中的值是
favoriteDrink: coffee,那么在 template 目录中的模板文件中,就可以使用{{ .Values.favoriteDrink }}获取到该值。
4.3.4 Chart 对象
- Chart 对象是从 Chart.yaml 文件传进模板的。Chart.yaml 里面的所有数据都可以通过 Chart 获取到,如:
{{ .Chart.Name }}-{{ .Chart.Version }}。 - Chart.yaml 的内容如下:
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的tagimport-values: # (可选)- ImportValue 保存源值到导入父键的映射。每项可以是字符串或者一对子/父列表项alias: (可选) chart中使用的别名。当你要多次添加相同的chart时会很有用maintainers: # (可选)- name: 维护者名字 (每个维护者都需要)email: 维护者邮箱 (每个维护者可选)url: 维护者URL (每个维护者可选)icon: 用做icon的SVG或PNG图片URL (可选)appVersion: 包含的应用版本(可选)。不需要是语义化,建议使用引号deprecated: 不被推荐的chart (可选,布尔值)annotations:example: 按名称输入的批注列表 (可选).
4.3.5 Files 对象
Files: 在chart中提供访问所有的非特殊文件的对象。你不能使用它访问Template对象,只能访问其他文件。 请查看这个 文件访问部分了解更多信息。Files.Get通过文件名获取文件的方法。 (.Files.Getconfig.ini)Files.GetBytes用字节数组代替字符串获取文件内容的方法。 对图片之类的文件很有用Files.Glob用给定的shell glob模式匹配文件名返回文件列表的方法Files.Lines逐行读取文件内容的方法。迭代文件中每一行时很有用Files.AsSecrets使用Base 64编码字符串返回文件体的方法Files.AsConfig使用YAML格式返回文件体的方法
4.3.6 Capabilities 对象
Capabilities: 提供关于 Kubernetes 集群支持功能的信息。Capabilities.APIVersions是一个版本列表Capabilities.APIVersions.Has $version说明集群中的版本 (比如,batch/v1) 或是资源 (比如,apps/v1/Deployment) 是否可用Capabilities.KubeVersion和Capabilities.KubeVersion.Version是Kubernetes的版本号Capabilities.KubeVersion.MajorKubernetes的主版本Capabilities.KubeVersion.MinorKubernetes的次版本Capabilities.HelmVersion包含Helm版本详细信息的对象,和helm version的输出一致Capabilities.HelmVersion.Version是当前Helm语义格式的版本Capabilities.HelmVersion.GitCommitHelm的git sha1值Capabilities.HelmVersion.GitTreeState是Helm git树的状态Capabilities.HelmVersion.GoVersion是使用的Go编译器版本
4.3.7 Template 对象
Template: 包含当前被执行的当前模板信息。Template.Name: 当前模板的命名空间文件路径 (如:mychart/templates/mytemplate.yaml)Template.BasePath: 当前chart模板目录的路径 (如:mychart/templates)
4.3.8 建议
- 内置对象都是以大写字母开始,符合 Go 的命名规范;自定义名称的时候,按照团队约定即可,一般建议首字母小写,以便和内置对象区分。
4.4 values.yaml 文件
- Value 对象来源于多个位置:
- ① chart 中的 values.yaml 文件。
- ② 父 chart 中的 values.yaml 文件。
- ③ 使用 -f 参数(
helm install -f myvals.yaml ./mychart)传递到 helm install 或 helm upgrade 的 values 文件。 - ④ 使用 —set 传递的单个参数。
以上列表有明确的顺序:默认使用 values.yaml ,可以被父 chart 的 values.yaml 覆盖,继而被用户提供的 values 文件覆盖,最后会被 —set 参数覆盖。
示例:
- 删除 values.yaml 中的默认内容,并设置一个参数:
echo "" > values.yaml
echo "favoriteDrink: coffee" > values.yaml

- 编辑 configmap.yaml 文件,读取 values.yaml 文件中配置的值:
vi configmap.yaml
kind: ConfigMapapiVersion: v1metadata:name: {{ .Release.Name }}-configmapnamespace: defaultdata:myvalue: "Hello World"drink: {{ .Values.favoriteDrink }} # 注意 favoriteDrink是Values的一个属性: {{ .Values.favoriteDrink }}

- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

- 使用
--set参数覆盖 values.yaml 中的值:
helm install --set favoriteDrink=slurm test mychart --dry-run

- 示例:values.yaml 设置多结构
- 编辑 values.yaml 文件,并设置内容:
vi values.yaml
favorite:drink: coffeefood: pizza

- 编辑 configmap.yaml 文件,读取 values.yaml 文件中配置的值:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink }}food: {{ .Values.favorite.food }}

- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

4.5 管道和函数
4.5.1 函数
- 到目前为止,我们已经知道了如何将信息传到模板中,但是传入的信息并不能被修改。 而有时我们又希望以一种更有用的方式来转换所提供的数据。
- 我们可以调用模板指令中的 quote 函数将 .values 对象中的字符串属性用引号引起来,然后放到模板中。
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ quote .Values.favorite.drink }} # 注意此处,使用了 quote 函数food: {{ quote .Values.favorite.food }} # 注意此处,使用了 quote 函数

- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

- 模板函数的语法是
functionName arg1 arg2...。在上面的代码片段中,quote .Values.favorite.drink调用了quote函数并传递了一个参数(.Values.favorite.drink)。 - Helm 有超过60个可用函数。其中有些通过 Go模板语言本身定义。其他大部分都是 Sprig 模板库。我们可以在示例看到其中很多函数。
4.5.2 管道
- 模板语言其中一个强大功能是 管道 概念。借鉴UNIX中的概念,管道符是将一系列的模板语言紧凑地将多个流式处理结果合并的工具。换句话说,管道符是按顺序完成一系列任务的方式。
- 现在使用管道父重写上面的示例:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | quote }}food: {{ .Values.favorite.food | quote }}
- 在这个示例中,并不是调用
quote 参数,而是倒置了命令。使用管道符(|)将参数“发送”给函数:.Values.favorite.drink | quote。使用管道符可以将很多函数链接在一起:
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | quote }}food: {{ .Values.favorite.food | upper | quote }}
倒置命令是模板中的常见做法。可以经常看到
.val | quote而不是quote .val。实际上两种操作都是可以的。
4.5.3 default 函数
- 模板中频繁使用的一个函数是
default:default DEFAULT_VALUE GIVEN_VALUE。 这个函数允许你在模板中指定一个默认值,以防这个值被忽略。 - 使用 default 函数重写上面的示例:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | default "tea" | quote }} # 注意此处food: {{ .Values.favorite.food | upper | quote }}

- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

- 从
values.yaml中移除设置:
vi values.yaml
favorite:#drink: coffeefood: pizza

- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

注意:
- 在实际的 chart 中,所有的静态默认值应该设置在 values.yaml 文件中,且不应该重复使用 default 命令(容易出现冗余),但是这个 default 函数很适合计算值,不能声明在 values.yaml 文件中。
- 模板函数列表。
- 对于模板来说,运算符(
eq,ne,lt,gt,and,or等等) 都是作为函数来实现的。 在管道符中,操作可以按照圆括号分组。
4.6 流程控制
4.6.1 概述
- 很多编程语言都提供了流程控制语言,如:if 、while 等,Helm 也不例外。
- Helm 提供了如下的三种流程控制:
if/else, 用来创建条件语句。with, 用来指定范围。range, 提供"for each"类型的循环。
- 除了这些之外,还提供了一些声明和使用命名模板的关键字:
define在模板中声明一个新的命名模板。template导入一个命名模板。block声明一种特殊的可填充的模板块。
4.6.2 if-else
- 语法:
{{ if PIPELINE }}# Do something{{ else if OTHER PIPELINE }}# Do something else{{ else }}# Default case{{ end }}
注意我们讨论的是
管道而不是值。这样做的原因是要清楚地说明控制结构可以执行整个管道,而不仅仅是计算一个值。
- 如果是一下值,管道会被认为是 false :
- 布尔false
- 数字0
- 空字符串
nil(空或null)- 空集合(
map,slice,tuple,dict,array)
- 修改 configmap.yaml 文件:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | default "tea" | quote }}food: {{ .Values.favorite.food | upper | quote }}{{ if .Values.favorite.drink }}mug: "true"{{ end }}

- 此时的 values.yaml 文件的内容如下:
favorite:# drink: coffeefood: pizza
- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

- 修改 values.yaml 文件,将
drink: coffee打开:
vi values.yaml
favorite:drink: coffee # 此处原来的注释关闭了food: pizza

- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

- 我们不可能
{ if }} xxx {{ end }}总写在一行,那么我们对其格式化一下:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | default "tea" | quote }}food: {{ .Values.favorite.food | upper | quote }}{{ if .Values.favorite.drink }}mug: "true"{{ end }}

- 使用
helm install --dry-run测试:
helm install test mychart --dry-run

其实,我是故意的,就是为了引出问题。
- 取消缩进,重新执行下:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | default "tea" | quote }}food: {{ .Values.favorite.food | upper | quote }}{{ if .Values.favorite.drink }}mug: "true" # 注意,此处取消的缩进{{ end }}
helm install test mychart --dry-run

- 此时,又有问题,那就是出现了空行。因为当模板引擎运行时,
{{和}}里面的内容,但是留下的空白完全保持原样。 - YAML 认为空白是有意义的,但是此时的结果和我们预想的有所不同,我们不希望出现这多余的空行。而 Helm 是可以处理此类问题的。
- 模板声明的大括号语法可以通过特殊的字符修改,并通知模板引擎取消空白。
{{-(包括添加的横杠和空格)表示向左删除空白, 而-}}表示右边的空格应该被去掉。
要确保
-和其他命令之间有一个空格。{{- 3 }}表示“删除左边空格并打印3”,而{{-3 }}表示“打印-3”。
- 使用这个语法,修改我们的模板,去掉新加的空白行,重新执行下:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | default "tea" | quote }}food: {{ .Values.favorite.food | upper | quote }}{{- if .Values.favorite.drink }} # 注意,此处向左删除空白mug: "true"{{- end }} # 注意,此处取消的缩进,# 注意,此处向左删除空白
helm install test mychart --dry-run

- 解释上面是如何删除的,用一个
*来代替每个遵循此规则被删除的空白, 在行尾的*表示删除新行的字符:
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata: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 }}
- 那么,如果我们在
{{ }}左边和右边都添加-,并重新执行:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"drink: {{ .Values.favorite.drink | default "tea" | quote }}food: {{ .Values.favorite.food | upper | quote }}{{- if .Values.favorite.drink -}} # 注意,此处向左删除空白mug: "true"{{- end -}} # 注意,此处取消的缩进,# 注意,此处向左删除空白
helm install test mychart --dry-run

出现了错误,是因为将两边的新行都删除了,就像food: "PIZZA"mug:"true",所以,使用-}}需谨慎。
4.6.3 with
with操作。这个用来控制变量范围。回想一下,.是对当前作用域的引用。因此.Values就是告诉模板在当前作用域查找Values对象。- 语法:
{{ with PIPELINE }}# restricted scope{{ end }}
with允许我们为特定对象设定当前作用域(.),比如我们前面一直使用的.Values.favorite,我们可以使用with来将.范围指向.Values.favorite。
- 修改 configmap.yaml 文件,并重新执行:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"{{- with .Values.favorite }}drink: {{ .drink }}food: {{ .food }}{{- end }}
helm install test mychart --dry-run
此时的
.的作用域已经改变了,指向了.Values.favorite。

- 可以优化一下:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"{{- with .Values.favorite }}{{- toYaml . | nindent 2 }}{{- end }}
helm install test mychart --dry-run
with 已经将
.指向了.Values.favorite,toYaml .就相当于toYaml .Values.favorite,将 value.yaml 中的 favorite 下面的值原模原样放到模板中,nindent 2的意思是,在字符串的开头添加新行,并缩进 2 个空格。

4.6.4 range
- 很多编程语言支持使用
for循环,foreach循环,或者类似的方法机制。 在Helm的模板语言中,在一个集合中迭代的方式是使用range操作符。 - 语法:
{{- range .Values.test }}{{ . }}{{- end }}
- 修改 values.yaml 文件,并增加如下的内容:
vi values.yaml
favorite:drink: coffeefood: pizzapizzaToppings: # 增加的内容- mushrooms- cheese- peppers- onions

- 在模板中,使用 range 遍历,并执行一下:
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"{{- with .Values.favorite }}{{- toYaml . | nindent 2 }}{{- end }}toppings: |-{{- range .Values.pizzaToppings }}- {{ . | title | quote }}{{- end }}
helm install test mychart --dry-run

4.7 变量
函数、管道符、对象和控制结构都可以控制,我们转向很多编程语言中更基本的思想之一:变量。 在模板中,很少被使用。但是我们可以使用变量简化代码,并更好地使用
with和range。示例:获取列表键值
vi values.yaml
favorite:drink: coffeefood: pizza

vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"{{- range $key, $val := .Values.favorite }} # 注意这里{{ $key }}: {{ $val | quote }}{{- end }}
helm install test mychart --dry-run

- 示例:解决 with 中不能使用内置对象(使用变量对另一个对象进行命名引用)
vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:myvalue: "Hello World"{{- $relname := .Release.Name -}} # 提前将对象赋值给变量{{- with .Values.favorite }}drink: {{ .drink | default "tea" | quote }}food: {{ .food | upper | quote }}release: {{ $relname }} # 使用 $变量 的方式{{- end }}
helm install test mychart --dry-run

4.8 命名模板
4.8.1 概述
- 我们知道,k8s 是通过标签 Label 来匹配和筛选资源的,如:Service 和 Deployment 都是通过 label 来匹配 Pod 的,那么这部分也是重复的。在 Helm 中,这类在模板中重复的地方,也可以被抽取取来,放在命名模板中(命名模板是全局的,换言之,所有的模板都可以使用)。
- 一个常见的命名惯例是用chart名称作为模板前缀:
{{ define "mychart.labels" }}。使用特定chart名称作为前缀可以避免可能因为 两个不同chart使用了相同名称的模板而引起的冲突。
4.8.2 局部的和_文件
- 目前为止,我们已经使用了单个文件,且单个文件中包含了单个模板。但Helm的模板语言允许你创建命名的嵌入式模板, 这样就可以在其他位置按名称访问。
- 在编写模板细节之前,文件的命名惯例需要注意:
templates/中的大多数文件被视为包含Kubernetes清单NOTES.txt是个例外- 命名以下划线(
_)开始的文件则假定 没有 包含清单内容。这些文件不会渲染为Kubernetes对象定义,但在其他chart模板中都可用。
- 这些文件用来存储局部和辅助对象,实际上当我们第一次创建
mychart时,会看到一个名为_helpers.tpl的文件,这个文件是模板局部的默认位置。
4.8.3 用define和template声明和使用模板
define操作允许我们在模板文件中创建一个命名模板,语法如下:
{{- define "MY.NAME" -}}# body of template here{{- end -}}
- 现在我们将模板嵌入到了已有的配置映射中,然后使用
template包含进来:
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmap{{- template "mychart.labels" . }}data:myvalue: "Hello World"{{- range $key, $val := .Values.favorite }}{{ $key }}: {{ $val | quote }}{{- end }}
- 示例:
vi _helpers.tpl
{{- define "mychart.labels" -}}labels: # 注意,这里空了2个字符generator: helmdate: {{ now | htmlDate }}{{- end -}}

vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmap{{- template "mychart.labels" . }}data:myvalue: "Hello World"{{- range $key, $val := .Values.favorite }}{{ $key }}: {{ $val | quote }}{{- end }}
helm install test mychart --dry-run

- template 指令是将一个模板包含在另一个模板中的方法。但是,template 函数不能用于 Go 模板管道。为了解决该问题,增加include 功能。
vi _helpers.tpl
{{- define "mychart.labels" -}}labels: # 注意,此处没有空格generator: helmdate: {{ now | htmlDate }}{{- end -}}

vi configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmap{{- include "mychart.labels" . | nindent 2 }}data:myvalue: "Hello World"{{- range $key, $val := .Values.favorite }}{{ $key }}: {{ $val | quote }}{{- end }}
helm install test mychart --dry-run

4.9 NOTES.txt 文件
- 在
helm install或helm upgrade命令的最后,Helm 会打印出对用户有用的信息。 使用模板可以高度自定义这部分信息。 - 要在chart添加安装说明,只需创建
templates/NOTES.txt文件即可。该文件是纯文本,但会像模板一样处理, 所有正常的模板函数和对象都是可用的。
- 示例:
Thank you for installing {{ .Chart.Name }}.Your release is named {{ .Release.Name }}.To learn more about the release, try:$ helm status {{ .Release.Name }}$ helm get all {{ .Release.Name }}
4.10 在模板内部访问文件
4.10.1 概述
- 有时想导入的是不是模板的文件并注入其内容,而无需通过模板渲染发送内容。
- Helm 提供了通过
.Files对象访问文件的方法。不过,在我们使用模板示例之前,有些事情需要注意:- 可以添加额外的文件到chart中。虽然这些文件会被绑定。但是要小心,由于Kubernetes对象的限制,Chart必须小于1M。
- 通常处于安全考虑,一些文件无法通过
.Files对象访问:- 无法访问
templates/中的文件 - 无法访问使用
.helmignore排除的文件
- 无法访问
- Chart 不能保留 UNIX 模式信息,因此当文件涉及到
.Files对象时,文件级权限不会影响文件的可用性。
4.10.2 基本示例
我们来写一个读取三个文件到配置映射 ConfigMap 的模板。开始之前,我们会在 chart 中添加三个文件, 直接放到
mychart/目录中。config1.toml:message = Hello from config 1
config2.toml:message = This is config 2
config3.toml:message = This is config 3
每个都是简单的 TOML 文件(类似于 windows 老式的 INI 文件)。我们知道这些文件的名称,因此我们使用
range功能遍历它们并将它们的内容注入到我们的 ConfigMap 中。
apiVersion: v1kind: ConfigMapmetadata:name: {{ .Release.Name }}-configmapdata:{{- $files := .Files }}{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}{{ . }}: |-{{ $files.Get . }}{{- end }}
- 这个配置映射使用了之前章节讨论过的技术。比如,我们创建了一个
$files变量来引用.Files对象。我们也使用了tuple方法创建了一个可遍历的文件列表。 然后我们打印每个文件的名字({{ . }}: |-),然后通过{{ $files.Get . }}打印文件内容。 - 执行这个模板会生成包含了三个文件所有内容的单个配置映射:
# Source: mychart/templates/configmap.yamlapiVersion: v1kind: ConfigMapmetadata:name: quieting-giraf-configmapdata:config1.toml: |-message = Hello from config 1config2.toml: |-message = This is config 2config3.toml: |-message = Goodbye from config 3
4.10.3 Path 辅助对象
- 使用文件时,对文件路径本身执行一些标准操作会很有用。为了实现这些,Helm 从Go的 path包中导入了一些功能。 都使用了与Go包中一样的名称就可以访问。但是第一个字符使用了小写,比如
Base变成了base等等。 - 导入的功能包括:
- Base
- Dir
- Ext
- IsAbs
- Clean
4.10.4 Glob patterns
- 当你的 chart 不断变大时,你会发现你强烈需要组织你的文件,所以我们提供了一个
Files.Glob(pattern string)方法来使用 全局模式的灵活性读取特定文件。 .Glob返回一个Files类型,因此你可以在返回对象上调用任意的Files方法。- 比如,假设有这样的目录结构:
foo/:foo.txt foo.yamlbar/:bar.go bar.conf baz.yaml
- 全局模式下您有多种选择:
{{ $currentScope := .}}{{ range $path, $_ := .Files.Glob "**.yaml" }}{{- with $currentScope}}{{ .Files.Get $path }}{{- end }}{{ end }}
# 或者{{ range $path, $_ := .Files.Glob "**.yaml" }}{{ $.Files.Get $path }}{{ end }}
4.10.5 ConfigMap and Secrets utility functions
在 Helm 2.0.2 及后续版本可用。
- 把文件内容放入配置映射和密钥是很普遍的功能,为了运行时挂载到你的pod上。为了实现它,我们提供了一些基于
Files类型的实用方法。 - 为了进一步组织文件,这些方法结合
Glob方法使用时尤其有用。 - 上面的文件结构使用 Glob时的示例如下:
apiVersion: v1kind: ConfigMapmetadata:name: confdata:{{ (.Files.Glob "foo/*").AsConfig | indent 2 }}---apiVersion: v1kind: Secretmetadata:name: very-secrettype: Opaquedata:{{ (.Files.Glob "bar/*").AsSecrets | indent 2 }}
4.10.6 Encoding
- 您可以导入一个文件并使用模板的base-64方式对其进行编码来保证成功传输:
apiVersion: v1kind: Secretmetadata:name: {{ .Release.Name }}-secrettype: Opaquedata:token: |-{{ .Files.Get "config1.toml" | b64enc }}
- 上面的内容使用我们之前使用的相同的
config1.toml文件进行编码:
# Source: mychart/templates/secret.yamlapiVersion: v1kind: Secretmetadata:name: lucky-turkey-secrettype: Opaquedata:token: |-bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
4.10.7 Lines
- 有时需要访问模板中的文件的每一行。我们提供了一个方便的
Lines方法。 - 可以使用
range方法遍历Lines:
data:some-file.txt: {{ range .Files.Lines "foo/bar.txt" }}{{ . }}{{ end }}
- 在
helm install过程中无法将文件传递到chart外。因此如果你想请求用户提供数据,必须使用helm install -f或helm install --set加载。
第五章:开发自己的 Chart(以 Java 应用为例)
5.1 单体项目
开发步骤:
- ① 使用 helm create 命令创建 chart 。
- ② 进行 chart 目录中的 templates 目录,修改其中的文件,如:deployment.yaml 、service.yaml 等文件。
- ③ 使用
helm install --dry-run demo3 mychart干跑 helm,如果测试没有错误,就执行步骤 ④,否则重复执行步骤 ② 和 ③。 - ④ 使用 helm install 命令安装。
示例:略。
5.2 微服务项目
官网。
假设商城微服务的结构如下所示:

- 在前面的示例中,你一定会见过 charts 目录:

- 创建过程如下所示:

需要注意子 chart 的重要细节:
- 子chart被认为是“独立的”,意味着子chart从来不会显示依赖它的父chart。
- 因此,子chart无法访问父chart的值。
- 父chart可以覆盖子chart的值。
- Helm有一个
全局值的概念,所有的chart都可以访问。
- 其余细节,看官网。
