Kubernetes 资源编排系列之二: Helm 篇

作者 凌可(彭兰舒) 雪尧(郭耀星)
这是我们的《Kubernetes 资源编排系列》的第二篇——Helm 篇,在上篇(《Kubernetes 资源编排系列之一: Pod YAML 篇》,可从文末链接直达)我们见识到了 Pod YAML 的强大能力,在 k8s 的集群中,所见之处皆是 YAML。YAML 多了之后,大家就希望有一种方案能将海量的 YAML 管理起来。于是本篇我们来介绍一下 Helm。
Helm 是什么 Helm 是 Kubernetes 生态系统中的一个软件包管理工具,类似于 Ubuntu 中的 apt、CentOS 中的 yum 等,它用于自动创建、打包、配置和部署应用程序和服务到 Kubernetes 集群。
Kubernetes 为用户提供了自动部署、扩展和管理容器化应用程序的能力,然而用户部署在 Kubernetes 集群上的应用可能会非常复杂,一个典型的应用(以 SREWorks 为例)通常会有一系列对象进行内部交互来使得程序正常运转。
首先我们需要 Deployment 用于部署我们想要运行的 pods,例如 Mysql 数据库;StorageClass 用于动态分配存储卷;Persistent Volume(PV)用于储存数据库;Service 用于指定 pod 的访问策略使得外部可以访问 pod 的服务;Job 用于执行任务保证指定数量的 pods 部署成功……
Kubernetes 资源编排系列之二: Helm 篇
文章图片

在 Helm 出现之前,每个对象都需要一个单独的 YAML 文件来配置,然后通过??kubeclt apply??逐一创建这些对象。如果需要对其中的一些内容进行更改,必须找到对应的 yaml 文件并在其中找到对应的属性;而如果想要升级其中的一些组件,也要仔细地编辑多个文件,防止错误和遗漏;当要删除这个应用程序时,也要手动删除其对应的所有组件……
Kubernetes 并不是从“应用程序”的层面来管理集群中的各个组件,它只是将用户提交上去的 yaml 保存在集群中并各自运行,实际上它并不知道用户声明的各个对象,例如 Job、Service、StorageClass、Deployment 等,是同一个应用程序下不同的组成成分。当应用较为复杂的时候,要记住变量的位置并人工执行操作是一件冗余且复杂的事,并且会给多人协作开发应用增加难度。
在这样的基础上,Helm 应运而生,为开发人员提供了模块化的应用管理工具,降低了 Kubernetes 用户的工作量和工作复杂度。
Helm 是怎么做的? Helm 将上述对象都看作一个程序包内的部分, 当我们想要对程序执行操作的时候,不需要告诉 helm 我们要对哪个对象进行变更,只用传入程序名称,Helm 会帮助我们对程序下的每个对象执行相应的操作。
这个包含了一组 yaml 文件的程序包,就叫做 Helm Chart。
Chart 是一个描述 Kubernetes 相关资源的文件集合,它的格式如下:

└── sreworks-chart/ ├── Chart.yaml# 文件包含了对该chart的描述 ├── values.yaml# 文件包含了导入模版中的chart的默认值,会在用户执行helm install或helm upgrade时被覆盖 ├── charts/# 目录包含依赖的其他chart ├── templates/# 目录包括了模板文件 └── ...

开发时通常不会将 name 硬编码在资源中,用户可以通过插入发布名称来生成 name 字段。
所以我们的 job.yaml 就变为了:
apiVersion: v1 kind: Job metadata: name: {{ .Release.Name }}-init-job

模板命令??{{ .Release.Name }} ??将发布名称注入了模板。值作为一个命名空间对象传给了模板,用点(.)分隔每个命名空间的元素。
在执行??helm install??命令时,模版渲染引擎会将括在??{{ ??和 ??}} ??之间的模版命令替换为 values.yaml 中定义的值或用户通过??--set??设置的值,并将渲染后的文件发送到??templates/??目录中,然后收集结果并发送给 Kubernetes。
可以通过 helm 命令对 chart 执行相应的操作:
// 安装 $ helm install sreworks . -n sreworks、// 升级 $ helm upgrade sreworks . -n sreworks// 回滚 $ helm rollback sreworks -n sreworks// 卸载 $ helm uninstall sreworks -n sreworks

还能用 Helm 创建自己的 charts,也可以从 Helm 仓库中下载。
社区的 Helm Chart 仓库位于 Artifact Hub,可以下载其他人上传的 chart 到本地使用,也可以将自己制作的 chart 上传到仓库中。同时 Helm 也支持创建并运行私有仓库,供个人和组织内部使用。
Helm 的优点
  • 生命周期管理:可以实现对组件实例的查询、安装、卸载、升级、回滚
  • 方便的命令行:对于简单变量,可以在部署的同时指定对应的参数,方便部署
  • 插件和工具生态:作为 CNCF 项目,Helm 已经变成了 K8S 基础生态的一部分,各种各样的外部系统都会对它进行默认支持,CICD 工具集成方面有得天独厚的优势;同时用户能够从社区中获取丰富的专业知识和共享的 Chart 包
  • 确保 Secret 安全性:一些敏感数据在 Kubernetes 中会存储为文本文件,作为 Helm 模版和值的一部分,而helm-secrets插件为关键信息提供秘密管理和保护,将相关值进行加密
  • Chart 调试功能:Helm 提供了一些命令让用户在安装前测试资源的正确性,检查 Chart 是否正确生成,例如??helm lint??;??helm install —dry-run — debug??等
Helm 的缺点
  • 应用定制受限于预置变量:Chart 是一种模板,Chart 的用户仅能通过对 values 的控制来定制组件的部署行为,模板中没有提供变量的位置,是无法在下游直接进行变更。
  • YAML 文件的无序下发: 在 YAML 文件变多之后,尤其在 Operator-CRD 场景下,YAML 下发需要存在一定的先后顺序。helm 中利用一个叫 crd 目录来进行优先下发来避免问题。
  • 应用安装状态感知:对于 Helm 而言,将所有 Chart 中的模版推到 Kubernetes 之后,它的 install 过程就结束了,它并不关心 yaml 中配置的各个组件在 Kubernetes 中是否能有效运行,而这恰恰是用户最关心的部分,因此使用 Helm 安装可能会出现用户无法感知的异常
SREWorks 对 Helm 的能力补充 部署 SREWorks 时的 Helm 安装进度反馈
SREWorks 平台自身的安装也是通过 Helm 包来实现的,常常会有一些 SREWorks 的用户以为 helm install 执行完就安装完了。事实上这个时候安装才刚刚开始。我们希望用户在这个过程中能够感知到具体的安装进度和异常反馈,于是我们在 helm install 执行完后提醒用户(该功能将于 v1.3 版本上线):
Please execute following command in terminal to trace the install progress:kubectl logs job.batch/sreworks-progress-check -nsreworks -fAfter install finished (5-10mins) open the following URL in your browser:http://xxxx/#/account: admin password: *****

在这个 sreworks-progress-check 的 job 中,我们实现了对 SREWorks 安装进度全跟踪。
Kubernetes 资源编排系列之二: Helm 篇
文章图片

我们编写了进度查询和错误诊断脚本,将其包装成 SREWorks 的一个 Job 和其他所有组件一起同步部署,这样用户就能在终端实时查看安装进度,并能在异常出现时及时进行排查。
进度查询:
Kubernetes 资源编排系列之二: Helm 篇
文章图片

错误诊断:
Kubernetes 资源编排系列之二: Helm 篇
文章图片

SREWorks 中的 Helm 组件状态统一管理
鉴于 Helm 并不跟踪各个组件的部署状态,在 SREWorks 中,我们复用 AppManager 已有的 Groovy 脚本托管能力,自己编写了一个小 Groovy 脚本,目的在于等待终态 + 获取数据。
代码如下:
getStatus(request) { def client = getKubeClient(kubeconfig from parameters) def service = client.services().inNamespace(namespace).withName(name).get() def response = new JSONObject() if (service.get("loadbalancer", "ingress", 0, "ip") not empty) { response.put("spec.metadata.annotations.vvpSlb", service.get("loadbalancer", "ingress", 0, "ip")) return Status.builder().response(response).status("SUCCESS").build(); } else { return Status.builder().status("RUNNING").build(); } }

上述流程完成了 Helm 的安装、终态等待及数据获取。
上述 getStatus() 函数除了部署过程会刷 (5s/次);部署完之后也会一直刷,不过频率逐步降低到 5min/次。作为状态感知的数据来源。
SREWorks 中的 Helm 组件顺序部署 前面也提到过 Helm 的 YAML 文件无序下发针对大型工程而言,会有一定的影响。SREWorks 的 Appmanager 基于 OAM 模型实现了 workflow 能力,能够支持多个 Helm 组件按照 DAG 图的顺序部署。
SREWorks 应用 Helm 组件实践
Tips: 虽然 Helm 官方将自身托管的 Chart 对应的包称为应用 (Application),但在一个真实的复杂应用 (Appliation) 下,Helm Chart 更像是应用 (Application) 中的组件 (Component)。故在 SREWorks 中将 Helm 托管的 Chart 归为组件 (Component)。
点击进入 SREWorks 的“运维开发”应用,点击运维开发-后端开发-Helm 组件就能看到 Helm 组件列表,图中所示均为安装在本地的 Helm 组件。
Kubernetes 资源编排系列之二: Helm 篇
文章图片

点击上图中“新增 Helm 组件的按钮”,就可以将 Helm 组件添加到应用中,而组件来源可以是社区仓库也可以是代码仓库。针对社区中的开源软件,可以直接使用社区仓库中持续更新维护的 helm 源。
Kubernetes 资源编排系列之二: Helm 篇
文章图片

Kubernetes 资源编排系列之二: Helm 篇
文章图片

只要在页面上点击 Helm 组件添加,SREWorks-Appmanager 就会根据这些信息会自动生成标准的 OAM 模型 YAML 文件。用户无需关心 helm install 的细节,只需要在页面上点击部署就可以实现一个或多个 Helm 组件的部署。
部署完成后可在运维应用-应用部署的页面查看部署记录。同时在应用实例中也可以看到这个应用。
Kubernetes 资源编排系列之二: Helm 篇
文章图片

Kubernetes 资源编排系列之二: Helm 篇
文章图片

SREWorks 也在公共市场中添加了几个开源的应用组件供用户使用(持续更新中~),同时也支持用户将自己制作或部署的 Helm 组件上传到本地市场中。
Kubernetes 资源编排系列之二: Helm 篇
文章图片

总结 在 SREWorks(Appmanager)的应用体系中,对于承载组件 (Component) 这个概念而言,Helm 是再合适不过的工具。K8S YAML 或 Kustomize(下一篇会提到) 会让使用者沉浸于过多的细节中难以自拔,而 Helm 非常明确自己的选择:把复杂交给开发者,把简单交给使用者。Helm 的自定义参数可以让开发者把 Chart 包装为一个黑盒,并明确这个黑盒可以接受什么参数。使用者不用关心黑盒里面是什么,只需要调整这些开发者暴露出来的参数来满足自己的需求即可。开发者、使用者的边界在组织中可以被正常映射为研发团队和 SRE 团队,在认知和共识层面无需进一步投入成本。
【Kubernetes 资源编排系列之二: Helm 篇】后续文章我们会分享更多的 Kubernetes 组件和应用管理工具,均会发布在我们的公众号“阿里智能运维”上,请大家持续关注~也欢迎大家在公众号后台留言想了解的内容和感兴趣的相关话题,与 SREWorks 团队进行交流。

    推荐阅读