随着容器技术和微服务架构逐渐被企业接受,在Kubernetes上已经能便捷地部署简单的应用了。但对于复杂的应用或者中间件系统,在Kubernetes上进行容器化部署并非易事,通常需要研究Docker镜像的运行需求、环境变量等内容,为容器配置依赖的存储、网络等资源,并设计和编写Deployment、ConfigMap、Service、Volume、Ingress等YAML文件,再将其依次提交给Kubernetes部署。总之,微服务架构和容器化给复杂应用的部署和管理都带来了很大的挑战。
Helm由Deis公司(已被微软收购)发起,用于对需要在Kubernetes上部署的复杂应用进行定义、安装和更新,是CNCF基金会的毕业项目,由Helm社区维护。Helm将Kubernetes的资源如Deployment、Service、ConfigMap、Ingress等,打包到一个Chart(图表)中,而Chart被保存到Chart仓库,由Chart仓库存储、分发和共享。Helm支持应用Chart的版本管理,简化了Kubernetes应用部署的应用定义、打包、部署、更新、删除和回滚等操作。
简单来说,Helm通过将各种Kubernetes资源打包,类似于Linux的apt-get或yum工具,来完成复杂软件的安装和部署,并且支持部署实例的版本管理等,大大简化了在Kubernetes上部署和管理应用的复杂度。
Helm的整体架构如图10.27所示。

图10.27 Helm的整体架构
Helm主要包括以下组件。
◎ Chart:Helm软件包,包含一个应用所需资源对象的YAML文件,通常以.tgz压缩包形式提供,也可以是文件夹形式。
◎ Repository(仓库):用于存放和共享Chart的仓库。
◎ Config(配置数据):部署时设置到Chart中的配置数据。
◎ Release:基于Chart和Config部署到Kubernetes集群中运行的一个实例。一个Chart可以被部署多次,每次的Release都不相同。
基于Helm的工作流程如下。
(1)开发人员将开发好的Chart上传到Chart仓库。
(2)运维人员基于Chart的定义,设置必要的配置数据(Config),使用Helm命令行工具将应用一键部署到Kubernetes集群中,以Release概念管理后续的更新、回滚等。
(3)Chart仓库中的Chart可以用于共享和分发。
Helm目前有v2和v3两个版本,v3版本在v2版本的基础上大大简化,安全性增强。
在v2版本中,Helm依赖Tiller组件,其系统架构如图10.28所示。Tiller组件用于接收Helm客户端发出的指令,与Kubernetes API Server交互,完成资源对象的部署和管理。但是Tiller组件的默认作用范围是整个集群,对于多租户在不同命名空间中部署应用的场景,权限管理更加复杂。

图10.28 Helm v2的系统架构
从v3版本开始,Helm不再使用Tiller组件,而是将与Kubernetes API Server交互的功能整合到Helm客户端程序中,这样一来,每个Helm v3客户端都能独立设置权限管理,更符合多租户环境的要求。管理员只需为Helm v3客户端设置正确的RBAC权限,租户就能使用各自独立的Helm客户端管理应用了。
此外,Helm的版本是基于Kubernetes的特定版本编译和发布的,不推荐将Helm用于比编译时的Kubernetes版本更高的版本,因为Helm并没有做出向前兼容的保证。
如表10.9所示是Helm版本支持的Kubernetes版本列表。
表10.9 Helm版本支持的Kubernetes版本列表

▼续表

安装Helm的前提条件包括:①Kubernetes集群已就绪;②配置了正确权限的kubeconfig,在Helm与API Server通信时使用;③本地kubectl客户端工具已就绪。
Helm的安装方式有多种选择,例如使用二进制文件安装、使用脚本安装、使用包管理器安装,等等。
(1)使用二进制文件安装。从Helm官方GitHub代码库下载合适的版本,例如helm-v3.4.1-linux-amd64.tar.gz,解压缩后得到二进制文件helm,将其复制到合适的$PATH路径下,例如/usr/bin,即可完成Helm的安装。
(2)使用脚本安装。Helm提供了一个安装脚本来一键安装Helm到系统中,命令如下:

(3)使用包管理器安装。Helm社区为不同的操作系统开发了包管理器来安装Helm。
◎ 对macOS使用Homebrew工具安装Helm:

◎ 对Windows使用Chocolatey工具安装Helm:

◎ 对Debian/Ubuntu使用Apt工具安装Helm:

在Helm安装完成之后,就可以使用Helm命令管理Chart、仓库、部署和应用了。
本节介绍Helm的常见用法,包括Chart仓库的使用、部署应用、更新或回滚Release应用、卸载Release、创建自定义Chart等。
安装好Helm之后,通常需要添加一个Chart仓库,一种常见的选择是添加Helm官方的稳定版Chart仓库:

添加完成后,可以使用helm search命令查询可部署的Chart列表:


helm search repo命令用于搜索已加入本地Helm客户端的仓库(通过helm repo add命令添加),只搜索本地数据,无须连接网络。
我们还可以使用helm search hub命令搜索由Artifact Hub提供的来自不同仓库的大量Chart列表:

在没有过滤的情况下,通过helm search命令会显示所有可用的Chart,也可以加上查询关键字参数来过滤需要查询的Chart列表,例如查询包含“mysql”关键字的Chart列表:


Helm v3不再提供默认的Chart仓库,用户需要通过helm repo命令来添加、查询、删除Chart仓库。
可以用helm repo list命令查看已经添加的Chart仓库列表,例如:

使用helm repo add命令添加新的Chart仓库,例如:

由于Chart仓库的内容更新频繁,所以在部署应用之前,都应该运行helm repo update命令来确保本地仓库的数据最新,例如:

最后,可以使用helm repo remove命令从本地删除一个仓库,例如:

在查询到需要的Chart之后,就可以使用helm install命令部署应用了,最少需要指定两个命令行参数:Release名称(由用户设置)和Chart名称。
例如部署一个Release名称为“mariadb-1”的MariaDB应用,代码如下:



helm install命令会显示该应用本次部署的Release状态及与应用相关的提示信息,例如Release名称为“mariadb-1”(如果想让Helm生成Release名称,则可以不指定名称,并加上--generate-name参数)。
在安装过程中,Helm客户端会打印资源的创建过程、发布状态及需要额外处理的配置步骤等有用信息。
从上面的输出可以看到,通过本次部署,在Kubernetes集群中创建了名为“mariadb-1”和“mariadb-1-slave”的两个Service,并且给出了服务访问地址mariadb-1.default.svc.cluster.local:3306和mariadb-1-slave.default.svc.cluster.local:3306。
我们可以通过kubectl命令查询在Kubernetes集群中部署的资源对象:

至此,一个MariaDB应用就部署完成了。
使用helm list命令可以查询部署的Release列表:

helm install命令不会等待所有资源都运行成功后才退出。在部署过程中,下载镜像通常需要花费较多时间,为了跟踪Release的部署状态,可以使用helm status命令进行查看,例如:


上面的输出信息显示了Release当前最新的状态信息。
前面的安装过程使用的是Chart的默认配置数据。在实际情况下通常都需要根据环境信息先修改默认配置,再部署应用。
通过helm show values命令可以查看Chart的可配置项,例如查看MariaDB的可配置项:

用户可以编写一个YAML配置文件来覆盖这些内容,然后在安装时引用这个配置文件。例如:

该配置表示创建一个名为“user0”的MariaDB默认用户,并授权该用户最新创建的名为“user0db”的数据库的访问权限,对其他配置则使用Chart中的默认值。
然后通过helm install命令,使用-f参数引用该配置文件进行部署:

在部署应用时有两种方法传递配置数据。
◎ --values或者-f:使用YAML文件进行参数配置,可以设置多个文件,最后一个优先。对多个文件中重复的value会进行覆盖操作,不同的value叠加生效。上面的例子使用的就是这种方式。
◎ --set:在命令行中直接设置参数的值。
如果同时使用两个参数,则--set会以高优先级合并到--values中。对于通过--set设置的值,可以用helm get values <release-name>命令在指定的Release信息中查询到。另外,--set指定的值会被helm upgrade运行时--reset-values指定的值清空。
--set可以使用0个或多个名称/值对,最简单的方式是--set name=value,对应的YAML文件中的语法如下:

多个值使用逗号分隔,例如--set a=b,c=d,对应的YAML配置如下:

还可以用于表达具有多层级结构的变量,例如--set outer.inner=value,对应的YAML配置如下:

大括号{}可以用来表示列表类型的数据,例如--set name={a,b,c}会被翻译如下:

Helm从2.5.0版本开始,允许使用数组索引语法访问列表项,例如--set servers[0].port=80会被翻译如下:

通过这种方式可以设置多个值,例如--set servers[0].port=80,servers[0].host=example会被翻译如下:

有时在--set的值里会存在一些特殊字符(例如逗号、双引号等),对其可以使用反斜线“\”符号进行转义,例如--set name=value1\,value2会被翻译如下:

类似地,我们可以对点符号“.”进行转义,这样Chart使用toYaml方法解析Label、Annotation或者Node Selector时就很方便了,例如:--set nodeSelector."kubernetes\.io/role"=master会被翻译如下:

尽管如此,--set语法的表达能力依然无法与YAML语言相提并论,尤其是在处理深层嵌套类型的数据结构时。建议Chart的设计者在设计values.yaml文件格式时考虑--set的用法。
使用helm install命令时,可以通过多种安装源基于Chart部署应用。
◎ Chart仓库,如前文所述。
◎ 本地的Chart压缩包,例如helm install foo foo-0.1.1.tgz。
◎ 解压缩的Chart目录,例如helm install foo path/to/foo。
◎ 一个完整的URL,例如helm install foo https://example.com/charts/foo-1.2.3.tgz。
当一个Chart有新版本发布或者需要修改已部署Release的配置时,可以使用helm upgrade命令完成应用的更新。
helm upgrade命令会利用用户提供的更新信息来对Release进行更新。因为Kubernetes Chart可能会很大且很复杂,所以Helm会尝试执行最小影响范围的增量更新,只更新相对于上一个Release发生改变的部分。
例如更新默认的用户名,创建user1.yaml配置文件,内容如下:

使用helm upgrade命令更新当前已部署的Release“mariadb-1”:

使用helm get values命令可以查看到,基于用户提供的user1.yaml的新配置内容已被更新到了Release中:

用helm list命令查看Release的信息,会发现Revision被更新为2:

当然,也可以使用kubectl命令查看Statefulset、Pod等资源的变化情况。
如果更新后的Release未按预期执行,则可以使用helm rollback [RELEASE]命令对Release进行回滚,例如:

以上命令将把名为“mariadb-1”的Release回滚到第1个版本。
需要说明的是,Release的修订(Revision)号是持续增加的,每次进行安装、升级或者回滚,修订号都会增加1,第1个版本号始终是1。
用helm list命令查看Release的信息,会发现Revision被更新为3:

另外,使用helm history [RELEASE]命令可以查看Release的修订历史记录,例如:

在运行helm install/upgrade/rollback命令时,有些很有用的参数可帮助我们控制这几个操作的行为。注意,以下不是完整的命令行参数列表,可以使用helm <command>--help命令查看对全部参数的说明。
◎ --timeout:等待Kubernetes命令完成的(Golang持续)时间,默认值为5m0s。
◎ --wait:在将Release标记为成功之前,需要等待一些条件达成。例如,所有Pod的状态都为Ready;PVC完成绑定;Deployment的最小Pod数量(Desired-maxUnavailable)的状态为Ready;Service的IP地址设置成功(如果是LoadBalancer类型,则Ingress设置成功),等等。等待时间与--timeout参数设置的时间一样。超时后该Release的状态会被标记为FAILED。注意:当Deployment的replicas被设置为1,且滚动更新策略的maxUnavailable不为0时,--wait才会在有最小数量的Pod达到Ready状态后返回Ready状态。
◎ --no-hooks:跳过该命令的运行钩子(Hook)。
◎ --recreate-pods:会导致所有Pod的重建(属于Deployment的Pod除外),仅对upgrade和rollback命令可用。该参数在Helm v3中已被弃用。
需要卸载某个Release时,可以使用helm uninstall命令。例如使用该命令从集群中删除名为“mariadb-1”的Release:

再次使用helm list命令查看Release列表,可以看到名为“mariadb-1”的Release已被卸载:

在Helm v2版本中删除Release后会保留删除记录,在Helm v3版本中会同时删除历史记录。如果希望保留删除记录,则可以加上--keep-history参数,例如:

使用helm list--uninstalled命令可以查看使用--keep-history保留的卸载记录,例如:

注意,由于Release的状态是已删除,所以不能再回滚已卸载的Release到某个版本了。
用户可以基于Helm Chart模板开发自己的应用Chart,这可以通过helm create命令快速创建一个Chart模板,例如:

helm create命令用于在当前目录下创建名为“deis-workflow”的子目录,其中的文件和目录结构如下:


然后就可以基于这个模板编辑其中的YAML配置了。
编辑之后,可以用helm lint命令验证Chart中的各文件格式是否正确,例如:

在Chart准备好之后,使用helm package命令将其打包为一个.tgz文件,例如:

然后就可以基于该Chart的本地.tgz文件用helm install命令部署应用了,例如:

用户可以将打包好的Chart上传到Chart仓库中保存,供后续分发和部署使用。如何上传,依赖于Chart仓库提供的服务,需要查看Chart仓库的文档来了解如何上传。
Helm使用的包格式被称为Chart。Chart就是一个描述所有Kubernetes资源的文件集合。一个Chart用于部署一个完整的应用,例如数据库、缓存、Web服务等。本节对Chart的目录结构、主要文件和关键配置信息进行说明。
Chart是包含一系列文件的集合,目录名称就是Chart名称(不包含版本信息),例如一个WordPress的Chart会被存储在名为“wordpress”的目录下。
对该目录下文件结构和各文件的说明如下:

Helm保留了charts/、crds/、templates/目录和上面列举的文件名。
Chart.yaml文件(首字母大写)是Chart必需的主要配置文件,包含的关键字段和说明如下:


其他字段将忽略。对其中每个字段的详细说明请参考官方文档的说明。
Helm的官方Chart仓库在互联网上由社区维护。同时Helm也很容易搭建并运行自己的私有Chart仓库。本节对如何搭建和管理Chart仓库进行说明。
Chart仓库是一个包含一个index.yaml文件和已经打好包的Chart文件的HTTP服务器。Chart仓库可以是任何提供YAML和Tar文件并响应GET请求的HTTP服务器,在搭建私有Chart仓库时有很多选择,例如可以使用公有云服务Google Cloud Storage(GCS)、Amazon S3、GitHub,或者搭建自己的Web服务器。
Chart仓库由Chart包和包含了仓库中所有Chart索引的特殊文件index.yaml组成。例如一个Chart仓库的布局可能如下:

在index.yaml文件中包含了Alpine这个Chart的信息,并提供了Chart包的下载地址:http://<server-url>/charts/alpine-0.1.2.tgz。
index.yaml文件不用必须与Chart包放在同一个服务器上,但放在一起最方便。
index.yaml文件是YAML格式的文件,主要包括Chart包的元数据信息,包括Chart中Chart.yaml文件的内容。一个合法的Chart仓库必须有一个index.yaml文件,包含Chart仓库中每一个Chart的信息。
helm repo index命令会基于包含Chart包的本地目录生成该index.yaml文件。
index.yaml文件的内容示例如下:


下面使用Apache搭建一个私有Chart仓库,并将自定义的Chart保存到仓库中。首先,对Apache设置如下:
◎ 使用/var/web/charts目录存储Chart包和index.yaml文件;
◎ 使用http://<server-ip>/charts URL地址提供服务;
◎ 确保对index.yaml文件无须认证即可访问。
然后,将开发、打包好的Chart包文件(例如mymariadb-0.1.1.tgz)复制到Apache的/var/web/charts目录下。
接着,使用helm repo index命令创建索引文件,代码如下,Helm将根据/var/web/repo目录下的Chart内容创建index.yaml索引文件:

注意:后续每次在仓库中添加或更新Chart时,都必须使用helm repo index命令重新生成index.yaml文件。另外,该命令提供了--merge参数向现有index.yaml文件中增量添加新的Chart信息(而不是全部重新生成),这对于使用远程仓库很有用。
最后,启动Web Server。一个私有Chart仓库就搭建完成了。
准备好分享自建Chart仓库时,只需将仓库的URL地址告诉其他人,其他人就可以通过helm repo add [NAME] [URL]命令将仓库添加到其Helm客户端,查询Chart列表并部署应用了,例如:

通过helm install命令部署mymariadb应用,例如:
