~/.kube/configHelm 是 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 listNAME URLopenebs https://openebs.github.io/chartsbitnami 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/mysqlNAME: mysqlLAST DEPLOYED: Tue Jan 3 11:14:32 2023NAMESPACE: defaultSTATUS: deployedREVISION: 1TEST SUITE: NoneNOTES:CHART NAME: mysqlCHART VERSION: 9.4.5APP 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 mysqlNAME: mysqlLAST DEPLOYED: Tue Jan 3 11:19:11 2023NAMESPACE: defaultSTATUS: deployedREVISION: 1TEST SUITE: NoneNOTES:CHART NAME: mysqlCHART VERSION: 9.4.5APP 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:
namevalue多个值使用逗号分割,因此 --set a=b,c=d 的 YAML 表示是:
abcd支持更复杂的表达式。例如,--set outer.inner=value 被转换为:
xxxxxxxxxxouter innervalue使用 { 和 } 中括起来的值表示列表。例如,--set name={a, b, c} 转换为:
xxxxxxxxxxnameabc某些 name/key 可被设置为 null 或者空数组 [],例如 --set name=[],a=null 转换为:
xxxxxxxxxxnameanull可以使用数组下标语法访问列表中的元素。例如 --set servers[0].port=80 变成:
xxxxxxxxxxserversport80也可以通过这种方式设置多个值。--set servers[0].port=80,servers[0].host=example 变成:
xxxxxxxxxxserversport80 hostexample如果需要在 --set 中使用特殊字符,那么可以使用反斜线进行转义。--set name=value1\,value2 将变成:
xxxxxxxxxxname"value1,value2"类似地,也可以转义点(英文句号)。这可能在 Chart 使用 toYaml 函数解析 annotations、labels 和 node selectors 时派上用场。--set nodeSelector."kubernetes\.io/role"=master 变成:
xxxxxxxxxxnodeSelector kubernetes.io/rolemaster深度嵌套的数据结构很难用 --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/mysqlhelm get 命令是查看集群中 Release 的实用工具。可以使用 helm get values 命令,查看配置值是否生效:
xxxxxxxxxx$ helm get values mysqlUSER-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 listNAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSIONmetallb 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 myappCreating 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 目录匹配以下结构:
xxxxxxxxxxmyapp # 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文件是必需的,包含以下字段:
xxxxxxxxxxapiVersionChart API 版本(必需)nameChart 名称(必需)version语义化 2 版本(必需)kubeVersion兼容的 Kubernetes 版本的语义化版本范围(可选)description项目描述(可选)typeChart 类型(可选)keywords关于项目的一组关键字(可选)home项目 Home 页面的 URL(可选)sources项目源码的 URL 列表(可选)dependencies# Chart 必要条件列表(可选)nameChart 名称(比如 nginx) versionChart 版本(比如 "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 字段中被定义为一个列表:
xxxxxxxxxxdependenciesnameapache version1.2.3 repositoryhttps//example.com/chartsnamemysql version3.2.1 repositoryhttps//another.example.com/charts其中:
helm repo add 在本地添加该仓库xxxxxxxxxx$ helm repo add fantastic-charts https://fantastic-charts.storage.googleapis.comxxxxxxxxxxdependenciesnameawesomeness version1.0.0 repository"@fantastic-charts"定义好依赖后,运行 helm dependency update 将所有依赖下载到 charts/ 目录:
xxxxxxxxxx$ helm dep up myappHang 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 repositoryUpdate Complete. ⎈Happy Helming!⎈Saving 1 chartsDownloading mysql from repo https://charts.bitnami.com/bitnamiDeleting outdated charts当 helm dependency update 拉取 Chart 时,将它们以 Chart 归档文件的格式存储在 charts/ 目录。因此对于上面的示例,将在 chart/ 目录中看到以下文件:
xxxxxxxxxxcharts└── mysql-9.4.5.tgz
除上面的字段外,每个依赖项可以包含可选的 alias 字段。该字段为 Chart 指定别名,别名将作为子 Chart 的名称,访问子 Chart 时可以使用别名。
xxxxxxxxxx# parentchart/Chart.yaml
dependenciesnamesubchart repositoryhttp//localhost10191 version0.1.0 aliasnew-subchart-1namesubchart repositoryhttp//localhost10191 version0.1.0 aliasnew-subchart-2namesubchart repositoryhttp//localhost10191 version0.1.0在上述例子中,parentchart 有 3 个依赖项:
xxxxxxxxxxsubchartnew-subchart-1new-subchart-2
除上面的字段外,每个依赖项可以包含可选的 tags 和 condition 字段。
默认情况下,将加载所有 Chart。如果存在 tags 或 condition 字段,那么将计算它们,用于控制应用它们的 Chart 的加载。
xxxxxxxxxx# parentchart/Chart.yaml
dependenciesnamesubchart1 repositoryhttp//localhost10191 version0.1.0 conditionsubchart1.enabled,global.subchart1.enabled tagsfront-endsubchart1namesubchart2 repositoryhttp//localhost10191 version0.1.0 conditionsubchart2.enabled,global.subchart2.enabled tagsback-endsubchart2xxxxxxxxxx# parentchart/values.yaml
subchart1 enabledtruetags 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 参数可以用于设置标签和条件值:
xxxxxxxxxxhelm 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 文件
dependenciesnamesubchart repositoryhttp//localhost10191 version0.1.0 import-valuesdataxxxxxxxxxx# 子 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 文件
dependenciesnamesubchart1 repositoryhttp//localhost10191 version0.1.0 ... import-valueschilddefault.data parentmyimports在上面的例子中,在 subchart1 的 value 中找到的 default.data 的值将被导入到父 Chart 的 myimports 键中。
假设父 Chart 的 values.yaml 文件如下:
xxxxxxxxxxmyimports myint0 myboolfalse mystring"helm rocks!"subchart1 的 values.yaml 文件如下:
xxxxxxxxxxdefault data myint999 mybooltrue那么父 Chart 结果值如下:
xxxxxxxxxxmyimports 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 updateHang tight while we grab the latest from your chart repositories......Successfully got an update from the "bitnami" chart repositoryUpdate Complete. ⎈Happy Helming!⎈$ helm pull bitnami/mysql --untar --untardir myapp/charts可以看到 mysql Chart 已被放到 myapp/charts/ 目录:
xxxxxxxxxxmyapp/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 文件可以向所有组件提供值:
xxxxxxxxxxtitle"My WordPress Site" # 发送给 WordPress 模版
mysql max_connections100 # 发送给 MySQL password"secret"
apache port8080 # 传递给 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"值。下面是前面的示例的修改版本:
xxxxxxxxxxtitle"My WordPress Site" # Sent to the WordPress template
global appMyWordPress
mysql max_connections100 # Sent to MySQL password"secret"
apache port8080 # Passed to Apache上面添加了 global 区域,其值为 app: MyWordPress。所有 Chart 都可以用 .Values.global.app 访问该值。
比如,mysql 模板可以用 {{.Values.global.app}} 访问 app,apache Chart 同样可以。 实际上,上面的 values 文件被重新生成为这样:
xxxxxxxxxxtitle"My WordPress Site" # Sent to the WordPress template
global appMyWordPress
mysql global appMyWordPress max_connections100 # Sent to MySQL password"secret"
apache global appMyWordPress port8080 # 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 种类的实例:
xxxxxxxxxxcrontabs/ Chart.yaml crds/ crontab.yaml templates/ mycrontab.yamlcrontab.yaml 文件必须包含无模版指令的 CRD:
xxxxxxxxxxkindCustomResourceDefinitionmetadata namecrontabs.stable.example.comspec groupstable.example.com versionsnamev1 servedtrue storagetrue scopeNamespaced names pluralcrontabs singularcrontab kindCronTab然后模板 mycrontab.yaml 可以创建新 CronTab(像平常一样使用模版):
xxxxxxxxxxapiVersionstable.example.comkindCronTabmetadata 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 foofoo 模版Helm 为 install 生命周期定义两个钩子:pre-install 和 post-install。如果 foo Chart 的开发者实现这两个钩子,那么生命周期变成这样:
helm install foocrds/ 目录中的 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:
xxxxxxxxxxapiVersionbatch/v1kindJobmetadata 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-succeededspec 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 restartPolicyNever containersnamepost-install-job image"alpine:3.3" command"/bin/sleep""{{ default "10" .Values.sleepyTime }}"下面的注解使该模版成为钩子:
xxxxxxxxxxannotations "helm.sh/hook"post-install一个资源可以实现多个钩子:
xxxxxxxxxxannotations "helm.sh/hook"post-install,post-upgrade类似地,实现给定钩子的不同资源的数量没有限制。比如,可以将 secret 和 configmap 都声明为 pre-install 钩子。
当子 Chart 声明钩子时,这些钩子也将被计算。顶级 Chart 无法禁用子 Chart 中声明的钩子。
为钩子定义权重有助于构建确定性的执行顺序。使用如下注解定义权重:
xxxxxxxxxxannotations "helm.sh/hook-weight""5"钩子权重可以是正数,也可以是负数,但必须是字符串形式。当 Helm 开始执行特定种类的钩子时,它将对这些钩子进行升序排序。
可以定义决定何时删除相应的钩子资源的策略。使用如下注解定义钩子删除策略:
xxxxxxxxxxannotations "helm.sh/hook-delete-policy"before-hook-creation,hook-succeeded可以选择一个或多个注解值:
| 注解值 | 描述 |
|---|---|
| before-hook-creation | 在新钩子启动前,删除之前的资源(默认) |
| hook-succeeded | 在钩子成功执行后,删除资源 |
| hook-failed | 在执行期间,如果钩子失败,那么删除资源 |
如果未指定钩子删除策略注解,那么默认应用 before-hook-creation 行为。
安装 MySQL 数据库后,使用 Job 创建 Database 和表。
如果未添加 bitnami 仓库,那么使用如下命令添加:
xxxxxxxxxxhelm repo add bitnami https://charts.bitnami.com/bitnami更新 bitnami 仓库:
xxxxxxxxxxhelm repo update bitnami如果集群尚未准备存储类型(StorageClass),那么可以安装 OpenEBS,安装过程可参考 http://timd.cn/k8s/openebs-installation/。使用如下命令查看存储类型:
xxxxxxxxxxkubectl get sc创建 Chart:
xxxxxxxxxxhelm create mysqltest删除 mysqltest/templates/ 目录下的文件:
xxxxxxxxxxrm -rf mysqltest/templates/*在 mysqltest/Chart.yaml 文件的最后面添加下面内容:
xxxxxxxxxxdependencies:- name: mysql version: 9.4.5 repository: "@bitnami"向 mysqltest/templates/ 目录添加模版文件 initjob.yaml:
xxxxxxxxxx- $mysqlEnvPrefix := .Release.Name | replace "-" "_" | upper -apiVersionbatch/v1kindJobmetadata name.Release.Name-initjob annotations "helm.sh/hook"post-install "helm.sh/hook-weight""100" "helm.sh/hook-delete-policy"before-hook-creationspec template spec containersname.Release.Name-initjob imagedocker.io/bitnami/mysql8.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" envnameMYSQL_ROOT_PASSWORD valueFrom secretKeyRef name.Release.Name keymysql-root-password restartPolicyNever 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}}-initjobb)然后执行:kubectl logs <Job Pod 名称>卸载时1. 删除 PVC:kubectl delete pvc data-{{.Release.Name}}-0,防止下次安装失败注意:1. 在执行 helm install 时,必须指定 --wait 参数。即等到 MySQL 就绪后,再执行初始化 Job2. 初始化 Job 在下次安装时删除,以便查看其日志,初始化 Job 最好提供幂等性支持,比如创建表可以使用 CREATE DATABASE IF NOT EXISTS ...,即不存在时创建,存在时不执行任何操作
该文件包含简要使用说明。在该文件里可以使用模版参数。
更新依赖:
xxxxxxxxxxhelm dep up mysqltest执行成功后,将在 mysqltest/charts/ 目录生成 mysql-9.4.5.tgz。
安装:
xxxxxxxxxxhelm install --debug --wait mysqltest mysqltest安装前,可以先使用如下命令进行调试(不真正安装):
xxxxxxxxxxhelm 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 的简单示例:
xxxxxxxxxxpackage 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) }}