~/.kube/config
Helm 是 Kubernetes 的包管理器。Helm 之于 Kubernetes 类似于 APT 之于 Ubuntu,Yum 之于 RedHat。使用 Helm 可以简化 Kubernetes 应用部署。可以在到 CNCF Helm 项目过程报告阅读详细的背景信息。
Chart 代表 Helm 包。它包含在 Kubernetes 集群内部运行应用程序、工具或服务所需的所有资源定义。可以把它看作 Homebrew formula、Apt dpkg 或 Yum RPM 文件在 Kubernetes 中的等价物。
Chart 是描述 Kubernetes 相关资源的文件集合。
Repository(仓库)用于存放和共享 Chart。它就像 Perl 的 CPAN 档案库网络或是 Fedora 的软件包仓库,只不过它用于 Kubernetes 包。
Release 是运行在 Kubernetes 集群中的 Chart 实例。一个 Chart 通常可以在同一个集群中安装多次。每次安装都会创建新 Release。以 MySQL Chart 为例,如果想在集群中运行两个数据库,那么可以安装该 Chart 两次。每个数据库拥有自己的 Release 和 Release Name。
在了解上述概念后,可以这样来解释 Helm:
Helm 安装 Chart 到 Kubernetes 集群中,每次安装都会创建新 Release。可以在 Helm 的 Chart Repositories 中寻找新 Chart。
下面是 Helm v2 的架构图:
其中:
Helm 客户端是给终端用户使用的命令行客户端。该客户端负责:
本地 Chart 开发
管理仓库
与 Tiller 服务进行交互:
Tiller 服务是集群内的服务,它与 Helm 客户端进行交互,是与 Kubernetes API Server 进行交互的接口。该服务负责:
简而言之,客户端负责管理 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 的所有流行的安全、 身份和授权特性。集群管理员可以限制用户权限,只要他们觉得合适, 无论什么粒度都可以做到。
helm repo
提供一组命令,用于添加、列出和移除仓库。
使用 helm repo list
可以查看配置的仓库:
$ helm repo list
NAME URL
openebs https://openebs.github.io/charts
bitnami https://charts.bitnami.com/bitnami
使用 helm repo add
可以添加新仓库:
helm repo add dev https://example.com/dev-charts
通过执行 helm repo update
命令,确保 Helm 客户端是最新的。使用 helm repo remove
命令移除仓库。
Helm 从两种来源搜索 Chart:
helm search hub
从 Artifact Hub 中查找并列出 Helm Chart。 Artifact Hub 中存放大量不同的仓库。helm search repo
从添加(使用 helm repo add
)到本地 Helm 客户端的仓库中进行查找。该命令基于本地数据进行搜索,无需连接网络。Helm 搜索使用模糊字符串匹配算法,所以可以只输入名字的一部分。
使用 helm install
安装新 Helm 包。最简单的使用方法只需要传入两个参数:Release 名称和 Chart 名称。
$ helm install mysql bitnami/mysql
NAME: mysql
LAST DEPLOYED: Tue Jan 3 11:14:32 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mysql
CHART VERSION: 9.4.5
APP VERSION: 8.0.31
** Please be patient while the chart is being deployed **
...
现在已经安装 mysql Chart。安装 Chart 时,将创建新 Release 对象。上述发布被命名为 mysql
。(如果想让 Helm 生成发布名称,那么删除发布名称,使用 --generate-name
。)
Helm 按照 kind_sorter 中定义的顺序安装资源。
Helm 客户端不会等到所有资源都运行才退出。可以使用 helm status
追踪 Release 的状态,或重新读取配置信息:
$ helm status mysql
NAME: mysql
LAST DEPLOYED: Tue Jan 3 11:19:11 2023
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mysql
CHART VERSION: 9.4.5
APP VERSION: 8.0.31
** Please be patient while the chart is being deployed **
...
上述安装方式使用 Chart 的默认配置选项,但有时需要指定我们想要的配置。
使用 helm show values
可以查看 Chart 中的可配置选项:
$ helm show values bitnami/mysql
# @section Global parameters
## Global Docker image parameters
## Please, note that this will override the image parameters, including dependencies, configured to use the global value
## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass
## @param global.imageRegistry Global Docker image registry
## @param global.imagePullSecrets Global Docker registry secret names as an array
## @param global.storageClass Global StorageClass for Persistent Volume(s)
##
global:
imageRegistry: ""
## E.g.
## imagePullSecrets:
## - myRegistryKeySecretName
##
imagePullSecrets: []
storageClass: ""
...
可以使用 YAML 格式的配置文件覆盖上述任意配置项,然后在安装过程中使用该文件。
$ echo '{primary: {service: {type: NodePort, nodePorts: {mysql: 30306}}}}' > values.yaml
$ helm install -f values.yaml mysql bitnami/mysql
上述命令将 mysql
Service 的类型设置为 NodePort,并且将其 nodePort
设置为 30306。Chart 中的其它配置保持不变。
在安装时,传递配置数据的方式有两种:
--values
(或 -f
):使用 YAML 文件覆盖配置。可以指定多次,优先使用最右边的文件--set
:通过命令行的方式,对指定项进行覆盖如果同时使用两种方式,则 --set
中的值将被合并到 --values
中,但是 --set
中的值优先级更高。可以通过 helm get values <release-name>
查看用户提供的值。在运行 helm upgrade
时,如果指定 --reset-values
选项,那么将清除 --set
设置的值。
--set
选项带 0 或多个 name/value 对。最简单的用法类似于:--set name=value
,等价于如下 YAML:
name value
多个值使用逗号分割,因此 --set a=b,c=d
的 YAML 表示是:
a b
c d
支持更复杂的表达式。例如,--set outer.inner=value
被转换为:
xxxxxxxxxx
outer
inner value
使用 {
和 }
中括起来的值表示列表。例如,--set name={a, b, c}
转换为:
xxxxxxxxxx
name
a
b
c
某些 name/key 可被设置为 null
或者空数组 []
,例如 --set name=[],a=null
转换为:
xxxxxxxxxx
name
a null
可以使用数组下标语法访问列表中的元素。例如 --set servers[0].port=80
变成:
xxxxxxxxxx
servers
port80
也可以通过这种方式设置多个值。--set servers[0].port=80,servers[0].host=example
变成:
xxxxxxxxxx
servers
port80
host example
如果需要在 --set
中使用特殊字符,那么可以使用反斜线进行转义。--set name=value1\,value2
将变成:
xxxxxxxxxx
name"value1,value2"
类似地,也可以转义点(英文句号)。这可能在 Chart 使用 toYaml
函数解析 annotations、labels 和 node selectors 时派上用场。--set nodeSelector."kubernetes\.io/role"=master
变成:
xxxxxxxxxx
nodeSelector
kubernetes.io/role master
深度嵌套的数据结构很难用 --set
表达,因此 Chart 的设计者在设计 values.yaml
文件的格式时,需要考虑 --set
的使用。(更多内容请查看 Values 文件)
helm install
命令可以从多个来源进行安装:
helm install foo foo-0.1.1.tgz
)helm install foo path/to/foo
)helm install foo https://example.com/charts/foo-1.2.3.tgz
)如果想升级 Chart 的版本,或修改 Release 的配置,那么可以使用 helm upgrade
命令。
升级操作使用现有 Release,根据提供的信息对其进行升级。由于 Chart 可能很复杂,所以 Helm 尝试执行最小侵入式升级,即它只更新发生更改的内容。
xxxxxxxxxx
$ helm upgrade -f values.yaml --set primary.service.nodePorts.mysql=30307 mysql bitnami/mysql
helm get
命令是查看集群中 Release 的实用工具。可以使用 helm get values
命令,查看配置值是否生效:
xxxxxxxxxx
$ helm get values mysql
USER-SUPPLIED VALUES:
primary:
service:
nodePorts:
mysql: 30307
type: NodePort
如果在发布过程中发生非预期行为,那么可以通过 helm rollback [RELEASE] [REVISION]
命令回滚到之前的发布版本。
xxxxxxxxxx
$ helm rollback mysql 1
该命令将 mysql
回滚到最初版本。Release 版本其实是一个增量修订版本(Revision)。每当发生安装、升级或回滚操作时,Release 的 Revision 增加 1(第一个 Revision 号永远是 1)。可以使用 helm history [RELEASE]
命令查看特定 Release 的修订版本号。
使用 helm uninstall
命令从集群中卸载 Release:
xxxxxxxxxx
$ helm uninstall mysql
该命令将从集群中移除指定 Release。可以通过 helm list
命令查看当前部署的所有 Release:
xxxxxxxxxx
$ helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
metallb default 2 2023-01-03 10:30:23.141746 +0800 CST deployed metallb-0.13.7 v0.13.7
在 Helm v3 中,删除也移除 Release 的记录。如果想保留删除记录,那么使用 helm uninstall --keep-history
。使用 helm list --uninstalled
只展示使用 --keep-history
删除的 Release。
helm list --all
展示 Helm 保留的所有 Release 记录,包括失败和删除的条目。
注意,因为当前默认删除 Release,所以不能回滚已被卸载的资源。
可以指定选项,自定义 Helm 在安装、升级、回滚期间的行为。请注意这不是完整的 CLI 参数列表。要查看所有参数的说明,请执行 helm <command> --help
命令。
--timeout
:等待任意单个 Kubernetes 操作的时间(默认 5m0s
)--wait
:如果设置该选项,将等待到所有 Pod 都处于 ready 状态,PVC 都被绑定,Deployment 都拥有最少(Desired
减去 maxUnavailable
) ready Pod,并且 Service 都具有 IP 地址(如果类型是 LoadBalancer
),才将该 Release 标记为成功。最长等待时间由 --timeout
指定。如果达到超时时间,Release 将被标记为 FAILED
。注意:当 Deployment 的 replicas
被设置为 1,但其滚动升级策略中的 maxUnavailable
没被设置为 0 时,--wait
将返回就绪,因为已经满足最小 ready Pod 数量--wait-for-jobs
:如果设置该选项,并且启用 --wait
,那么在将 Release 标记为成功之前,将等待到所有 Job 都完成。最长等待时间由 --timeout
指定--no-hooks
:在安装期间,阻止钩子运行--debug
:启用详细输出--dry-run
:模拟安装。当想测试正在渲染的模版,但不真正地安装时,可以使用 helm install --debug --dry-run goodly-guppy ./mychart
使用 helm create
命令可以快速创建 Chart:
xxxxxxxxxx
$ helm create myapp
Creating myapp
生成的 Chart 在 ./myapp
目录下,可以编辑它,创建自己的模版。
在编辑 Chart 时,可以通过 helm lint
验证格式是否正确。
当准备将 Chart 打包分发时,可以运行 helm package
命令:
xxxxxxxxxx
$ helm package myapp
然后可以通过 helm install
命令进行安装:
xxxxxxxxxx
$ helm install myapp myapp-0.1.0.tgz
可以将打包好的 Chart 上传到 Chart 仓库中。查看 Helm Chart 仓库获取更多信息。
如果想下载和查看 Chart,但不安装它,可以用这个命令: helm pull chartrepo/chartname
。
Chart 是组织在目录中的文件集合。目录名称是 Chart 名称(没有版本信息)。因而描述 myapp 的 Chart 存储在 myapp/
目录中。Helm 期望 Chart 目录匹配以下结构:
xxxxxxxxxx
myapp # Chart 名称
├── Chart.yaml # 包含 Chart 信息(比如名称、版本号等)的 YAML 文件
├── charts # 包含 Chart 依赖的其它 Chart(称之为子 Chart)的目录
├── crds # 自定义资源定义
├── templates # 模板目录,当与 values 结合时,将生成有效的 Kubernetes 清单文件
│ └── NOTES.txt # 可选:包含简要使用说明的纯文本文件
├── LICENSE # 可选:包含 Chart 许可证的纯文本文件
├── README.md # 可选:易于阅读的 README 文件
├── values.schema.json # 可选:校验 values.yaml 文件结构的 JSON 模式
└── values.yaml # 默认配置值
Helm 保留使用 charts/
、crds/
、 templates/
目录,以及上面列出的文件名。
template/
目录下名称以下划线开头的文件用于放置可复用的模版辅助对象。
Chart.yaml
文件是必需的,包含以下字段:
xxxxxxxxxx
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(比如 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 按名称键入的注解列表(可选)
从 v3.3.2 开始,不再允许额外字段。推荐的方法是在 annotations
中添加自定义元数据。
请参阅官方文档,了解:
在 Helm 中,Chart 可以依赖其它任意 Chart(称之为子 Chart)。可以使用 Chart.yaml
文件中的 dependencies
字段动态链接这些依赖;也可以将依赖放到 charts/
目录,手工管理。
子 Chart 在 dependencies
字段中被定义为一个列表:
xxxxxxxxxx
dependencies
name apache
version1.2.3
repository https //example.com/charts
name mysql
version3.2.1
repository https //another.example.com/charts
其中:
helm repo add
在本地添加该仓库xxxxxxxxxx
$ helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.com
xxxxxxxxxx
dependencies
name awesomeness
version1.0.0
repository"@fantastic-charts"
定义好依赖后,运行 helm dependency update
将所有依赖下载到 charts/
目录:
xxxxxxxxxx
$ helm dep up myapp
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "metallb" chart repository
...Successfully got an update from the "openebs" chart repository
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading mysql from repo https://charts.bitnami.com/bitnami
Deleting outdated charts
当 helm dependency update
拉取 Chart 时,将它们以 Chart 归档文件的格式存储在 charts/
目录。因此对于上面的示例,将在 chart/
目录中看到以下文件:
xxxxxxxxxx
charts
└── mysql-9.4.5.tgz
除上面的字段外,每个依赖项可以包含可选的 alias
字段。该字段为 Chart 指定别名,别名将作为子 Chart 的名称,访问子 Chart 时可以使用别名。
xxxxxxxxxx
# parentchart/Chart.yaml
dependencies
name subchart
repository http //localhost10191
version0.1.0
alias new-subchart-1
name subchart
repository http //localhost10191
version0.1.0
alias new-subchart-2
name subchart
repository http //localhost10191
version0.1.0
在上述例子中,parentchart
有 3 个依赖项:
xxxxxxxxxx
subchart
new-subchart-1
new-subchart-2
除上面的字段外,每个依赖项可以包含可选的 tags
和 condition
字段。
默认情况下,将加载所有 Chart。如果存在 tags
或 condition
字段,那么将计算它们,用于控制应用它们的 Chart 的加载。
xxxxxxxxxx
# parentchart/Chart.yaml
dependencies
name subchart1
repository http //localhost10191
version0.1.0
condition subchart1.enabled,global.subchart1.enabled
tags
front-end
subchart1
name subchart2
repository http //localhost10191
version0.1.0
condition subchart2.enabled,global.subchart2.enabled
tags
back-end
subchart2
xxxxxxxxxx
# parentchart/values.yaml
subchart1
enabledtrue
tags
front-endfalse
back-endtrue
在上面的例子中,所有带 front-end
标签的 Chart 都将被禁用,但因为在父 Chart 的 value 中,subchart1.enabled
路径计算为 true
,该条件将覆盖 front-end
标签,所以 subchart1
将被启用。
因为 subchart2
使用 back-end
标签,并且该标签计算为 true
,所以将启用 subchart2
。 尽管 subchart2
有条件字段, 但是由于在父 Chart 的 value 中没有相应的路径,所以该条件不生效。
在 CLI 中,--set
参数可以用于设置标签和条件值:
xxxxxxxxxx
helm install --set tags.front-end=true --set subchart2.enabled=false
标签和条件的解析规则如下:
true
,那么启用该 Charttags:
键必须是顶层键在某些情况下,允许将子 Chart 的值传播到父 Chart。
使用 exports 格式
如果子 Chart 的 values.yaml
文件在根节点包含 exports
字段,那么可以通过指定要导入的键的方式,将其内容直接导入到父 Chart 的 Value 中, 如下所示:
xxxxxxxxxx
# 父 Chart 的 Chart.yaml 文件
dependencies
name subchart
repository http //localhost10191
version0.1.0
import-values
data
xxxxxxxxxx
# 子 Chart 的 values.yaml 文件
exports
data
myint99
因为我们在导入列表中指定了键 data
,所以 Helm 在子 Chart 的 exports
字段中查找 data
键,并导入它的内容。
最终父级 Value 将包含导出字段:
xxxxxxxxxx
# 父 Chart 的 Value
myint99
注意父级键 data
没被包含在父级的最终 Value 中,如果想指定这个父级键,那么使用"子-父"格式。
使用子-父格式
要访问子 Chart 中未包含在 exports
键的值,需要指定要导入的值的源键(child
)和在父 Chart value(parent
)中的目的路径。
下面示例中的 import-values
指示 Helm 去获取能在 child:
路径中找到的任何值,然后拷贝到 parent:
指定的路径。
xxxxxxxxxx
# 父 Chart 的 Chart.yaml 文件
dependencies
name subchart1
repository http //localhost10191
version0.1.0
...
import-values
child default.data
parent myimports
在上面的例子中,在 subchart1 的 value 中找到的 default.data
的值将被导入到父 Chart 的 myimports
键中。
假设父 Chart 的 values.yaml 文件如下:
xxxxxxxxxx
myimports
myint0
myboolfalse
mystring"helm rocks!"
subchart1 的 values.yaml 文件如下:
xxxxxxxxxx
default
data
myint999
mybooltrue
那么父 Chart 结果值如下:
xxxxxxxxxx
myimports
myint999
mybooltrue
mystring"helm rocks!"
父 Chart 的最终值包含从 subchart1导入的 myint
和 mybool
字段。
charts/
目录,手动管理依赖如果想对依赖进行更多控制,那么可以通过将有依赖关系的 Chart 复制到 charts/
目录中的方式,显式表达依赖关系。
依赖应该是解压的 Chart 目录,并且名字不能以 _
或 .
开头,否则将被 Chart 加载器忽略。
比如 myapp Chart 依赖 mysql Chart,那么可以这样做:
xxxxxxxxxx
$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm pull bitnami/mysql --untar --untardir myapp/charts
可以看到 mysql Chart 已被放到 myapp/charts/
目录:
xxxxxxxxxx
myapp/charts
└── mysql
上面的部分说明如何指定 Chart 的依赖。这对使用 helm install
和 helm 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 给出。
Values 文件可以为顶级 Chart 以及包含在该 Chart 的 charts/
目录中的任意 Chart 声明值。或者换个说法,values 文件可以为 Chart 及其任意依赖项提供值。比如在下面的示例中,WordPress Chart 有两个依赖 - mysql
和 apache
。values 文件可以向所有组件提供值:
xxxxxxxxxx
title"My WordPress Site" # 发送给 WordPress 模版
mysql
max_connections 100 # 发送给 MySQL
password"secret"
apache
port 8080 # 传递给 Apache
高级 Chart 可以访问下级定义的所有变量。因此 WordPress Chart 可以用 .Values.mysql.password
访问 MySQL 密码。 但是低级 Chart 不能访问父级 Chart 的内容,所以 MySQL 不能访问 title
属性。同样也不能访问 apache.port
。
Values 被限制在命名空间中,但是命名空间已被删减。因此对于 WordPress Chart, 它可以用 .Values.mysql.password
访问 MySQL 密码。但是对于 MySQL Chart,值的作用域已经缩小,命名空间前缀已经移除, 因此它可以用 .Values.password
查看密码字段。
从 2.0.0-Alpha.2 开始,Helm 支持特殊的"global"值。下面是前面的示例的修改版本:
xxxxxxxxxx
title"My WordPress Site" # Sent to the WordPress template
global
app MyWordPress
mysql
max_connections 100 # Sent to MySQL
password"secret"
apache
port 8080 # Passed to Apache
上面添加了 global
区域,其值为 app: MyWordPress
。所有 Chart 都可以用 .Values.global.app
访问该值。
比如,mysql
模板可以用 {{.Values.global.app}}
访问 app
,apache
Chart 同样可以。 实际上,上面的 values 文件被重新生成为这样:
xxxxxxxxxx
title"My WordPress Site" # Sent to the WordPress template
global
app MyWordPress
mysql
global
app MyWordPress
max_connections 100 # Sent to MySQL
password"secret"
apache
global
app MyWordPress
port 8080 # Passed to Apache
这提供一种与所有子 Chart 共享顶级变量的方式,这在设置 metadata
属性(比如标签)时很有用。
如果子 Chart 声明全局变量,那么全局变量将向下传递(到子 Chart 的子 Chart),但不会向上传递到父 Chart。子 Chart 无法影响父 Chart 的值。
并且,父 Chart 的全局变量优先于子 Chart 中的全局变量。
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
种类的实例:
xxxxxxxxxx
crontabs/
Chart.yaml
crds/
crontab.yaml
templates/
mycrontab.yaml
crontab.yaml
文件必须包含无模版指令的 CRD:
xxxxxxxxxx
kind CustomResourceDefinition
metadata
name crontabs.stable.example.com
spec
group stable.example.com
versions
name v1
servedtrue
storagetrue
scope Namespaced
names
plural crontabs
singular crontab
kind CronTab
然后模板 mycrontab.yaml
可以创建新 CronTab
(像平常一样使用模版):
xxxxxxxxxx
apiVersion stable.example.com
kind CronTab
metadata
name .Values.name
spec
# ...
Helm 在处理安装 templates/
中的内容之前,保证 CronTab
种类安装成功,并且对 Kubernetes API Server 可用。
与 Kubernetes 中的大多数对象不同,CRD 是全局安装的。因此 Helm 管理 CRD 时采取非常谨慎的方式。CRD 受到如下限制:
crds/
目录中的 CRD 已经存在(不关心版本),那么 Helm 不尝试安装或升级升级或删除 CRD 的操作人员应该谨慎地手动执行此操作。
Helm 提供钩子机制,允许 Chart 开发者在发布生命周期的某些点进行干预。比如,可以使用钩子:
钩子的工作方式与常规模板类似,但它们有特殊的注解,导致 Helm 以不同的方式利用它们。下面讲述钩子的基本使用模式。
Helm 定义以下钩子:
注解值 | 描述 |
---|---|
pre-install | 在渲染模版后,创建 Kubernetes 资源之前执行 |
post-install | 在所有资源都被加载进 Kubernetes 后执行 |
pre-delete | 在从 Kubernetes 删除资源前,执行删除请求 |
post-delete | 在删除发布的所有资源后,执行删除请求 |
pre-upgrade | 在渲染模版后,更新资源前执行升级请求 |
post-upgrade | 在升级所有资源后,执行升级请求 |
pre-rollback | 在渲染模版后,回滚资源前执行回滚请求 |
post-rollback | 在修改资源后,执行回滚请求 |
test | 当调用 Helm test 子命令时执行 |
钩子使 Chart 开发者有机会在发布生命周期的关键节点上执行操作。比如,默认情况下,helm install
的生命周期如下:
helm install foo
foo
模版Helm 为 install
生命周期定义两个钩子:pre-install
和 post-install
。如果 foo Chart 的开发者实现这两个钩子,那么生命周期变成这样:
helm install foo
crds/
目录中的 CRDfoo
模版pre-install
钩子(将钩子资源加载到 Kubernetes)--wait
标记,那么库将等待到所有资源处于就绪状态,直到它们就绪后,才运行 post-install
钩子post-install
钩子(加载钩子资源)等待到钩子就绪意味着什么?这取决于钩子中声明的资源。如果资源种类是 Job
或 Pod
,那么 Helm 将等到它成功地运行到完成。如果钩子失败,那么发布失败。这是阻塞操作,因此 Helm 客户端在 Job 运行期间将暂停。
对于其它种类,一旦 Kubernetes 将资源标记为已加载(已添加或已更新),就认为资源已“就绪”。当在钩子中声明多个资源时,那么串行执行这些资源。如果资源有钩子权重,那么按照权重顺序执行它们。从 Helm 3.2.0 开始,具有相同权重的钩子资源以与普通非钩子资源相同的顺序安装。否则,无法保证顺序。(在 Helm 2.3.0 及以后,它们按照字母序排序。不过该行为将来可能改变。)增加钩子权重被认为是最佳实践,如果权重不重要,那么将其设置为 0
。
当前,钩子创建的资源无法作为发布的一部分进行跟踪和管理。一旦 Helm 确定钩子达到它的就绪状态,它不再管理钩子资源。当删除相应的发布时,钩子资源的垃圾回收将来可能被添加进 Helm v3。因此不能删除的钩子资源应该使用 helm.sh/resource-policy: keep
注释。
实际上,如果在钩子中创建资源,不能依靠 helm uninstall
删除这些资源。为销毁这类资源,要么向钩子模版文件添加自定义的 helm.sh/hook-delete-policy
注解,要么设置 Job 资源的生存(TTL)周期字段。
钩子是在 metadata
部分指定特殊注解的 Kubernetes 清单文件。因为它们是模板文件,所以可以使用所有普通模板特性,包括读取 .Values
、 .Release
和 .Template
。
比如,存储在 templates/post-install-job.yaml
的模版声明一个在 post-install
运行的 Job:
xxxxxxxxxx
apiVersion batch/v1
kind Job
metadata
name"{{ .Release.Name }}"
labels
app.kubernetes.io/managed-by .Release.Service | quote
app.kubernetes.io/instance .Release.Name | quote
app.kubernetes.io/version .Chart.AppVersion
helm.sh/chart"{{ .Chart.Name }}-{{ .Chart.Version }}"
annotations
# This is what defines this resource as a hook. Without this line, the
# job is considered part of the release.
"helm.sh/hook" post-install
"helm.sh/hook-weight""-5"
"helm.sh/hook-delete-policy" hook-succeeded
spec
template
metadata
name"{{ .Release.Name }}"
labels
app.kubernetes.io/managed-by .Release.Service | quote
app.kubernetes.io/instance .Release.Name | quote
helm.sh/chart"{{ .Chart.Name }}-{{ .Chart.Version }}"
spec
restartPolicy Never
containers
name post-install-job
image"alpine:3.3"
command"/bin/sleep""{{ default "10" .Values.sleepyTime }}"
下面的注解使该模版成为钩子:
xxxxxxxxxx
annotations
"helm.sh/hook" post-install
一个资源可以实现多个钩子:
xxxxxxxxxx
annotations
"helm.sh/hook" post-install,post-upgrade
类似地,实现给定钩子的不同资源的数量没有限制。比如,可以将 secret 和 configmap 都声明为 pre-install 钩子。
当子 Chart 声明钩子时,这些钩子也将被计算。顶级 Chart 无法禁用子 Chart 中声明的钩子。
为钩子定义权重有助于构建确定性的执行顺序。使用如下注解定义权重:
xxxxxxxxxx
annotations
"helm.sh/hook-weight""5"
钩子权重可以是正数,也可以是负数,但必须是字符串形式。当 Helm 开始执行特定种类的钩子时,它将对这些钩子进行升序排序。
可以定义决定何时删除相应的钩子资源的策略。使用如下注解定义钩子删除策略:
xxxxxxxxxx
annotations
"helm.sh/hook-delete-policy" before-hook-creation,hook-succeeded
可以选择一个或多个注解值:
注解值 | 描述 |
---|---|
before-hook-creation | 在新钩子启动前,删除之前的资源(默认) |
hook-succeeded | 在钩子成功执行后,删除资源 |
hook-failed | 在执行期间,如果钩子失败,那么删除资源 |
如果未指定钩子删除策略注解,那么默认应用 before-hook-creation
行为。
安装 MySQL 数据库后,使用 Job 创建 Database 和表。
如果未添加 bitnami 仓库,那么使用如下命令添加:
xxxxxxxxxx
helm repo add bitnami https://charts.bitnami.com/bitnami
更新 bitnami 仓库:
xxxxxxxxxx
helm repo update bitnami
如果集群尚未准备存储类型(StorageClass),那么可以安装 OpenEBS,安装过程可参考 http://timd.cn/k8s/openebs-installation/。使用如下命令查看存储类型:
xxxxxxxxxx
kubectl get sc
创建 Chart:
xxxxxxxxxx
helm create mysqltest
删除 mysqltest/templates/
目录下的文件:
xxxxxxxxxx
rm -rf mysqltest/templates/*
在 mysqltest/Chart.yaml 文件的最后面添加下面内容:
xxxxxxxxxx
dependencies:
- name: mysql
version: 9.4.5
repository: "@bitnami"
向 mysqltest/templates/
目录添加模版文件 initjob.yaml:
xxxxxxxxxx
"-" "_" | upper - - $mysqlEnvPrefix := .Release.Name | replace
apiVersion batch/v1
kind Job
metadata
name .Release.Name -initjob
annotations
"helm.sh/hook" post-install
"helm.sh/hook-weight""100"
"helm.sh/hook-delete-policy" before-hook-creation
spec
template
spec
containers
name .Release.Name -initjob
image docker.io/bitnami/mysql 8.0.31-debian-11-r20
command"/bin/sh" "-c" "mysql -h${{$mysqlEnvPrefix}}_SERVICE_HOST -P${{$mysqlEnvPrefix}}_SERVICE_PORT -uroot -p$MYSQL_ROOT_PASSWORD -e 'CREATE DATABASE IF NOT EXISTS test; USE test; CREATE TABLE IF NOT EXISTS test(id INT PRIMARY KEY); REPLACE INTO test(id) VALUES (1);' && echo 'initialize successfully' && echo 'environments:' && printenv"
env
name MYSQL_ROOT_PASSWORD
valueFrom
secretKeyRef
name .Release.Name
key mysql-root-password
restartPolicy Never
backoffLimit3
activeDeadlineSeconds240
该 Job 实现 post-install
钩子,因此将在安装完 MySQL 后执行,用于对 MySQL 进行初始化。
向 mysqltest/templates/
目录添加模版文件 NOTES.txt:
xxxxxxxxxx
首先安装第三方组件,比如数据库、消息队列。
然后使用 post-install 钩子,执行初始化工作,比如创建 MySQL 表,创建 Kafka Topic。
最好在本文件提供人工验证方式,比如
1. 验证 bitnami/mysql:
kubectl exec {{.Release.Name}}-0 -- mysql -uroot -p$(kubectl get secret {{.Release.Name}} -o jsonpath='{.data.mysql-root-password}' | base64 -d) -e "USE test; SELECT * from test;"
2. 查看 Job 输出:
a)先查询 Job Pods:kubectl get pod --selector=job-name={{.Release.Name}}-initjob
b)然后执行:kubectl logs <Job Pod 名称>
卸载时
1. 删除 PVC:kubectl delete pvc data-{{.Release.Name}}-0,防止下次安装失败
注意:
1. 在执行 helm install 时,必须指定 --wait 参数。即等到 MySQL 就绪后,再执行初始化 Job
2. 初始化 Job 在下次安装时删除,以便查看其日志,初始化 Job 最好提供幂等性支持,比如创建表可以使用 CREATE DATABASE IF NOT EXISTS ...,即不存在时创建,存在时不执行任何操作
该文件包含简要使用说明。在该文件里可以使用模版参数。
更新依赖:
xxxxxxxxxx
helm dep up mysqltest
执行成功后,将在 mysqltest/charts/
目录生成 mysql-9.4.5.tgz。
安装:
xxxxxxxxxx
helm install --debug --wait mysqltest mysqltest
安装前,可以先使用如下命令进行调试(不真正安装):
xxxxxxxxxx
helm install --debug --dry-run mysqltest mysqltest
安装成功后,将看到渲染后的使用说明。接下来可以按照使用说明,进行人工验证。
Helm 3 提供完全重组的 Go SDK,以便在构建利用 Helm 的软件和工具时获得更好的体验。完整的文档地址在 https://pkg.go.dev/helm.sh/helm/v3,下面简要介绍最常见的包,以及一个简单的示例。
下面列举最常用的包,并且对每个包进行简单的解释:
pkg/action
:包含用于执行 Helm 操作的主“客户端”,CLI 的底层也使用该包。如果只需要从其它 Go 程序执行基本 Helm 命令,那么可以使用该包pkg/{chart,chartutil}
:用于加载和操作 Chart 的方法和辅助程序pkg/cli
及其子包:包含标准 Helm 环境变量的所有处理器,它的子包包含输出和值文件处理pkg/release
:定义 Release 对象及状态显然,除这些包外还有很多包,所以请查看文档,获取更多信息!
下面是使用 Go SDK 执行 helm list
的简单示例:
xxxxxxxxxx
package main
import (
"log"
"os"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
)
func main() {
settings := cli.New()
actionConfig := new(action.Configuration)
// You can pass an empty string instead of settings.Namespace() to list
// all namespaces
if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), log.Printf); err != nil {
log.Printf("%+v", err)
os.Exit(1)
}
client := action.NewList(actionConfig)
// Only list deployed
client.Deployed = true
results, err := client.Run()
if err != nil {
log.Printf("%+v", err)
os.Exit(1)
}
for _, rel := range results {
log.Printf("%+v", rel)
}
}