最近在做 Helm 相关的开发工作,这里记录下使用和开发心得,并结合自己对 Helm 的理解,做个小小的总结。
helm是什么?
Helm 是一个软件包管理器,提供了一种『简单的方法来查找、共享和使用为 Kubernetes 而构建的软件』。
Helm 能够用来管理 k8s 上的复杂应用,它提供了比 k8s 更上一层的编排能力。它的编排目标是应用,而 k8s 的编排目标则是更小粒度的服务。
Helm 能够管理 k8s 中的资源依赖,能够重用 chart 包,能够为不同 k8s 集群制定不同的配置。
Helm 本身的处理逻辑很简单,其自身价值并不大,而有价值的是 Helm 的生态圈和 Helm 官方所提供的众多包含有最佳实践的 chart 包。
Helm 已被 CNCF 收录,可放心食用。
相关概念
chart: 一系列用于描述 k8s 资源相关文件的集合,是 Helm 用于打包 k8s 资源的方式;
release: 一个 chart 被 Helm 运行后将会生成对应的一个 release;
TillerServer: Helm 的服务端,部署在 k8s 集群内,主要管理 release 相关的存储和与 k8s 的交互;
helm: Helm 的客服端,通过 gRPC 协议与 TillerServer 进行交互,主要提供了增删查改 chart、release 和 repository 相关的功能;
chart 和 release 的关系可以用代码和进程的关系来类比。chart 是打包了 k8s 资源的集合(比如 deployment、service 等),而 release 则是在 Helm 中运行的集合实体(比如 values )。
结构图
Helm 的服务模型比较简单,如下图:
helm-tiller.jpg
定位
我们内部自己开发了一套能够在线可视化操作 k8s 资源的管理系统,但是只能操作单一资源对象。比如需要部署一个 redis 服务,那么就需要分别去创建对应的 Pod 资源、Service 资源和 ingress 资源。 通过这种方式来部署几个服务还是可以接受的,但是如果服务数量众多,这种一个一个的操作方式不仅效率低下,而且非常容易出错。能尽量减少人工的操作那么就需要尽量去减少,甚至消除人工操作。k8s 仅仅只是针对资源对象定义了操作,而非对一个服务整体,又或者是一个应用整体进行定义,它是一种细粒度的编排管理。出于这样的原因,k8s 对高层次的服务或应用编排的支持可能就不够了。Helm 的出现就是为了解决这种粗粒度的编排管理的。
Helm 官方希望一个 chart 能包含某个应用所需的全部基础组件依赖。比如 wordpress 应用,chart 包中包含了 PHP、MySQL 等相关基础组件依赖,这样就能通过 Helm 来一键安装了。这里有一个相关理念的产品,RANCHER。但这种 chart 会产生重复部署和资源浪费的问题,比如现在需要部署 wordpressA 和 wordpressB 两套系统,则这两个系统都会分别单独部署PHP、MySQL。这种行为可能不是我们想要的。
我使用 Helm 的目的是为了管理服务间的依赖,而不是基础组件的依赖。我把基础组件依赖都提取成一个独立的 chart,所有服务共享这些基础组件。这和上面的理念不同,官方推荐每个 chart 都打造成一个全栈应用。前者可能存在 chart 之间耦合度高的情况,但是不会出现重复部署、资源浪费的情况。
不足
Helm 虽然提供了 install、update 命令来安装或更新对应的 release,但这给使用者带来了需要维护 release 状态的压力。举个例子,在还没安装 release 之前,release 是不存在的,update 操作是会失败的。反之已经存在的 release,install 操作也会失败。其实大部分情况下我是不需要知道 release 的状态的,不管它存在还是不存在,我执行的命令就是我希望的意图,我希望 release 能成为我执行命令后的状态。这一点上 k8s 的 apply 命令就非常好,不需要用户来维护资源的状态。
使用心得
helm 客户端的功能非常简单,直接参考官网文档即可。
列一下相关使用心得:
Helm 的所有功能都是围绕着 chart、release 和 repository 的;
仅初始化客户端相关配置且仅建立本地仓库,可执行 helm init --client-only --skip-refresh;
查找 chart 的方式是通过 HELM_HOME(默认是 ~/.helm 目录)下的 repositories 目录进行的,几个重要文件或目录为 cache、repositories/cache;
修改 chart index.yaml 的 url,可执行 helm serve --url http://demo.com 来重新 reindex;
依赖关系管理,requirements定义,子 chart 值定义;
install 、 update 的方式管理不方便,这样需要维护 chart 的版本关系,集成 install 和 update ,组成类似 k8s 中的 apply 命令;
package 命令 -u 可以更新依赖,建议推到 repositiories 前先 package ,否则后期可能出现依赖检测不全的错误;
release 相关的信息存储在 k8s 的 configmap 中,命名形式为 release_name.v1 的格式。 rollback 相关功能就是通过存储在 configmap 中的信息进行回滚的;
Helm 客户端与 k8s 中的 TillerServer 是通过 k8s 提供的 port-forward 来实现的,而 port-forward 需要在指定节点上部署 socat;
TillerServer 可以不部署在 k8s 中, 此时 Helm 客户端需要通过 HELM_HOST 环境变量来指定 TillerServer 的地址和端口;
建议 TillerServer 部署在 k8s 中,既然 Helm 为 CNCF 的一员,那么就尽量把云原生做到极致吧;
Helm 开发
Helm 源码结构比较清晰单一,主要的源码目录有 cmd/helm、cmd/tiller 以及 pkg 三个,通讯协议使用的是 gRPC。
Helm 处理每个命令的流程基本都是一致的,没有复杂的地方。
在为 Helm 构建 HTTP 协议接口时,大部分输入参数都可以直接参考 cmd/helm 对应的命令。
在开发环境时,TillerServer 可以直接在集群外运行,并 export HELM_HOST=ip:port,方便调试。
Helm 给我带来的价值不单单是服务依赖管理,更多的是社区提供的众多有最佳实践的 k8s 编排技巧。CNCF 下还有很多非常优秀的项目,学好 k8s 任重道远。