微服务和容器化给复杂应用部署与管理带来了极大的挑战。Helm是目前Kubernetes服务编排领域的唯一开源子项目,做为Kubernetes应用的一个包管理工具,可理解为Kubernetes的apt-get / yum,由Deis 公司发起,该公司已经被微软收购。Helm通过软件打包的形式,支持发布的版本管理和控制,很大程度上简化了Kubernetes应用部署和管理的复杂性。
随着业务容器化与向微服务架构转变,通过分解巨大的单体应用为多个服务的方式,分解了单体应用的复杂性,使每个微服务都可以独立部署和扩展,实现了敏捷开发和快速迭代和部署。但任何事情都有两面性,虽然微服务给我们带来了很多便利,但由于应用被拆分成多个组件,导致服务数量大幅增加,对于Kubernetest编排来说,每个组件有自己的资源文件,并且可以独立的部署与伸缩,这给采用Kubernetes做应用编排带来了诸多挑战:
- 管理、编辑与更新大量的K8s配置文件
- 部署一个含有大量配置文件的复杂K8s应用
- 分享和复用K8s配置和应用
- 参数化配置模板支持多个环境
- 管理应用的发布:回滚、diff和查看发布历史
- 控制一个部署周期中的某一些环节
- 发布后的验证
而Helm可以帮助我们解决这些问题。
Helm把Kubernetes资源(比如deployments、services或 ingress等) 打包到一个chart中,而chart被保存到chart仓库。通过chart仓库可用来存储和分享chart。Helm使发布可配置,支持发布应用配置的版本管理,简化了Kubernetes部署应用的版本控制、打包、发布、删除、更新等操作。
本文主要对Helm Hook进行简单的介绍和使用说明。
关于Helm的安装、介绍与使用请参考:
简化Kubernetes应用部署工具-Helm之Release配置
Helm Hook
在简化Kubernetes应用部署工具-Helm之应用部署文章中,展示了采用Helm的一次应用release的生命周期过程,涵盖了发布(helm install)、更新(helm upgrade)、回滚(helm rollback)与删除(helm delete)等部分。为了允许chart开发者在应用release的生命周期中某些关键的时间点上,执行一些操作来更好的服务于release的需求,为此Helm提供了hook机制。
举例说明什么是一个Helm hook,比如ConfigMap或Secret要先于其他chart被加载,或希望在更新/回滚/删除一个release之前能够安全的关闭服务,都可以通过Helm hook实现。
Hook和普通模板文件基本相同,但其可以通过加入一些特殊的注释(annotation)与普通模板文件加以区分,下文会介绍hook的基本格式和用法。
支持Hook类型
Helm提供了如下hook供chart开发者使用:
- pre-install:在模板文件被渲染之后、而在Kubernetes创建任何资源创建之前执行。
- post-install:在Kubernetes加载全部的资源之后执行。
- pre-delete:在Kubernetes删除任何resource之前执行。
- post-delete:在一个release的全部资源被删除之后执行。
- pre-upgrade:在模板渲染之后,而在Kubernetes加载任何资源之前执行。
- post-upgrade:在Kubernetes更新完全部resource之后执行。
- pre-rollback:在模板被渲染之后、而在Kubernetes执行对全部resource的回滚之前。
- post-rollback:在Kubernetes的全部resource已经被修改之后执行。
Hook与Release生命周期
上面已经提到,Hook允许chart开发者在release生命周期的一些时间点上可以执行一些操作。以helm install为例,默认情况下其基本生命周期如下:
- 用户执行helm install foo
- Chart被加载到Helm server-Tiller
- 经过一些用户自定义case验证,Tiller用chart中的Values.yaml等渲染foo模板
- Tiller加载渲染好的Kubernetes resource到Kubernetes集群
- Tiller返回release名称等数据给Helm client
- Helm Client退出
如果在install的生命周期内定义pre-install与post-install 2个hook,那么install的生命周期会变成如下序列:
- 用户执行helm install foo
- Chart被加载到Helm server-Tiller
- 经过一些用户自定义case验证,Tiller用chart中的Values.yaml等渲染foo模板
- Tiller准备执行pre-install hook(加载hook resource到Kubernetes)
- Tiller按hook的权重对hook进行排序,对相同权重的hook按照字母从a-z顺序排序
- Tiller按照权重由低到高顺序加载hook
- Tiller等待hook状态变为就绪(”Ready”)
- Tiller加载resource到Kubernetes
- Tiller执行post-install hook(加载hook resource)
- Tiller等待hook状态变为就绪
- Tiller返回release名称等数据给Helm client
- Helm Client退出
所谓hook就绪状态,取决于hook中定义的Kubernetes resource的类型,比如,对于Job类型的resource,就绪状态是job成功构建完成,如果job构建失败,release也失败了。而对于其他类型的resource,一旦resource加载(添加或更新)到Kubernetes,其状态被认为是就绪状态。
Hook权重
前面提到,一个hook可以对应多个resource,例如, secret与config map做为一个pre-install hook.
同时一个resource也可以有多个hook。
annotations: "helm.sh/hook": post-install,post-upgrade
Helm支持一个hook中的resource设置权重,Helm推荐通过对resource设置权重的方式,按权重大小加载resource。对于权重数值可以设为负数,权重的大小由负数到正数顺序。如果未设置权重,resource的加载是顺序执行的,但执行顺序并不会保证(Helm 2.3.0开始,相同权重的执行顺序按照字母a-z顺序执行,但未来版本可能会对相同权重的资源执行顺序有所变化)。
写一个Hook
Hook与普通的Kubernetes声明文件一样,只是在文件metadata中加了特殊的注释(annotations)。但由于Hook文件是模板文件,所以其也具备全部的模板文件特性,包括从内置对象 .Values
, .Release
, and .Template
获取渲染值等。
例如下面的模板中,定义了一个Job类型的Kubernetes resource,并定义了一个post-install hook,该hook的用途是当Kubernetes加载全部resource完毕后,按照Values对象设置的值sleepyTime执行sleep操作(如sleepyTime未设置,那么sleep 10秒)。
apiVersion: batch/v1 kind: Job metadata: name: "{{.Release.Name}}" labels: heritage: {{.Release.Service | quote }} release: {{.Release.Name | quote }} chart: "{{.Chart.Name}}-{{.Chart.Version}}" annotations: # This is what defines this resource as a hook. Without this line, the # job is considered part of the release. "helm.sh/hook": post-install "helm.sh/hook-weight": "-5" "helm.sh/hook-delete-policy": hook-succeeded spec: template: metadata: name: "{{.Release.Name}}" labels: heritage: {{.Release.Service | quote }} release: {{.Release.Name | quote }} chart: "{{.Chart.Name}}-{{.Chart.Version}}" spec: restartPolicy: Never containers: - name: post-install-job image: "alpine:3.3" command: ["/bin/sleep","{{default "10" .Values.sleepyTime}}"]
Hook资源删除
Helm支持2种删除hook resource策略:
- hook-succeeded
- hook-failed
当使用"helm.sh/hook-delete-policy"
注释(annoation)时,删除hook resource支持2种策略:"hook-succeeded"
与 "hook-failed"
。当删除策略为 "hook-succeeded"
时,hook执行成功后,该hook会被Tiller删除。而删除策略为 "hook-failed"
时,hook在执行过程中失败后,该hook会被Tiller删除。
Hook resource删除策略举例:
annotations: "helm.sh/hook-delete-policy": hook-succeeded
利用Hook处理服务启动顺序依赖
所谓服务依赖指的是启动一个服务,依赖于另外服务。这个时候我们就需要设置服务依赖关系来处理了。可以通过Helm hook来实现服务启动依赖的处理:
举例:serviceA服务依赖serviceB, serviceA的pre-install hook中实现
apiVersion: batch/v1 kind: Job metadata: name: "{{.Release.Name}}" labels: chart: "{{.Chart.Name}}-{{.Chart.Version}}" annotations: "helm.sh/hook": pre-install "helm.sh/hook-weight": "-5" spec: template: metadata: name: "{{.Release.Name}}" labels: chart: "{{.Chart.Name}}-{{.Chart.Version}}" spec: restartPolicy: Never containers: - name: pre-install-job image: "registry.docker.dev.fwmrm.net/busybox:latest" command: ['sh', '-c', "curl --connect-timeout 3 --max-time 5 --retry 10 --retry-delay 5 --retry-max-time 60 --retry-connrefused serviceB:portB/pathB/"]
Helm hook的是一种串行且阻塞式操作(blocking operation),所谓阻塞指的同一个chart,在同一个时刻只有一个hook执行,其他hook以及release的生命周期的其他行为活动(helm install, helm upgrade, helm rollback等)都会被阻塞(block),而串行指的是只有当一个hook成功执行完毕才会执行其他hook。
以上面的pre-install hook的例子说明,pre-install hook的job会先于helm install 执行,且在pre-install hook的job执行过程中,其他的release周期活动已经其他hook都不会被执行。当且仅当serviceB:portB/pathB/在指定时间内服务可达时,pre-install hook才会成功执行,余下的hook以及release其他操作才可以继续执行。
把被依赖的服务是否启动的逻辑判断,放在依赖服务的pre-install hook中,利用Helm hook的是一种串行且阻塞式操作(blocking operation)的特性,如果hook执行失败,那么对应的一次release也就失败了,这就达到了服务启动依赖的处理效果。
本文转自中文社区-简化Kubernetes应用部署工具-Helm之Hook