Helm 介绍

工作需要批量部署同一套ToB(to business)服务到多个k8s cluster/namespace 上,经过几百根头发的研究,本文介绍Helm相关概念。

简介

Helm 是 Kubernetes 包管理器,类似 Mac 下的 brew,Linux 下的 apt、yum 等包管理工具。

Helm 是 CNCF 的毕业项目,是查找、分享和使用软件构建 Kubernetes 的最优方式(Helm官方说的)。

Helm is the best way to find, share, and use software built for Kubernetes.

基本概念

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

  • Repository(仓库) 是用来存放和共享 charts 的地方。(非必须,本文以本地 charts 文件为例,不使用Repository)

  • Release 是运行在 Kubernetes 集群中的 chart 的实例。一个 chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 release。以 MySQL chart为例,如果你想在你的集群中运行两个数据库,你可以安装该chart两次。每一个数据库都会拥有它自己的 release 和 release name。

Helm 安装 charts 到 Kubernetes 集群中,每次安装都会创建一个新的 release。你可以在 Helm 的 chart repositories 中寻找新的 chart。

helm3

安装

前置条件

  1. 一个 Kubernetes 集群

  2. 确定你安装版本

    查看Helm和对应支持的Kubernetes版本,您可以参考 Helm 版本支持策略

  3. 安装和配置Helm。

1
2
3
4
5
6
7
8
9
# mac os
# 安装 kubectl
brew install kubectl
brew install kubectx
# 安装 helm
brew install helm

# 查看
helm version

Chart 模板指南

创建chart

1
2
# 创建一个名为`mychart`的chart
helm create mychart

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 目录结构
mychart/
Chart.yaml # 包含了chart信息的YAML文件
LICENSE # 可选: 包含chart许可证的纯文本文件
README.md # 可选: 可读的README文件
values.yaml # chart 默认的配置值
values.schema.json # 可选: 一个使用JSON结构的values.yaml文件
charts/ # 可选: 包含chart依赖的其他chart
crds/ # 可选: 自定义资源的定义
templates/ # 模板目录, 当和values 结合时,可生成有效的Kubernetes manifest文件
deployment.yaml # 创建Kubernetes 工作负载的基本清单
service.yaml # 为你的工作负载创建一个 service终端基本清单。
_helpers.tpl # 放置可以通过chart复用的模板辅助对象
NOTES.txt # chart的"帮助文本"。这会在你的用户执行helm install时展示给他们。
  • values.yaml 文件也导入到了模板。这个文件包含了chart的 默认值 。这些值会在用户执行helm installhelm upgrade时被覆盖。

  • Chart.yaml 文件包含了该chart的描述。你可以从模板中访问它。charts/目录 可以 包含其他的chart(称之为 子chart)。

  • templates/ 目录包括了模板文件。当Helm评估chart时,会通过模板渲染引擎将所有文件发送到templates/目录中。 然后收集模板的结果并发送给Kubernetes。

    • NOTES.txt: chart的"帮助文本"。这会在你的用户执行helm install时展示给他们。

    • deployment.yaml: 创建Kubernetes 工作负载的基本清单

    • service.yaml: 为你的工作负载创建一个 service终端基本清单。

    • _helpers.tpl: 放置可以通过chart复用的模板辅助对象

Chart.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
apiVersion: chart API 版本 (必需)
name: chart名称 (必需)
version: 语义化2 版本(必需)
kubeVersion: 兼容Kubernetes版本的语义化版本(可选)
description: 一句话对这个项目的描述(可选)
type: chart类型 (可选)
keywords:
- 关于项目的一组关键字(可选)
home: 项目home页面的URL (可选)
sources:
- 项目源码的URL列表(可选)
dependencies: # chart 必要条件列表 (可选)
- name: chart名称 (nginx)
version: chart版本 ("1.2.3")
repository: (可选)仓库URL ("https://example.com/charts") 或别名 ("@repo-name")
condition: (可选) 解析为布尔值的yaml路径,用于启用/禁用chart (e.g. subchart1.enabled )
tags: # (可选)
- 用于一次启用/禁用 一组chart的tag
import-values: # (可选)
- ImportValue 保存源值到导入父键的映射。每项可以是字符串或者一对子/父列表项
alias: (可选) chart中使用的别名。当你要多次添加相同的chart时会很有用
maintainers: # (可选)
- name: 维护者名字 (每个维护者都需要)
email: 维护者邮箱 (每个维护者可选)
url: 维护者URL (每个维护者可选)
icon: 用做icon的SVG或PNG图片URL (可选)
appVersion: 包含的应用版本(可选)。不需要是语义化,建议使用引号
deprecated: 不被推荐的chart (可选,布尔值)
annotations:
example: 按名称输入的批注列表 (可选).

dependencies

当前chart依赖的其他chart会在dependencies字段定义为一个列表。

1
2
3
4
5
6
7
dependencies:
- name: apache
version: 1.2.3
repository: https://example.com/charts
- name: mysql
version: 3.2.1
repository: https://another.example.com/charts

一旦你定义好了依赖,运行 helm dependency update 就会使用你的依赖文件下载所有你指定的chart到你的charts/目录

helm dependency update 拉取chart时,会在charts/目录中形成一个chart包。因此对于上面的示例,会在chart目录中期望看到以下文件:

1
2
3
charts/
apache-1.2.3.tgz
mysql-3.2.1.tgz

依赖顺序

上面的部分说明如何指定chart的依赖,但是对使用 helm installhelm upgrade 安装chart有什么影响?

假设有个chart “A” 创建了下面的Kubernetes对象:

  • namespace “A-Namespace”
  • statefulset “A-StatefulSet”
  • service “A-Service”

另外,A是依赖于chart B创建的对象:

  • namespace “B-Namespace”
  • replicaset “B-ReplicaSet”
  • service “B-Service”

安装/升级chart A后,会创建/修改一个单独的Helm版本。这个版本会按顺序创建/升级以下所有的Kubernetes对象:

  • A-Namespace
  • B-Namespace
  • A-Service
  • B-Service
  • B-ReplicaSet
  • A-StatefulSet

这是因为当Helm安装/升级chart时,chart中所有的Kubernetes对象以及依赖会

  • 聚合成一个单一的集合;然后
  • 按照类型和名称排序;然后
  • 按这个顺序创建/升级。

至此会为chart及其依赖创建一个包含所有对象的release版本。

Kubernetes类型的安装顺序会按照kind_sorter.go(查看 Helm源文件)中给出的枚举顺序进行。

内置对象

  • ReleaseRelease对象描述了版本发布本身,如 Release.Name: release名称

  • ValuesValues对象是从values.yaml文件和用户提供的文件传进模板的。默认为空

  • ChartChart.yaml文件内容,比如 {{ .Chart.Name }}-{{ .Chart.Version }} 会打印出 mychart-0.1.0

  • Files: 在chart中提供访问所有的非特殊文件的对象。 请查看这个 文件访问部分了解更多信息

  • Capabilities: 提供关于Kubernetes集群支持功能的信息

  • Template: 包含当前被执行的当前模板信息

    请在内置对象中查看更多

Values 文件

Values内容来自于多个位置:

  • chart中的values.yaml文件
  • 如果是子chart,就是父chart中的values.yaml文件
  • 使用-f参数(helm install -f myvals.yaml ./mychart)传递到 helm installhelm upgrade的values文件
  • 使用--set (比如helm install --set foo=bar ./mychart)传递的单个参数

以上列表有明确顺序:默认使用values.yaml,可以被父chart的values.yaml覆盖,继而被用户提供values文件覆盖, 最后会被--set参数覆盖,优先级为values.yaml最低,--set参数最高。

Templates and Values

所有模板文件存储在chart的 templates/ 文件夹。 当Helm渲染chart时,它会通过模板引擎遍历目录中的每个文件。

模板的Value通过两种方式提供:

  • Chart开发者可以在chart中提供一个命名为 values.yaml 的文件。这个文件包含了默认值。
  • Chart用户可以提供一个包含了value的YAML文件。可以在命令行使用 helm install -f demo.yaml 命令时提供。

当用户提供自定义value时,这些value会覆盖chart的values.yaml文件中value。

模板函数

模板函数列表

开始之前,我们先在values.yaml文件添加一个披萨的配料列表:

1
2
3
4
5
6
7
8
favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

语法

模板函数的语法是 functionName arg1 arg2...

1
2
3
4
# 示例  quote 双引号
drink: {{ quote .Values.favorite.drink }}
# 渲染结果
drink: "coffee"

管道符

1
2
3
4
# 示例  quote 双引号
drink: {{ .Values.favorite.drink | quote }}
# 渲染结果
drink: "coffee"

流控制

If/Else

1
2
3
4
5
6
7
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}

PIPELINE 如果是以下值时,管道会被设置为 false

  • 布尔false
  • 数字0
  • 空字符串
  • nil (空或null)
  • 空集合(map, slice, tuple, dict, array)

在所有其他条件下,条件都为true。

with

with 用来控制变量范围

1
2
3
{{ with PIPELINE }}
# restricted scope
{{ end }}
  • .是对 当前作用域 的引用。因此 .Values就是告诉模板在当前作用域查找Values对象。with允许你为特定对象设定当前作用域(.)。

  • with后面的块只有在 PIPELINE 的值不为空时才会执行。

  • with的作用域内,无法使用.访问父作用域的对象,可以使用$从根作用域中访问对象或使用变量

1
2
3
4
5
6
7
8
9
10
11
# 修改配置映射中的`.`的作用域指向`.Values.favorite`:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $.Release.Name }}
{{- end }}

# 渲染结果
drink: "coffee"
food: "PIZZA"
release: mychart

range 循环

  • range的作用域内,无法使用.访问父作用域的对象,可以使用$从根作用域中访问对象或使用变量
  • range可被用于迭代列表和元组以及有键值对的集合(像mapdict
1
2
3
4
5
6
7
8
9
toppings:
{{- range .Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}
# 渲染结果
toppings:
- small
- medium
- large

变量

变量是对另一个对象的命名引用。遵循$name变量的格式且指定了一个特殊的赋值运算符::=

  • 用于解决with、range作用域问题

    1
    2
    3
    4
    5
    6
    7
    # 把.Release.Name 赋值给 $relname
    {{- $relname := .Release.Name -}}
    {{- with .Values.favorite }}
    drink: {{ .drink | default "tea" | quote }}
    food: {{ .food | upper | quote }}
    release: {{ $relname }}
    {{- end }}
  • 变量还可以用于类似列表的对象,以捕获索引和值

    1
    2
    3
    {{- range $index, $topping := .Values.pizzaToppings }}
    {{ $index }}: {{ $topping }}
    {{- end }}
  • 变量还可以用于对于数据结构有key和value,可以使用range获取key和value。

    1
    2
    3
    {{- range $key, $val := .Values.favorite }}
    {{ $key }}: {{ $val | quote }}
    {{- end }}

空格

你可能注意到在上面的流控制中存在 {{-` `-}} 是为了消除流控制空格即换行,YAML认为空格是有意义的。

一定注意空格就是换行

  • {{- `(包括添加的横杠和空格)表示向左删除空白, 而 - ` -}}表示右边的空格应该被去掉。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 示例1
food: "orange"
{{ if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{ end }}
# 渲染结果
food: "orange"

mug: "true"

# 示例2
food: "orange"
{{- if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{- end }}
# 渲染结果
food: "orange"
mug: "true"

在上面示例中 1未加- food,mug后各有一行空格,因此一般流控制中都会加{{- if xxx -}}来确保没有空格/换行。

命名模板

  • 以下划线(_)开始的文件不会渲染为Kubernetes对象定义,但会被helm命令加载。
  • _helpers.tpl这个文件是默认的模板文件。

define使用模板

define操作允许我们在模板文件中创建一个命名模板

我们可以定义一个模板:

1
2
3
4
5
{{/* Generate basic labels */}}
{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}

templateinclude使用模板

在configMap使用模板标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
{{- include "mychart.labels" | indent 4 }}
# 渲染结果
apiVersion: v1
kind: ConfigMap
metadata:
name: measly-whippet-configmap
labels:
# template
app_name: mychart
app_version: "0.1.0"
# include
app_name: mychart
app_version: "0.1.0"

注意两处的app_version缩进都不对,为啥?因为被替换的模板中文本是左对齐的。由于template是一个行为,不是方法,无法将 template调用的输出传给其他方法,数据只是简单地按行插入。而indent 可以使用管道进行缩进。

相较于使用template,在helm中使用include被认为是更好的方式 为了更好地处理YAML文档的输出格式

Chart Hook

Helm 提供了一个 hook 机制允许chart开发者在发布生命周期的某些点进行干预。

注释值 描述
pre-install 在模板渲染之后,Kubernetes资源创建之前执行
post-install 在所有资源加载到Kubernetes之后执行
pre-delete 在Kubernetes删除之前,执行删除请求
post-delete 在所有的版本资源删除之后执行删除请求
pre-upgrade 在模板渲染之后,资源更新之前执行一个升级请求
post-upgrade 所有资源升级之后执行一个升级请求
pre-rollback 在模板渲染之后,资源回滚之前,执行一个回滚请求
post-rollback 在所有资源被修改之后执行一个回滚请求
test 调用Helm test子命令时执行 ( test文档)

常用命令

  • helm version 打印客户端版本信息

  • helm upgrade --install 升级/安装chart

  • helm rollback - 回滚发布到上一个版本

  • helm lint 是验证chart是否遵循最佳实践的首选工具。

  • helm template --debug 在本地测试渲染chart模板。

  • helm install --dry-run --debug :让服务器渲染模板,然后返回生成的清单文件。这样不会安装应用(chart)到你的kubenetes集群中,只会渲染模板内容到控制台(用于测试)

  • helm get manifest: 这是查看安装在服务器上的模板。

  • 更多命令

总结

  • 即使是最复杂的应用,Helm Chart 依然可以描述, 提供使用单点授权的可重复安装应用程序。

  • 随时随地升级和自定义的钩子消除您升级的痛苦。

  • Helm Chart 很容易在公共或私有化服务器上发版,分发和部署站点。

  • 使用 helm rollback 可以轻松回滚到之前的发布版本。

参考


Helm 介绍
https://zhengshuoo.github.io/posts/015-helm-Introduction
作者
zhengshuo
发布于
2023年3月11日
许可协议