目录

  1. 目标

  2. 说明

    1. 测试环境说明
    2. 参考文档
  3. Helm 是什么?

  4. 三大概念

    1. Chart
    2. Repository
    3. Release
  5. Helm 架构

  6. 使用 Helm

    1. 使用仓库
    2. 搜索 Chart
    3. 安装 Helm 包
    4. 升级、回滚和卸载
    5. 有用的选项
  7. 创建自己的 Chart

    1. Chart 文件结构

      1. Chart.yaml 文件
    2. Chart 模版指南

  8. 依赖管理

    1. 使用 dependencies 字段管理依赖

      1. 依赖的别名字段
      2. 依赖中的 tags 和 condition 字段
      3. 通过依赖导入子 Value
    2. 通过 charts/ 目录,手动管理依赖

    3. 安装顺序

    4. 作用域、依赖和值

      1. 全局 Value
    5. 用户自定义资源 CRD

      1. CRD 的限制
  9. 钩子

    1. 可用的钩子

    2. 钩子和发布的生命周期

      1. 发布不管理相应的钩子资源
    3. 编写钩子

      1. 钩子删除策略
  10. 示例 1

    1. 说明
    2. 示例
  11. Appendix 1 - Go SDK


1. 目标


2. 说明

2.1. 测试环境说明

2.2. 参考文档


3. Helm 是什么?

Helm 是 Kubernetes 的包管理器。Helm 之于 Kubernetes 类似于 APT 之于 Ubuntu,Yum 之于 RedHat。使用 Helm 可以简化 Kubernetes 应用部署。可以在到 CNCF Helm 项目过程报告阅读详细的背景信息。


4. 三大概念

4.1. Chart

Chart 代表 Helm 包。它包含在 Kubernetes 集群内部运行应用程序、工具或服务所需的所有资源定义。可以把它看作 Homebrew formula、Apt dpkg 或 Yum RPM 文件在 Kubernetes 中的等价物。

Chart 是描述 Kubernetes 相关资源的文件集合。

4.2. Repository

Repository(仓库)用于存放和共享 Chart。它就像 Perl 的 CPAN 档案库网络或是 Fedora 的软件包仓库,只不过它用于 Kubernetes 包。

4.3. Release

Release 是运行在 Kubernetes 集群中的 Chart 实例。一个 Chart 通常可以在同一个集群中安装多次。每次安装都会创建新 Release。以 MySQL Chart 为例,如果想在集群中运行两个数据库,那么可以安装该 Chart 两次。每个数据库拥有自己的 ReleaseRelease Name

在了解上述概念后,可以这样来解释 Helm:

Helm 安装 Chart 到 Kubernetes 集群中,每次安装都会创建新 Release。可以在 Helm 的 Chart Repositories 中寻找新 Chart。


5. Helm 架构

下面是 Helm v2 的架构图:

helmv2arch.png

其中:

简而言之,客户端负责管理 Chart,Tiller 负责管理 Release。

Helm 客户端是用 Go 语言编写的,使用 gRPC 协议套件与 Tiller 服务进行交互。

Tiller 服务也是用 Go 语言编写的。它提供使用客户端进行连接的 gRPC 服务,使用 Kubernetes 客户端类库与 Kubernetes 进行通信。当前该库使用 REST + JSON。

Tiller 服务将信息存储在位于 Kubernetes 内部的 ConfigMap 中,它不需要自己的数据库。

配置文件用 YAML 编写。

因为 Tiller 拥有最高权限,所以会授予用户他们不该有的权限,因此在 Helm v3 中,完全移除了 Tiller。取而代之的是,简单地从 Kubernetes API Server 获取信息, 在客户端侧渲染 Chart,并且在 Kubernetes 中存储安装记录。使用 kubecconfig 文件评估 Helm 的权限。Helm v3 支持现代 Kubernetes 的所有流行的安全、 身份和授权特性。集群管理员可以限制用户权限,只要他们觉得合适, 无论什么粒度都可以做到。


6. 使用 Helm

6.1. 使用仓库

helm repo 提供一组命令,用于添加、列出和移除仓库。

使用 helm repo list 可以查看配置的仓库:

使用 helm repo add 可以添加新仓库:

通过执行 helm repo update 命令,确保 Helm 客户端是最新的。使用 helm repo remove 命令移除仓库。

6.2. 搜索 Chart

Helm 从两种来源搜索 Chart:

Helm 搜索使用模糊字符串匹配算法,所以可以只输入名字的一部分。

6.3. 安装 Helm 包

使用 helm install 安装新 Helm 包。最简单的使用方法只需要传入两个参数:Release 名称和 Chart 名称。

现在已经安装 mysql Chart。安装 Chart 时,将创建新 Release 对象。上述发布被命名为 mysql。(如果想让 Helm 生成发布名称,那么删除发布名称,使用 --generate-name。)

Helm 按照 kind_sorter 中定义的顺序安装资源。

Helm 客户端不会等到所有资源都运行才退出。可以使用 helm status 追踪 Release 的状态,或重新读取配置信息:

上述安装方式使用 Chart 的默认配置选项,但有时需要指定我们想要的配置。

使用 helm show values 可以查看 Chart 中的可配置选项:

可以使用 YAML 格式的配置文件覆盖上述任意配置项,然后在安装过程中使用该文件。

上述命令将 mysql Service 的类型设置为 NodePort,并且将其 nodePort 设置为 30306。Chart 中的其它配置保持不变。

在安装时,传递配置数据的方式有两种:

如果同时使用两种方式,则 --set 中的值将被合并到 --values 中,但是 --set 中的值优先级更高。可以通过 helm get values <release-name> 查看用户提供的值。在运行 helm upgrade 时,如果指定 --reset-values 选项,那么将清除 --set 设置的值。

--set 选项带 0 或多个 name/value 对。最简单的用法类似于:--set name=value,等价于如下 YAML:

多个值使用逗号分割,因此 --set a=b,c=d 的 YAML 表示是:

支持更复杂的表达式。例如,--set outer.inner=value 被转换为:

使用 {} 中括起来的值表示列表。例如,--set name={a, b, c} 转换为:

某些 name/key 可被设置为 null 或者空数组 [],例如 --set name=[],a=null 转换为:

可以使用数组下标语法访问列表中的元素。例如 --set servers[0].port=80 变成:

也可以通过这种方式设置多个值。--set servers[0].port=80,servers[0].host=example 变成:

如果需要在 --set 中使用特殊字符,那么可以使用反斜线进行转义。--set name=value1\,value2 将变成:

类似地,也可以转义点(英文句号)。这可能在 Chart 使用 toYaml 函数解析 annotations、labels 和 node selectors 时派上用场。--set nodeSelector."kubernetes\.io/role"=master 变成:

深度嵌套的数据结构很难用 --set 表达,因此 Chart 的设计者在设计 values.yaml 文件的格式时,需要考虑 --set 的使用。(更多内容请查看 Values 文件

helm install 命令可以从多个来源进行安装:

6.4. 升级、回滚和卸载

如果想升级 Chart 的版本,或修改 Release 的配置,那么可以使用 helm upgrade 命令。

升级操作使用现有 Release,根据提供的信息对其进行升级。由于 Chart 可能很复杂,所以 Helm 尝试执行最小侵入式升级,即它只更新发生更改的内容。

helm get 命令是查看集群中 Release 的实用工具。可以使用 helm get values 命令,查看配置值是否生效:

如果在发布过程中发生非预期行为,那么可以通过 helm rollback [RELEASE] [REVISION] 命令回滚到之前的发布版本。

该命令将 mysql 回滚到最初版本。Release 版本其实是一个增量修订版本(Revision)。每当发生安装、升级或回滚操作时,Release 的 Revision 增加 1(第一个 Revision 号永远是 1)。可以使用 helm history [RELEASE] 命令查看特定 Release 的修订版本号。

使用 helm uninstall 命令从集群中卸载 Release:

该命令将从集群中移除指定 Release。可以通过 helm list 命令查看当前部署的所有 Release:

在 Helm v3 中,删除也移除 Release 的记录。如果想保留删除记录,那么使用 helm uninstall --keep-history。使用 helm list --uninstalled 只展示使用 --keep-history 删除的 Release。

helm list --all 展示 Helm 保留的所有 Release 记录,包括失败和删除的条目。

注意,因为当前默认删除 Release,所以不能回滚已被卸载的资源。

6.5. 有用的选项

可以指定选项,自定义 Helm 在安装、升级、回滚期间的行为。请注意这不是完整的 CLI 参数列表。要查看所有参数的说明,请执行 helm <command> --help 命令。


7. 创建自己的 Chart

使用 helm create 命令可以快速创建 Chart:

生成的 Chart 在 ./myapp 目录下,可以编辑它,创建自己的模版。

在编辑 Chart 时,可以通过 helm lint 验证格式是否正确。

当准备将 Chart 打包分发时,可以运行 helm package 命令:

然后可以通过 helm install 命令进行安装:

可以将打包好的 Chart 上传到 Chart 仓库中。查看 Helm Chart 仓库获取更多信息。

如果想下载和查看 Chart,但不安装它,可以用这个命令: helm pull chartrepo/chartname

7.1. Chart 文件结构

Chart 是组织在目录中的文件集合。目录名称是 Chart 名称(没有版本信息)。因而描述 myapp 的 Chart 存储在 myapp/ 目录中。Helm 期望 Chart 目录匹配以下结构:

Helm 保留使用 charts/crds/templates/ 目录,以及上面列出的文件名。

template/ 目录下名称以下划线开头的文件用于放置可复用的模版辅助对象。

7.1.1. Chart.yaml 文件

Chart.yaml文件是必需的,包含以下字段:

v3.3.2 开始,不再允许额外字段。推荐的方法是在 annotations 中添加自定义元数据。

7.2. Chart 模版指南

请参阅官方文档,了解:


8. 依赖管理

在 Helm 中,Chart 可以依赖其它任意 Chart(称之为子 Chart)。可以使用 Chart.yaml 文件中的 dependencies 字段动态链接这些依赖;也可以将依赖放到 charts/ 目录,手工管理。

8.1. 使用 dependencies 字段管理依赖

子 Chart 在 dependencies 字段中被定义为一个列表:

其中:

定义好依赖后,运行 helm dependency update 将所有依赖下载到 charts/ 目录:

helm dependency update 拉取 Chart 时,将它们以 Chart 归档文件的格式存储在 charts/ 目录。因此对于上面的示例,将在 chart/ 目录中看到以下文件:

8.1.1. 依赖的别名字段

除上面的字段外,每个依赖项可以包含可选的 alias 字段。该字段为 Chart 指定别名,别名将作为子 Chart 的名称,访问子 Chart 时可以使用别名。

在上述例子中,parentchart 有 3 个依赖项:

8.1.2. 依赖中的 tags 和 condition 字段

除上面的字段外,每个依赖项可以包含可选的 tagscondition 字段。

默认情况下,将加载所有 Chart。如果存在 tagscondition 字段,那么将计算它们,用于控制应用它们的 Chart 的加载。

在上面的例子中,所有带 front-end 标签的 Chart 都将被禁用,但因为在父 Chart 的 value 中,subchart1.enabled 路径计算为 true,该条件将覆盖 front-end 标签,所以 subchart1 将被启用。

因为 subchart2 使用 back-end 标签,并且该标签计算为 true,所以将启用 subchart2。 尽管 subchart2 有条件字段, 但是由于在父 Chart 的 value 中没有相应的路径,所以该条件不生效。

在 CLI 中,--set 参数可以用于设置标签和条件值:

标签和条件的解析规则如下:

8.1.3. 通过依赖导入子 Value

在某些情况下,允许将子 Chart 的值传播到父 Chart。

使用 exports 格式

如果子 Chart 的 values.yaml 文件在根节点包含 exports 字段,那么可以通过指定要导入的键的方式,将其内容直接导入到父 Chart 的 Value 中, 如下所示:

因为我们在导入列表中指定了键 data,所以 Helm 在子 Chart 的 exports 字段中查找 data 键,并导入它的内容。

最终父级 Value 将包含导出字段:

注意父级键 data 没被包含在父级的最终 Value 中,如果想指定这个父级键,那么使用"子-父"格式。

使用子-父格式

要访问子 Chart 中未包含在 exports 键的值,需要指定要导入的值的源键(child)和在父 Chart value(parent)中的目的路径。

下面示例中的 import-values 指示 Helm 去获取能在 child: 路径中找到的任何值,然后拷贝到 parent: 指定的路径。

在上面的例子中,在 subchart1 的 value 中找到的 default.data 的值将被导入到父 Chart 的 myimports 键中。

假设父 Chart 的 values.yaml 文件如下:

subchart1 的 values.yaml 文件如下:

那么父 Chart 结果值如下:

父 Chart 的最终值包含从 subchart1导入的 myintmybool 字段。

8.2. 通过 charts/ 目录,手动管理依赖

如果想对依赖进行更多控制,那么可以通过将有依赖关系的 Chart 复制到 charts/ 目录中的方式,显式表达依赖关系。

依赖应该是解压的 Chart 目录,并且名字不能以 _. 开头,否则将被 Chart 加载器忽略。

比如 myapp Chart 依赖 mysql Chart,那么可以这样做:

可以看到 mysql Chart 已被放到 myapp/charts/ 目录:

8.3. 安装顺序

上面的部分说明如何指定 Chart 的依赖。这对使用 helm installhelm upgrade 安装 Chart 有何影响呢?

假设 Chart A 创建如下 Kubernetes 对象:

此外,A 依赖创建如下对象的 Chart B:

在安装/升级 Chart A 后,将创建/修改单个 Helm 发布。该发布按照如下顺序创建/升级上述所有 Kubernetes 对象:

这是因为当 Helm 安装/升级 Chart 时,来自 Chart 的所有 Kubernetes 对象及其所有依赖

至此已为 Chart 及其依赖创建包含所有对象的单个发布。

Kubernetes 类型的安装顺序由 kind_sorter.go(查看 the Helm source file)中的枚举 InstallOrder 给出。

8.4 作用域、依赖和值

Values 文件可以为顶级 Chart 以及包含在该 Chart 的 charts/ 目录中的任意 Chart 声明值。或者换个说法,values 文件可以为 Chart 及其任意依赖项提供值。比如在下面的示例中,WordPress Chart 有两个依赖 - mysqlapache。values 文件可以向所有组件提供值:

高级 Chart 可以访问下级定义的所有变量。因此 WordPress Chart 可以用 .Values.mysql.password 访问 MySQL 密码。 但是低级 Chart 不能访问父级 Chart 的内容,所以 MySQL 不能访问 title 属性。同样也不能访问 apache.port

Values 被限制在命名空间中,但是命名空间已被删减。因此对于 WordPress Chart, 它可以用 .Values.mysql.password 访问 MySQL 密码。但是对于 MySQL Chart,值的作用域已经缩小,命名空间前缀已经移除, 因此它可以用 .Values.password 查看密码字段。

8.4.1. 全局 Value

从 2.0.0-Alpha.2 开始,Helm 支持特殊的"global"值。下面是前面的示例的修改版本:

上面添加了 global 区域,其值为 app: MyWordPress。所有 Chart 都可以用 .Values.global.app 访问该值。

比如,mysql 模板可以用 {{.Values.global.app}} 访问 appapache Chart 同样可以。 实际上,上面的 values 文件被重新生成为这样:

这提供一种与所有子 Chart 共享顶级变量的方式,这在设置 metadata 属性(比如标签)时很有用。

如果子 Chart 声明全局变量,那么全局变量将向下传递(到子 Chart 的子 Chart),但不会向上传递到父 Chart。子 Chart 无法影响父 Chart 的值。

并且,父 Chart 的全局变量优先于子 Chart 中的全局变量。

8.5. 用户自定义资源 CRD

Kubernetes 提供声明新 Kubernetes 对象类型的机制。使用 CustomResourceDefinition(CRD),Kubernetes 开发者可以声明自定义资源类型。

在 Helm v3 中,CRD 被视为特殊种类的对象。在 Chart 的其它部分之前安装它们,并且受到一些限制。

CRD YAML 文件应被放置在 Chart 里面的 crds/ 目录。多个 CRD(用 YAML 开始和结束标记分隔)可放在同一文件中。Helm 尝试将 CRD 目录中的所有文件加载进 Kubernetes。

CRD 文件无法模板化,必须是普通的 YAML 文档。

当 Helm 安装新 Chart 时,它将上传 CRD,暂停安装直到 CRD 可以被 API Server 使用,然后启动模版引擎,渲染 Chart 的其余部分,将其上传到 Kubernetes。因为该顺序,可以在 Helm 模板的 .Capabilities 对象中使用 CRD 信息,并且 Helm 模板可以创建在 CRD 中声明的对象的新实例。

比如,如果你的 Chart 在 crds/ 目录中有一个 CRD CronTab,那么可以在 templates/ 目录中创建 CronTab 种类的实例:

crontab.yaml 文件必须包含无模版指令的 CRD:

然后模板 mycrontab.yaml 可以创建新 CronTab(像平常一样使用模版):

Helm 在处理安装 templates/ 中的内容之前,保证 CronTab 种类安装成功,并且对 Kubernetes API Server 可用。

8.5.1. CRD 的限制

与 Kubernetes 中的大多数对象不同,CRD 是全局安装的。因此 Helm 管理 CRD 时采取非常谨慎的方式。CRD 受到如下限制:

升级或删除 CRD 的操作人员应该谨慎地手动执行此操作。


9. 钩子

Helm 提供钩子机制,允许 Chart 开发者在发布生命周期的某些点进行干预。比如,可以使用钩子:

钩子的工作方式与常规模板类似,但它们有特殊的注解,导致 Helm 以不同的方式利用它们。下面讲述钩子的基本使用模式。

9.1. 可用的钩子

Helm 定义以下钩子:

注解值描述
pre-install在渲染模版后,创建 Kubernetes 资源之前执行
post-install在所有资源都被加载进 Kubernetes 后执行
pre-delete在从 Kubernetes 删除资源前,执行删除请求
post-delete在删除发布的所有资源后,执行删除请求
pre-upgrade在渲染模版后,更新资源前执行升级请求
post-upgrade在升级所有资源后,执行升级请求
pre-rollback在渲染模版后,回滚资源前执行回滚请求
post-rollback在修改资源后,执行回滚请求
test当调用 Helm test 子命令时执行

9.2. 钩子和发布的生命周期

钩子使 Chart 开发者有机会在发布生命周期的关键节点上执行操作。比如,默认情况下,helm install 的生命周期如下:

  1. 用户运行 helm install foo
  2. 调用 Helm 库安装 API
  3. 在某些验证后,库渲染 foo 模版
  4. 库将结果资源加载进 Kubernetes
  5. 库将发布对象(以及其它数据)返回给客户端
  6. 客户端退出

Helm 为 install 生命周期定义两个钩子:pre-installpost-install。如果 foo Chart 的开发者实现这两个钩子,那么生命周期变成这样:

  1. 用户运行 helm install foo
  2. 调用 Helm 库安装 API
  3. 安装 crds/ 目录中的 CRD
  4. 在某些验证后,库渲染 foo 模版
  5. 库准备执行 pre-install 钩子(将钩子资源加载到 Kubernetes)
  6. 库按照权重对钩子进行排序(权重默认为 0),然后按照资源种类进行排序,最后按照名称升序排列
  7. 库先加载权重最小的钩子(从负到正)
  8. 库等待到钩子“Ready”(除 CRD 外)
  9. 库将结果资源加载进 Kubernetes。注意,如果设置 --wait 标记,那么库将等待到所有资源处于就绪状态,直到它们就绪后,才运行 post-install 钩子
  10. 库执行 post-install 钩子(加载钩子资源)
  11. 库等待到钩子“Ready”
  12. 库将发布对象(以及其它数据)返回给客户端
  13. 客户端退出

等待到钩子就绪意味着什么?这取决于钩子中声明的资源。如果资源种类是 JobPod,那么 Helm 将等到它成功地运行到完成。如果钩子失败,那么发布失败。这是阻塞操作,因此 Helm 客户端在 Job 运行期间将暂停。

对于其它种类,一旦 Kubernetes 将资源标记为已加载(已添加或已更新),就认为资源已“就绪”。当在钩子中声明多个资源时,那么串行执行这些资源。如果资源有钩子权重,那么按照权重顺序执行它们。从 Helm 3.2.0 开始,具有相同权重的钩子资源以与普通非钩子资源相同的顺序安装。否则,无法保证顺序。(在 Helm 2.3.0 及以后,它们按照字母序排序。不过该行为将来可能改变。)增加钩子权重被认为是最佳实践,如果权重不重要,那么将其设置为 0

9.2.1. 发布不管理相应的钩子资源

当前,钩子创建的资源无法作为发布的一部分进行跟踪和管理。一旦 Helm 确定钩子达到它的就绪状态,它不再管理钩子资源。当删除相应的发布时,钩子资源的垃圾回收将来可能被添加进 Helm v3。因此不能删除的钩子资源应该使用 helm.sh/resource-policy: keep 注释。

实际上,如果在钩子中创建资源,不能依靠 helm uninstall 删除这些资源。为销毁这类资源,要么向钩子模版文件添加自定义的 helm.sh/hook-delete-policy 注解,要么设置 Job 资源的生存(TTL)周期字段

9.3. 编写钩子

钩子是在 metadata 部分指定特殊注解的 Kubernetes 清单文件。因为它们是模板文件,所以可以使用所有普通模板特性,包括读取 .Values.Release.Template

比如,存储在 templates/post-install-job.yaml 的模版声明一个在 post-install 运行的 Job:

下面的注解使该模版成为钩子:

一个资源可以实现多个钩子:

类似地,实现给定钩子的不同资源的数量没有限制。比如,可以将 secret 和 configmap 都声明为 pre-install 钩子。

当子 Chart 声明钩子时,这些钩子也将被计算。顶级 Chart 无法禁用子 Chart 中声明的钩子。

为钩子定义权重有助于构建确定性的执行顺序。使用如下注解定义权重:

钩子权重可以是正数,也可以是负数,但必须是字符串形式。当 Helm 开始执行特定种类的钩子时,它将对这些钩子进行升序排序。

9.3.1. 钩子删除策略

可以定义决定何时删除相应的钩子资源的策略。使用如下注解定义钩子删除策略:

可以选择一个或多个注解值:

注解值描述
before-hook-creation在新钩子启动前,删除之前的资源(默认)
hook-succeeded在钩子成功执行后,删除资源
hook-failed在执行期间,如果钩子失败,那么删除资源

如果未指定钩子删除策略注解,那么默认应用 before-hook-creation 行为。


10. 示例 1

10.1. 说明

安装 MySQL 数据库后,使用 Job 创建 Database 和表。

10.2. 示例

  1. 如果未添加 bitnami 仓库,那么使用如下命令添加:

  2. 更新 bitnami 仓库:

  3. 如果集群尚未准备存储类型(StorageClass),那么可以安装 OpenEBS,安装过程可参考 http://timd.cn/k8s/openebs-installation/。使用如下命令查看存储类型:

  4. 创建 Chart:

  5. 删除 mysqltest/templates/ 目录下的文件:

  6. 在 mysqltest/Chart.yaml 文件的最后面添加下面内容:

  7. mysqltest/templates/ 目录添加模版文件 initjob.yaml:

    该 Job 实现 post-install 钩子,因此将在安装完 MySQL 后执行,用于对 MySQL 进行初始化。

  8. mysqltest/templates/ 目录添加模版文件 NOTES.txt:

    该文件包含简要使用说明。在该文件里可以使用模版参数。

  9. 更新依赖:

    执行成功后,将在 mysqltest/charts/ 目录生成 mysql-9.4.5.tgz。

  10. 安装:

    安装前,可以先使用如下命令进行调试(不真正安装):

    安装成功后,将看到渲染后的使用说明。接下来可以按照使用说明,进行人工验证。


Appendix 1 - Go SDK

Helm 3 提供完全重组的 Go SDK,以便在构建利用 Helm 的软件和工具时获得更好的体验。完整的文档地址在 https://pkg.go.dev/helm.sh/helm/v3,下面简要介绍最常见的包,以及一个简单的示例。

包概览

下面列举最常用的包,并且对每个包进行简单的解释:

显然,除这些包外还有很多包,所以请查看文档,获取更多信息!

简单示例

下面是使用 Go SDK 执行 helm list 的简单示例: