08-Helm手记
Helm手记
Helm是一个用于Kubernetes应用的包管理工具。它允许你定义、安装和升级Kubernetes应用。 Helm 使用称为“Charts”的打包格式,每个Chart都包含用于部署一个具体应用程序的相关文件。
1. 创建Chart
$ helm create example-chart
$ tree example-chart
example-chart
├── charts # 初始为空目录,存放本 Chart 依赖的其他 Charts
├── Chart.yaml # 记录这个Chart的元数据,如名称/描述/版本等
├── templates # 主要。存放k8s部署文件的helm模板,不完全等于k8s模板,扩展go template语法
│ ├── deployment.yaml # 用于定义 Kubernetes Deployment 对象,描述如何部署你的应用程序。
│ ├── _helpers.tpl # 包含了一些 Helm 模板引擎的辅助函数,可以在其他所有模板文件中使用。
│ ├── hpa.yaml # 用于定义 Horizontal Pod Autoscaler 对象,允许根据 CPU 使用率或其他指标动态调整 Pod 的数量。
│ ├── ingress.yaml # 用于定义 K8s Ingress 对象
│ ├── NOTES.txt # 当执行 helm install 时,Helm 将在安装完成后显示这个文件中的注释。
│ ├── serviceaccount.yaml # 用于定义 Kubernetes ServiceAccount 对象,用于为 Pod 中的进程提供身份验证信息。
│ ├── service.yaml # 用于定义 Kubernetes Service 对象,用于将流量路由到你的 Pod
│ └── tests # 包含用于测试 Chart 的测试文件
│ └── test-connection.yaml
└── values.yaml # 该文件包含了 Helm Chart 的默认值,这些值将用于渲染模板文件; 用户可以通过传递自定义的 values.yaml 文件或通过命令行选项来覆盖这些默认值
其中deployment.yaml
和service.yaml
是必须要使用的(即需要修改),其他K8s对象模板文件都是用到时才会改动,包含hpa.yaml
, ingress.yaml
, serviceaccount.yaml
, 在这几个文件的首行包含if .Values.*.enabled
字样表示动态启用,需要在values.yaml
文件中的enabled
字段为true
时才会启用。
1.1 解释 deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "example-chart.fullname" . }}
labels:
{{- include "example-chart.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "example-chart.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
...
其中{{ ... }}
是Go Template语法。大括号中以
.Values
开头的属性值是在values.yaml
中定义的- 其他属性是在
Chart.yaml
中定义的 .Release
开头的是在发布版本时确定
通过Go Template,可以使模板的具体部署操作和部署参数分离开来,各自单独维护。最关键的是可以多个对象复用同一套Chart模板。
1.2 解释 _helpers.tpl
_helpers.tpl
与其他模板文件不同,它可以被除了Chart.yaml
以外的所有模板文件(包括自己)引用。 一般用来定义生成逻辑稍微复杂的变量,比如某项命名/标签等。
一般我们可以直接将变量的生成逻辑写入K8s YAML文件中,但这样会使得它们变得臃肿而降低模板可读性,所以会用到
_helpers.tpl
。
这个文件的语法也很简单,主要使用Helm 模板引擎的各种函数来组合成具体的逻辑。
# 定义一个变量 example-chart.name
# 其值的生成逻辑是:优先取 .Chart.Name,若为空则取.Values.nameOverride
# 然后,|类似管道符号,继续将值调用trunc函数确保字符长度不超过63,最后去掉后缀-
{ { - define "example-chart.name" - } }
{ { - default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" } }
{ { - end } }
1.3 解释 tests 目录
默认这个目录下有个test-connection.yaml
文件,用于定义【部署完成后需要执行的测试内容】,以便验证应用是否成功部署。
执行helm test <RELEASE_NAME>
来运行测试,以便验证部署的Helm资源是否正常运行。下面是一个例子:
# 默认是一个Pod,测试对Service的访问连通性
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "example-chart.fullname" . }}-test-connection"
labels:
{ { - include "example-chart.labels" . | nindent 4 } }
annotations:
"helm.sh/hook": test # 测试资源都有这个注解,它是helm的一个钩子
spec:
containers:
- name: wget
image: busybox
command: [ 'wget' ]
args: [ '{{ include "example-chart.fullname" . }}:{{ .Values.service.port }}' ]
restartPolicy: Never
注意,执行测试的Pod资源在测试完成后应该以(exit 0)成功退出,所以注意command
部分的编写。
在Helm v3中,支持使用以下测试钩子(helm.sh/hook
)之一:
- test-failure:这是一个针对【失败】情况的测试用例
- test-success:这是一个针对【成功】情况的测试用例(等同于旧版的
test
) - test(向后兼容,等同于
test-success
)
1.4 解释 values.yaml
这是最主要的配置文件,用于定义应用部署的各项参数。比如Pod副本数量,镜像名称等。
通过查看values.yaml
可以知道,默认的配置是一个使用Nginx镜像的Deployment控制器,副本数量为1。并且基于Deployment控制器创建了一个Service, 类型ClusterIP,监听80端口;此外还创建了Pod专属的serviceAccount。Ingress和Hpa配置项默认未启用。
2. 验证Chart
发布前需要对Chart配置格式进行验证:
$ helm lint example-chart
==> Linting example-chart
[INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
在最终执行helm install
进行部署时,会将Chart文件解析为K8s能够识别的各种对象模板以进行部署。 可使用helm install --dry-run --debug [Chart目录位置] <name>
来提前检查Chart生成的k8s对象模板是否正确。
# 其中helm-nginx是发布名称,最后才是chart目录作为参数
helm install --dry-run --debug helm-nginx example-chart
...输出计算后的各模板内容
新版的Helm必须提供发布名称参数,或者提供--generate-name
标志使用自动生成的名称。
3. 发布和查看
$ helm install helm-nginx example-chart
NAME: helm-nginx
LAST DEPLOYED: Mon Dec 4 20:23:48 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
...
查看部署:
$ helm ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
helm-nginx default 1 2023-12-04 20:23:48.653998103 +0800 CST deployed example-chart-0.1.0 1.16.0
# status可以查看最后部署的时间,namespace,状态,递增版本号
$ helm status helm-nginx
NAME: helm-nginx
LAST DEPLOYED: Mon Dec 4 20:23:48 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
...
# --show-resources 列出Chart部署的资源
$ helm status helm-nginx --show-resources
NAME: helm-nginx
LAST DEPLOYED: Mon Dec 4 20:23:48 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
RESOURCES:
==> v1/ServiceAccount
NAME SECRETS AGE
helm-nginx-example-chart 0 6m5s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
helm-nginx-example-chart ClusterIP 20.1.80.84 <none> 80/TCP 6m5s
==> v1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
helm-nginx-example-chart 1/1 1 1 6m5s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
helm-nginx-example-chart-5b5b69cb9d-nnrpn 1/1 Running 0 6m5s
删除部署(无法回滚):
helm uninstall helm-nginx
4. 打包Chart
当Chart编写和验证完成后,你如果有分发给给其他用户使用的需求(像分享镜像那样)或者需要版本化Chart包,则可以打包Chart到仓库中。
$ helm package example-chart
Successfully packaged chart and saved it to: /mnt/hgfs/go_dev/k8s-tutorial-cn/helm/example-chart-0.1.0.tgz
升级Chart
升级表示要对Chart配置进行大或小的修改,并且更新Chart.yaml
的版本号。在其中会有两个意义不同的版本号:
# 打包时增加的版本号
version: 0.1.0
# 发布应用时增加的版本号
appVersion: "1.16.0"
其中version
是helm search xxx
输出的Chart Version。helm search xxx --versions
会输出每个Chart的所有历史版本。
5. 发布的升级、回滚和删除
5.1 升级
刚才我们已经发布了example-chart
,命名为helm-nginx
,其Chart.yaml
中的appVersion
为1.16.0
。现在我们修改appVersion
为1.16.1
来模拟升级所做的修改 ,然后更新发布。
# 首先修改Chart.yaml中的appVersion为 1.16.1
# 然后更新发布,--description 增加发布说明
$ helm upgrade helm-nginx example-chart
Release "helm-nginx" has been upgraded. Happy Helming!
NAME: helm-nginx
LAST DEPLOYED: Mon Dec 4 20:57:14 2023
NAMESPACE: default
STATUS: deployed
REVISION: 2
...
# APP VERSION 已更新
$ helm ls
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
helm-nginx default 2 2023-12-04 20:57:14.959204562 +0800 CST deployed example-chart-0.1.0 1.16.1
$ helm upgrade helm-nginx example-chart
Release "helm-nginx" has been upgraded. Happy Helming!
NAME: helm-nginx
LAST DEPLOYED: Mon Dec 4 20:57:14 2023
NAMESPACE: default
STATUS: deployed
REVISION: 2
...
如果最后的Chart参数是引用某个仓库中的Chart(引用形式为repo/chart_name
),此时可以使用helm upgrade helm-nginx example-chart --version x.x.x
来指定Chart版本进行升级。
如果是本地的Chart目录,那--version
参数就无效了,会直接使用所引用目录下的Chart配置进行升级。无论你是否修改了Chart中的任何一个文件 ,Helm都会为发布增加REVISION
号。 当然,实际的K8s对象如Deployment只会在模板变化时重新部署Pod。
实际环境中,我们通常会使用-f values.yaml
参数来指定配置文件(或使用--set
指定某个配置参数)进行升级。使用helm upgrade -h
查看全部参数。
例如:
helm upgrade helm-nginx example-chart -f example-chart/values.yaml --set "serviceType=NodePort"
其中--set
可以指定多个键值对参数(只用于替换values.yaml
中的配置),使用helm show values example-chart
查看Chart的values.yaml
配置。 此外,它还有一些细节上的规范(比如如何设置值为数组的字段),可以参考以下文档:
最后,说一点笔者的个人建议。在实际的项目开发中,建议只需要在每个服务目录下保留
values.yaml
即可,而不需要保留Chart.yaml
来定义其APP VERSION, 因为这样就免去了在每个服务目录下维护两个helm配置文件的麻烦。在发布时我们只需要使用--description
来简述 本次发布的具体内容即可,并可以直接将镜像tag作为发布说明。这样也可以为回滚提供帮助。Helm不支持在Upgrade时设置
appVersion
,这是难以理解的。在 #3555 这个讨论时间长达三年的 Issue中,官方最终也没有支持这种方式,而是推荐使用helm package --app-version
的方式来设置appVersion
,但打包就需要部署Helm仓库,增加了运维成本。 社区中的另一种非常规做法则是在更新发布前使用sed
命令修改了Chart.yaml
中的appVersion
。
5.2 回滚
查看helm发布的记录:
# 在upgrade时使用--description设置的说明会覆盖这里的 DESCRIPTION
$ helm history helm-nginx
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Mon Dec 4 20:23:48 2023 superseded example-chart-0.1.0 1.16.0 Install complete
2 Mon Dec 4 20:57:14 2023 superseded example-chart-0.1.0 1.16.1 Upgrade complete
注意:REVISION
是永远递增的。
回滚到指定REVISION
:
# 1是REVISION,不指定就默认上个REVISION
$ helm rollback helm-nginx 1
Rollback was a success! Happy Helming!
注意,Helm默认最多保留10条发布记录,也就是说,当REVISION
为11的时候(只能看到2~11的记录),1就被删除了,也不能回滚到1了。
5.3 删除
新版本中,helm delete RELEASE-NAME
命令已经不再保留发布记录了,而是彻底删除发布涉及的所有K8s对象和Helm中的记录。
delete
可以使用关键字uninstall/del/un
进行等价替换。
6. 钩子
Helm提供了钩子(Hook)功能,允许在Helm资源的安装前/后、删除前/后等特定时机执行特定的操作。 一般使用钩子来执行以下任务:
- 升级之前检查环境是否具备升级的条件
- 安装前先创建一些基础资源,如ConfigMap、Secret等
- 删除后执行一些清理工作
所有Helm钩子:
Hook | 作用 |
---|---|
pre-install | 在渲染模板之后,在 Kubernetes 中创建任何资源之前执行 |
post-install | 将所有资源加载到 Kubernetes 之后执行 |
pre-delete | 在从 Kubernetes 删除任何资源之前对删除请求执行 |
post-delete | 删除所有发行版资源后,对删除请求执行 |
pre-upgrade | 在呈现模板之后但在更新任何资源之前,对升级请求执行 |
post-upgrade | 升级所有资源后执行升级 |
pre-rollback | 在呈现模板之后但在回滚任何资源之前,对回滚请求执行 |
post-rollback | 修改所有资源后,对回滚请求执行 |
test | 调用Helm test子命令时执行 |
在1.3节【解释tests目录】中我们已经看见过钩子是通过在Pod资源(其他k8s资源也可)中使用注解来使用的。下面是一个例子:
# 通常在Pod和Job中使用
annotations:
"helm.sh/hook": post-install,post-upgrade
所有钩子关联的资源都是串行阻塞加载的,当使用钩子的资源达到Ready
状态时, Helm会继续加载下一个钩子。如果一个资源加载失败,则不会继续加载后续的资源。
针对Pod和Job以外的资源,一旦K8s将资源标记为已加载(已添加或已更新),资源会被认为是
Ready
。
此外,还可以定义:
- 钩子关联资源的权重,这决定了钩子资源加载顺序
- 钩子关联资源的删除策略,这决定了删除钩子资源的时机
示例如下:
# 权重是字符串形式的数字,支持负数和正数,按照升序执行
"helm.sh/hook-weight": "-5"
# 删除策略
# before-hook-creation 新钩子启动前删除之前的资源 (默认)
# hook-succeeded 钩子成功执行之后删除资源
# hook-failed 如果钩子执行失败,删除资源
"helm.sh/hook-delete-policy": hook-succeeded
你可以通过 Kibana-templates 来进一步学习钩子的使用。