Helm简介
Helm 是 Kubernetes 的开源包管理器。它提供了一种查找、共享和使用为 Kubernetes 构建的软件的简便方法。该项目使用一种称为图表的打包格式,它是描述一组相关 Kubernetes 资源的文件集合。
Helm 于 2015 年在 Deis 创建,后来被微软收购。现在称为 Helm Classic 的是在当年 11 月的首届 KubeCon 上推出的。2016 年 1 月,Helm Classic 与谷歌的 Kubernetes 部署管理器合并到现在是 Helm 主要项目的存储库中。2018 年 6 月 加入 CNCF 。2018年年底推出了Helm Hub用于存储chart。2019年11月发布V3版本。
三大概念
Chart:代表着 Helm 包。它包含在 Kubernetes 集群内部运行应用程序,工具或服务所需的所有资源定义。你可以把它看作是 Homebrew formula,Apt dpkg,或 Yum RPM 在Kubernetes 中的等价物。
Repository:仓库,是用来存放和共享 charts 的地方。
Release:类似于版本号,Release 是运行在 Kubernetes 集群中的 chart 的实例。一个 chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 release。
特色
轻松创建和托管自己的应用(以应用为一个维度,而不是k8s的一个deploy、cm等)
将软件包安装到任何 K8S 集群中
查询集群以查看已安装和正在运行的程序包
更新、删除、回滚或查看已安装软件包的历史记录
目标
从头开始创建新的chart
将chart打包成归档(tgz)文件
与存储chart的仓库进行交互
在现有的Kubernetes集群中安装和卸载chart
管理与Helm一起安装的chart的发布周期
版本形式和版本偏差
Helm 的版本用
x.y.z
的形式描述,x
是主版本,y
是次版本,z
是补丁版本。Helm的版本与k8s存在版本的兼容性问题:Helm 没有向上兼容机制,故推荐安装下表进行版本选择。
https://helm.sh/docs/topics/version_skew/
Helm Version | Supported Kubernetes Versions |
---|---|
3.11.x | 1.26.x - 1.23.x |
3.10.x | 1.25.x - 1.22.x |
3.9.x | 1.24.x - 1.21.x |
3.8.x | 1.23.x - 1.20.x |
3.7.x | 1.22.x - 1.19.x |
3.6.x | 1.21.x - 1.18.x |
3.5.x | 1.20.x - 1.17.x |
3.4.x | 1.19.x - 1.16.x |
3.3.x | 1.18.x - 1.15.x |
3.2.x | 1.18.x - 1.15.x |
3.1.x | 1.17.x - 1.14.x |
3.0.x | 1.16.x - 1.13.x |
2.16.x | 1.16.x - 1.15.x |
2.15.x | 1.15.x - 1.14.x |
2.14.x | 1.14.x - 1.13.x |
2.13.x | 1.13.x - 1.12.x |
V2和V3版本变化
2019年11月,helm团队发布了第一个helm v3
稳定版,其与helm v2
主要的变化如下:
1、架构变化:最显著的变化,就是删除了Tiller而采用kubeconfig来授权和调用k8s api.
2、Release变化:Release名称可以在不通NameSpace中重用
3、chart存储变化:支持将chart推送到OCI注册中心(类似于Docker Registry)
4、支持JSONschema的Chart Values
5、其他
helm delete 更名 helm uninstall
helm inspect 更名 helm show
helm fetch 更名 helm pull
helm reset (已删除)
helm serve (已删除)
移除了用于本地临时搭建Chart Repository的命令
helm serve
不再需要
requirements.txt
,将其合并到Chart.yaml
中
社区chart仓库
https://artifacthub.io/packages/search?kind=0
安装Helm
helm v3(推荐)
下载想要的版本:[https://github.com/helm/helm/tags)
# 安装二进制命令
wget https://get.helm.sh/helm-v2.17.0-linux-amd64.tar.gz
tar -zxvf helm-v2.17.0-linux-amd64.tar.gz
cp helm-v2.17.0-linux-amd64/helm /usr/local/sbin/
# 安装Tiller:https://github.com/orgs/helm/packages/container/package/tiller
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
EOF
# init安装tiller
helm init --service-account tiller --skip-refresh
kubectl get pod -n kube-system -l app=helm
helm v2(不推荐)
下载想要的版本:https://github.com/helm/helm/releases/tag/v2.17.0
# 安装二进制命令
wget https://get.helm.sh/helm-v2.17.0-linux-amd64.tar.gz
tar -zxvf helm-v2.17.0-linux-amd64.tar.gz
cp helm-v2.17.0-linux-amd64/helm /usr/local/sbin/
# 安装Tiller:https://github.com/orgs/helm/packages/container/package/tiller
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
EOF
# init安装tiller
helm init --service-account tiller --skip-refresh
kubectl get pod -n kube-system -l app=helm
Helm常用命令
命令自动补全
yum install -y bash-completion
source <(helm completion bash)
helm completion bash > /etc/bash_completion.d/helm
# 查看helm相关环境变量
helm env
# 添加公用或私有仓库
helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add elastic https://helm.elastic.co
helm repo add gitlab https://charts.gitlab.io
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add incubator https://kubernetes-charts-incubator.storage.googleapis.com
helm repo add stable https://kubernetes-charts.storage.googleapis.com
helm repo add harbor https://helm.goharbor.io
# 在 Repo 中查询可安装的 chart 包(支持模糊匹配)
$ helm search repo harbor
NAME CHART VERSION APP VERSION DESCRIPTION
harbor/harbor 1.11.1 2.7.1 An open source trusted cloud native reg...
$ helm search repo harbor --version 1.5
NAME CHART VERSION APP VERSION DESCRIPTION
harbor/harbor 1.5.6 2.1.6 An open source trusted cloud native reg...
# 下载一个chart到本地(--untar 表示解压,不加下载后为.tgz)
helm pull harbor/harbor --untar
# 检查一个chart是否合法
helm lint mychart
# 安装名为nginx的chart包在bitnmai仓库,并制定命令空间
helm install [NAME] [CHART] [-n namespace]
helm install nginx bitnami/nginx -n mycloud
helm install nginx bitnami/nginx --debug --dry-run # 用于调试,并不真正执行
# 查看全部应用(包含安装和卸载的)
helm list -n mydlqcloud --all
# 查看应用状态
helm status nginx -n mycloud
# 卸载应用,并保留安装记录
helm uninstall nginx -n mycloud --keep-history
# 卸载应用,不保留安装记录
helm delete nginx -n mycloud
# 渲染模板(查看通过指定的参数渲染的 Kubernetes 部署资源模板)
helm template bitnami/nginx
# 查看chart 可配置参数
helm show values bitnami/nginx
# 查看已安装chart配置的参数
helm get values nginx
# 应用升级
helm upgrade -f values.yaml nginx bitnami/nginx -n mycloud
# 应用回滚
helm history nginx
helm rollback nginx 1
Chart打包上传到chartmusem仓库(以Harbor为例)
需要helm 插件:https://github.com/chartmuseum/helm-push
注意:上传比较规划的公网公开chat还应该做签名,并生成一个*.prov
helm package --sign --key 'helm signing key' --keyring path/to/keyring.secret mychart
# 打包上传Chart
# * 打包chart(版本默认由<chart_name>/Chart.yaml 中的version决定)
helm package <chart_name>
# * 生成索引
helm repo index .
# * 方式一:下载push插件
$ helm plugin install https://github.com/chartmuseum/helm-push
Downloading and installing helm-push v0.10.3 ...
https://github.com/chartmuseum/helm-push/releases/download/v0.10.3/helm-push_0.10.3_linux_amd64.tar.gz
# * 方式二:helm plugin install 失败,可以wget下载然后解压打插件目录
wget https://github.com/chartmuseum/helm-push/releases/download/v0.10.3/helm-push_0.10.3_linux_amd64.tar.gz
$ helm env | grep PLUGIN
HELM_PLUGINS="/root/.local/share/helm/plugins"
$ mkdir /root/.local/share/helm/plugins/cm-push
tar xf helm-push_0.10.3_linux_amd64.tar.gz -C /root/.local/share/helm/plugins/cm-push
# 查看helm插件
helm plugin list
# 添加要上传的仓库
helm repo add --username admin --password Harbor12345 --ca-file /harbor/tls/ca.crt localchart https://harbor.wzp.com/chartrepo/library
# 上传Chart到仓库
helm cm-push --ca-file /harbor/tls/ca.crt mycahrt-0.1.0.tgz localchart
Chart详解
helm默认本地存储的配置文件
helm env
可以查看
System | Cache Path | Configuration Path | Data Path |
---|---|---|---|
Linux | $HOME/.cache/helm | $HOME/.config/helm | $HOME/.local/share/helm |
macOS | $HOME/Library/Caches/helm | $HOME/Library/Preferences/helm | $HOME/Library/helm |
Windows | %TEMP%\helm | %APPDATA%\helm | %APPDATA%\helm |
什么是Chart?
Helm 使用一种称为Chart的打包格式。Chart是描述一组相关 Kubernetes 资源的文件集合。单个Chart可以用于部署简单的东西,如 memcached pod,或复杂的东西,如带有 HTTP 服务器、数据库、缓存等的完整 Web 应用程序堆栈。部署时通过helm客户端提交给k8s apiserver去完成
Chart的文件结构
mychart/
├── Chart.yaml # 包含了chart信息的YAML文件
├── LICENSE # 可选: 包含chart许可证的纯文本文件
├── README.md # 可选: 可读的README文件
├── values.yaml # chart 默认的配置值
├── values.schema.json # 可选: 一个使用JSON结构的values.yaml文件
├── templates # 资源模版,配置 values 进行渲染,生成有效的 K8s manifest
│ ├── deployment.yaml
│ ├── _helpers.tpl # 定义可以复用的子模板文件
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt # 可选: 包含简要使用说明的纯文本文件
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests # 用于测试的文件
│ └── test-connection.yaml # 具体做测试的容器或是k8s能识别的资源类型
├── crds/ # 自定义资源的定义
├── Chart.lock # 保存先前版本的依赖
├── charts/ # 包含chart依赖的其他chart
├── .helmignore # 指定忽略 Helm 包中哪些文件和目录
└── xxxx # 其他自定义内容,可以是conf/xxx.conf 一些配置文件
文件/目录 | 定义 | 是否必须 |
---|---|---|
Chart.yaml | 该文件中包含了Chart的元数据 | 是 |
templates/ | 存放chart的资源模板文件,这些文件是 YAML 格式 | 是(除非依赖项在 Chart.yaml 文件中声明) |
templates/NOTES.txt | chart的说明文件。安装、升级或执行 notes 命令会输出此文件内容 | 否 |
values.yaml | chart的默认存放变量的文件 | 否,但作为最佳实践,最好包含此文件 |
.helmignore | 指定忽略 Helm 包中哪些文件和目录 | 否 |
charts/ | 存放chart依赖的其他chart | 无需明确提供,Helm 的依赖管理系统会自动创建此目录 |
Chart.lock | 保存先前版本的依赖 | 无需明确提供,Helm 的依赖管理系统会自动创建此文件 |
crds/ | 存储自定义(CRD)资源的目录。YAML 格式。这些资源会在 templates/ 中的资源之前安装。 | 否 |
README.md | chart安装和使用说明的自述文件 | 否,但作为最佳实践,最好包含此文件 |
LICENSE | license 文件 | 否 |
values.schema.json | JSON 格式的chart values文件 | 否 |
Chart文件详解
https://helm.sh/zh/docs/topics/charts/
Chart.yaml
文件
- 注意:从
v3.3.2
之后该文件不允许,设置如下字段以外的任何字段,推荐的方法是在annotations
中添加自定义元数据。
apiVersion: # 必需: chart API 版本, v2 版本为 helm3 语法,v1 版本仍然保留使用
name: # 必需: chart名称
# 遵循规范 https://semver.org/spec/v2.0.0.html
# 打包Chart时,以此版本作为后缀和包版本
version: # 必需: 版本标识 示例:1.0.0 < 1.0.0-alpha < 1.0.0-beta < 2.0.0 < 2.1.0
# 一旦设置,会检测k8s版本,不匹配会显式报错
kubeVersion: # 可选: 兼容的k8s版本标识 # 示例:>= 1.13.0 < 1.14.0 || >= 1.14.1 < 1.15.0
description: # 可选: 项目的描述
# 有两种类型: application(默认)和 library(不可用于安装helm install)
# - library类型:定义了可以由其他chart中Helm 模板共享的chart原语或定义。这允许用户通过chart分享可复用得代码片段来避免重复并保持chart 干燥。
type: # 可选:chart类型
keywords: # 可选: 项目的关键字标识
- xxx
- xxx
home: # 可选:项目home页面的URL
sources: # 可选:项目源码的URL列表
- https://xxxx
- https://xxxx
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: # 可选
- 关联values.yaml文件,可以用于指定启用或禁用所有的带tag的chart。
import-values: # (可选)
- ImportValue 保存源值到导入父键的映射。每项可以是字符串或者一对子/父列表项
alias: (可选) chart中使用的别名。当你要多次添加相同的chart时会很有用
maintainers: # 可选
- name: 维护者名字 (每个维护者都需要)
email: 维护者邮箱 (每个维护者可选)
url: 维护者URL (每个维护者可选)
icon: # 可选:用做icon的SVG或PNG图片URL
appVersion: # 可选:包含的应用版本。不需要是语义化,建议使用引号
# 表示是否被弃用,latest版本被标记为已弃用,则所有的chart都会被认为是已弃用的
deprecated: # 可选:是否被启用,布尔值
# 自定义元数据
annotations:
example: 按名称输入的批注列表 (可选).
LICENSE
文件
许可证(LICENSE)是一个包含了chart license的纯文本文件。
README.md
文件
Markdown格式的说明文件,一般应包含:
chart提供的应用或服务的描述
运行chart的先决条件或要求
values.yaml
的可选项和默认值的描述与chart的安装或配置相关的其他任何信息
crds/
目录
用于存放crd资源的目录,其下存放具体的CRD
Helm确定
crds/
目录中的CRD
已经存在,Helm不会安装或升级。在升级或回滚时不会更新
CRD
。Helm只会在安装时创建CRD
。CRD从不会被删除。自动删除CRD会删除集群中所有命名空间中的所有CRD内容。因此Helm不会删除CRD。
# crds/crontab.yaml
# 需要注意的是:该文件中不能存在模板语言,不可引用变量
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
values.yaml
文件
存放变量的位置,YAML格式,支持层次
变量名称以小写字母开头,单词按驼峰区分:
chicken: true
chickenNoodleSoup: true
.helmignore
文件
用来指定你不想包含在你的helm chart中的文件。
helm package
命令会在打包应用时忽略所有在.helmignore
文件中匹配的文件。
# comment
# Match any file or path named .helmignore
.helmignore
# Match any file or path named .git
.git
# Match any text file
*.txt
# Match only directories named mydir
mydir/
# Match only text files in the top-level directory
/*.txt
# Match only the file foo.txt in the top-level directory
/foo.txt
# Match any file named ab.txt, ac.txt, or ad.txt
a[b-d].txt
# Match any file under subdir matching temp*
*/temp*
*/*/temp*
temp?
不支持'!'作为特殊的引导序列
默认不会排除自身,需要显式添加
.helmignore
末尾空格总会被忽略(不支持转义序列)
不支持'**'语法。
NOTES.txt
文件
在helm install
或 helm upgrade
命令的最后,Helm会打印出对用户有用的信息。
要在chart添加安装说明,只需创建templates/NOTES.txt
文件即可。该文件是纯文本,但会像模板一样处理, 所有正常的模板函数和对象都是可用的。
示例
Thank you for installing {{ .Chart.Name }}.
Your release is named {{ .Release.Name }}.
To learn more about the release, try:
$ helm status {{ .Release.Name }}
$ helm get all {{ .Release.Name }}
Chart模板详解
Helm Chart 模板是按照 Go模板语言书写, 增加了50个左右的附加模板函数 来自 Sprig库 和一些其他 指定的函数。
所有模板文件存储在chart的 templates/
文件夹。 当Helm渲染chart时,它会通过模板引擎遍历目录中的每个文件。
模板的Value来源:
Chart开发者可以在chart中提供一个命名为
values.yaml
(默认)的文件。-
Chart用户可以在执行
helm install -f values.yaml
或helm install --set key=value
来提供一个包含了value的YAML文件或变量- 提供的文件中的value会覆盖chart包中的values.yaml中的value
如果是子chart,就是父chart中的
values.yaml
文件
yaml 文件写法 | set 的写法 |
---|---|
name: value | --set name=value |
a: b c: d | --set a=b,c=d |
outer: inner: value | --set outer.inner=value |
name: - a - b -c | --set name={a, b, c} |
servers: - port: 80 | --set servers[0].port=80 |
servers: - port: 80 host: example | --set servers[0].port=80,servers[0].host=example |
name: "value1,value2" | --set name=value1,value2 |
nodeSelector: kubernetes.io/role: master | --set nodeSelector."kubernetes.io/role"=master |
模板中各资源的先后加载顺序
# 从上到下依次执行
Namespace
NetworkPolicy
ResourceQuota
LimitRange
PodSecurityPolicy
PodDisruptionBudget
ServiceAccount
Secret
SecretList
ConfigMap
StorageClass
PersistentVolume
PersistentVolumeClaim
CustomResourceDefinition
ClusterRole
ClusterRoleList
ClusterRoleBinding
ClusterRoleBindingList
Role
RoleList
RoleBinding
RoleBindingList
Service
DaemonSet
Pod
ReplicationController
ReplicaSet
Deployment
HorizontalPodAutoscaler
StatefulSet
Job
CronJob
Ingress
APIService
模板使用方式
在模板的使用过程中不论是变量
还是函数
都通过{{ }}
中引用
Values 通过模板中{{ .Values }}
对象可访问的values.yaml
文件,也可以通过 {{ .Chart }}
对象可访问的 Chart.yaml
,以此类推。
内置Values
此外helm内置了一些values
-
.Release
该对象描述了版本发布本身。
使用方法如,
{{ .Release.Name }}
(获取 release 的名称)
.Release.Name # 版本名称(非chart的)
.Release.Namespace # 发布的chart版本的命名空间
.Release.Service # 组织版本的服务,通常都是Helm
.Release.IsUpgrade # 如果当前操作是升级或回滚,设置为true
.Release.IsInstall # 如果当前操作是安装,设置为true
-
.Values
访问
values.yaml
的内容。使用方法如,
{{ .Values.image.repository }}
(获取 values.yaml 中image - repository
的值)
-
.Chart
访问
Chart.yaml
的内容。使用方法如,
{{ .Chart.Version }}
注意:Chart.yaml中的字段在调用时首字母大写
语法风格遵循 go 模版规则,类似 jinja2。是一种层级检索的方式,
.
表示层级,最顶层的.
即根
。
-
.Files
访问 chart 中存在的文件的内容 (
.helmignore
除外)。使用方法如,
{{ index .Files "file.name" }}
(获取该文件中name
的值),它等效于{{ .Files.Get name }}
方法。{{ .Files.GetBytes }}
方法等效于访问文件中的[]byte
对象{{ (.Files.Glob "conf/*.conf").AsConfig }}
以configmap的形式获取文件名和内容{{ (.Files.Glob "conf/*.conf").AsSecrets }}
以secret的形式获取文件名和内容
-
.Capabilities
访问 K8s 的
Capabilities
对象。使用方法如,
{{ .Capabilities.KubeVersion }}
(获取 K8s 版本)、{{ .Capabilities.APIVersions.Has "batch/v1" }}
(获取支持的 K8s API 版本)
.Capabilities.APIVersions # 返回所有api列表
.Capabilities.APIVersions.Has $version # 判断是否支持对应的api,布尔值
.Capabilities.KubeVersion # k8s 版本
.Capabilities.KubeVersion.Version # k8s 版本
.Capabilities.KubeVersion.Major # k8s 主版本号
.Capabilities.KubeVersion.Minor # k8s 次版本号
-
.Template
当前被执行的当前模板信息
.Template.Name
: 当前模板的命名空间文件路径 (e.g.mychart/templates/mytemplate.yaml
).Template.BasePath
: 当前chart模板目录的路径 (e.g.mychart/templates
)
使用示例
$ helm create mychart
rm -rf mychart/templates/*
cat << EOF > mychart/values.yaml
who: wzp
user: test
EOF
cat << EOF > mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-cm
data:
rel_name: {{ .Release.Name }}
rel_namespace: {{ .Release.Namespace }}
rel_service: {{ .Release.Service }}
rel_isupgrade: {{ .Release.IsUpgrade }}
rel_install: {{ .Release.IsInstall }}
value_who: {{ .Values.who }}
value_user: {{ .Values.who.user }}
chart_name: {{ .Chart.Name }}
chart_Version: {{ .Chart.Version }}
k8s_metadata: |
{{ .Capabilities.APIVersions.Has "batch/v1" }}
{{ .Capabilities.KubeVersion }}
{{ .Capabilities.KubeVersion.Version }}
{{ .Capabilities.KubeVersion.Major }}
{{ .Capabilities.KubeVersion.Minor }}
EOF
$ tree mychart
mychart
├── charts
├── Chart.yaml
├── templates
│ └── configmap.yaml
└── values.yaml
# 测试执行
helm install mycm mychart/ --debug --dry-run
NAME: mycm
LAST DEPLOYED: Tue Mar 21 07:26:47 2023
NAMESPACE: default
...
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mycm-cm
data:
rel_name: mycm
rel_namespace: default
rel_service: Helm
rel_isupgrade: false
rel_install: true
value_who: map[user:test]
value_user: test
chart_name: mychart
chart_Version: 0.1.0
k8s_metadata: |
true
v1.23.13
v1.23.13
1
23
内置函数
函数的使用格式
语法:
func_name arg1 arg2 ...
倒置命令:
arg1 | func_name arg2 ...
推荐用法
在倒置命令中的|
也被称之为管道符,类似于unix
的管道概念通过管道符|
将一系列的模板语言紧凑地将多个流式处理结果合并的工具。
不过值得注意是管道仅能将第一个参数传递给函数使用,在大多数仅有一个参数的函数使用时很直观
常用函数
quote <str> 或 squote <str> # 将指定字符串添加上双引号或单引号
upper <str> 或 lower <str> # 将指定字符串变成全部大写或小写
indent # 用于指定字符串缩进指定长度,以上一行为标准开始缩进
nindent # 用于指定字符串缩进指定长度,以行首为标准开始缩进,且会在上方添加一个新行
toYAML # 引用一整块YAMl内容,像健康检查,资源配额resources,或者端口,这都是一块一块的内容,通过toYaml引用很方便,(默认定格将后部分内容引入,所以需要连用缩进)
uuid # 生成随机uuid
repeat <str> <n> # 将指定字符串重复n次
trim <str> # 去除字符串两边的空格
trimAll <> <str> # 用于移除两边字符串中指定字符
trimPerfix <> <str> # 用于移除字符串指定的前缀
trimSuffix <> <str> # 用于移除字符串指定后缀
default <str> <default_value> # 指定一个默认值,当引用值不存在时,使用默认值
unixEpoch # 返回时间戳,now | unixEpoch
sha256sum <str> # 用于计算字符串的SHA256值进行加密
htpasswd <user> <pass> # 根据传入的username和paasword生成一个密码的bcrypt哈希值,可以用于HTTP Server的基础认证
encryptAES <盐> <要加密的内容> # 加密函数,使用一个AES-256 CBC加密文本并返回一个base64编码字符串
decryptAES <盐> <加密后的内容> # 解密函数,接收一个AES-256 CBC编码的字符串返回解密文本
b64enc # 将字符串以base64进行编码
printf "%s is %s" "wzp" "boby" # 格式化输出
lookup "apiVersion" "kind" "namespace" "name" # 用于获取k8s的一些信息,类似于kubectl
# k8s 命令 lookup函数
kubectl get pod mypod -n myns lookup "v1" "Pod" "myns" "mypod"
kubectl get pod -n myns lookup "v1" "Pod" "myns" ""
kubectl get pod -A lookup "v1" "Pod" "" ""
kubectl get ns myns lookup "v1" "Namespace" "myns" ""
kubectl get ns lookup "v1" "Namespace" "" ""
结尾附大部分函数说明
使用示例
helm create mychart
rm -rf mychart/templates/*
cat << EOF > mychart/values.yaml
who:
user: test
trim: " space "
str: "hello world hello"
hello:
nindent1: test
nindent2: test2
EOF
cat << EOF > mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-cm
data:
quote: {{ .Release.Name | quote }} # .Release.Name 加双引号
squote: {{ .Release.Name | squote }} # .Release.Name 加单引号
indent: {{ .Release.Name | indent 2 | quote }} # 以当前行的位置,缩进
nindent: {{ .Release.Name | nindent 2 | quote }} # 增加一个换行,然后也行首为标准缩进
{{- with .Values.hello }}
{{- toYaml . | nindent 2 }}
{{- end }}
upper: {{ .Release.Name | upper | quote }} # .Release.Name 全大写
lower: {{ .Release.Name | lower | quote }} # .Release.Name 全小写
repeat3: {{ .Release.Name | repeat 3 | quote }} # 重复.Release.Name 三次,并加上双引号(quote)
default: {{ .Release.Haha | default "默认值" | quote }} # Haha并不存在,给一个默认值,并加上双引号(quote)
all_ns: {{ "v1" | lookup "Namespace" "" "" | quote }} # 相当于kubectl get ns, 加了--dry-run时不会获取值
unixepoch: {{ now | unixEpoch }} # 返回时间戳
trim: {{ .Values.trim | trim | quote }} # 去掉.Values.trim字段两边的空格
trimall: {{ trimAll "wo" "wohello worldwo" }} # 去掉wohello worldwo 两边的 wo
trimprefix: {{ trimPrefix "wo" "wohello worldwo" }} # 去掉wohello worldwo 前边的 wo
trimsuffix: {{ trimSuffix "wo" "wohello worldwo" }} # 去掉wohello worldwo 后边的 wo
sha256sum: {{ sha256sum "hello world" }} # sha256加密 hello world
htpasswd: {{ htpasswd "user1" "password" }} # httppass加密user1和password
b64enc: {{ b64enc "hello world" }} # base64编码 hello world
encryptaes: {{ encryptAES "testkey" "testvalues" }} # 以testky为加密秘钥,加密testvalues
decryptaes: {{ decryptAES "testkey" "1BEUTeflfCLvsg5ivwh3OJ50zDfsHYqM1lYPndFNDck=" }} # 以testkey为秘钥,解密加密内容
uuid: {{ uuidv4 }}
EOF
$ tree mychart
mychart
├── charts
├── Chart.yaml
├── templates
│ └── configmap.yaml
└── values.yaml
# 测试执行
helm install mycm mychart/ --debug --dry-run
install.go:172: [debug] Original chart version: ""
install.go:189: [debug] CHART PATH: /root/wzp/mychart
NAME: mycm
...
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mycm-cm
data:
quote: "mycm" # .Release.Name 加双引号
squote: 'mycm' # .Release.Name 加单引号
indent: " mycm" # 以当前行的位置,缩进
nindent: "\n mycm" # 增加一个换行,然后也行首为标准缩进
nindent1: test
nindent2: test2
upper: "MYCM" # .Release.Name 全大写
lower: "mycm" # .Release.Name 全小写
repeat3: "mycmmycmmycm" # 重复.Release.Name 三次,并加上双引号(quote)
default: "默认值" # Haha并不存在,给一个默认值,并加上双引号(quote)
all_ns: "map[]" # 相当于kubectl get ns, 加了--dry-run时不会获取值
unixepoch: 1679646539 # 返回时间戳
trim: "space" # 去掉.Values.trim字段两边的空格
trimall: hello world # 去掉wohello worldwo 两边的 wo
trimprefix: hello worldwo # 去掉wohello worldwo 前边的 wo
trimsuffix: wohello world # 去掉wohello worldwo 后边的 wo
sha256sum: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 # sha256加密 hello world
htpasswd: user1:$2a$10$HLqEoWCIK0E6NCved.r7me8CiIM0USPMiet9AbE1u6mEuOBRYJ75K # httppass加密user1和password
b64enc: aGVsbG8gd29ybGQ= # base64编码 hello world
encryptaes: dEziheGC1mXzWq+VEHhqN/ebvsl0Km1MJb3Jgs/T5hg= # 以testky为加密秘钥,加密testvalues
decryptaes: testvalues # 以testkey为秘钥,解密加密内容
uuid: 24e17b0e-bfaf-4e52-9a4a-ec3394e691cd
流程控制语句
控制结构(在模板语言中称为"actions")提供控制模板迭代流的能力。Helm的模板语言提供了以下控制结构:
if
/else
, 用来创建条件语句with
, 用来指定范围range
, 提供"for each"类型的循环
if/else
条件控制语句
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
如下值为被解析为false:
布尔
false
数字
0
空字符串
nil
(空或null)空集合(
map
,slice
,tuple
,dict
,array
)
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{ if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{ end }}
# 我们看一下结果,多了一个空行,为什么?
helm install mycm mychart/ --debug --dry-run
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: telling-chimp-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"
空行是怎么来的?
当模板引擎运行时,它 移除了
{{
和}}
里面的内容,但是留下的空白完全保持原样。YAML认为空白是有意义的,因此管理空白变得很重要。helm模板中也给我们提供了处理该问题的方法
首先,模板声明的大括号语法可以通过特殊的字符修改,并通知模板引擎取消空白。{{-
(包括添加的横杠和空格)表示向左删除空白, 而-}}
表示右边的空格应该被去掉。 一定注意空格就是换行
要确保
-
和其他命令之间有一个空格。{{- 3 }}
表示“删除左边空格并打印3”,而{{-3 }}
表示“打印-3”。
使用这个语法,我们就可修改我们的模板,去掉新加的空白行:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{- if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{- end }}
# 注意不要{{- -}} 前后都加-,都加上会变成把两边的新行都删除了
food: {{ .Values.favorite.food | upper | quote }}
{{- if eq .Values.favorite.drink "coffee" -}}
mug: "true"
{{- end -}}
# 这样会变成food: "PIZZA"mug:"true",因为这把两边的新行都删除了。
比较复杂的用法
annotations:
{{- if and .Values.internalTLS.enabled (eq .Values.internalTLS.certSource "auto") }}
checksum/tls: {{ include (print $.Template.BasePath "/internal/auto-tls.yaml") . | sha256sum }}
{{- else if and .Values.internalTLS.enabled (eq .Values.internalTLS.certSource "manual") }}
checksum/tls: {{ include (print $.Template.BasePath "/core/core-tls.yaml") . | sha256sum }}
{{- end }}
spec:
{{- if .Values.core.serviceAccountName }}
serviceAccountName: {{ .Values.core.serviceAccountName }}
with
这个用来控制变量范围。回想一下,.
是对 当前作用域 的引用。因此 .Values
就是告诉模板在当前作用域查找Values
对象。而with
就是用来改变.
的查找作用域
{{ with PIPELINE }}
# restricted scope
{{ end }}
作用域可以被改变。with
允许你为特定对象设定当前作用域(.
)。比如,我们已经在使用.Values.favorite
。 修改配置映射中的.
的作用域指向.Values.favorite
:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
注意,在限定的作用域内,无法使用
.
访问父作用域的对象。
例如:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }} # 会报错,找不到
{{- end }}
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
release: {{ .Release.Name }} # 拿到 {{ end }} 之外可以正常执行
range
类似for
循环的机制。Helm的模板语言中,在一个集合中迭代的方式是使用range
操作符。
range
可被用于列表和元组、用于迭代有键值对的集合(像map
或dict
)
range 如果给定两个变量接收 列表或元组,那么第一个变量为索引(0开始),第二个为值。
range 如果给定两个变量接收 map或字典,那么第一个变量为key,第二个为值。
# values.yaml
favorite:
drink: coffee
food: pizza
pizzaToppings: # 列表(模板中称为切片)
- mushrooms
- cheese
- peppers
- onions
# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
toppings: |-
{{- range .Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}
# range方法“涵盖”(迭代)pizzaToppings列表。
# 就像with设置了.的作用域,range操作符也做了同样的事。
# 每一次循环,.都会设置为当前的作用域。 也就是说,第一次.设置成了mushrooms,第二次迭代设置成了cheese,等等。
helm install mycm mychart/ --debug --dry-run
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: edgy-dragonfly-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
toppings: |-
- "Mushrooms"
- "Cheese"
- "Peppers"
- "Onions"
变量
在helm3 中,变量一般我们不会直接使用,通常搭配 with
和 range
一起使用。
变量的定义格式:$name := value
, :=
为赋值运算符。
作用域
变量一般不是"全局的"。作用域是其声明所在的块。
with
可以改变他的作用域,下面我们看个例子
data:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }}
{{- end }}
在这个例子中,执行会失败,因为with将其作用域改到了
.Values.favorite
那么在去调用
.Release.Name
就变成了.Values.favorite.Release.Name
从而未发现而失败
考虑到作用域的问题,我们可以若下操作:重定义一个变量用来存放.Release.Name
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- $relname := .Release.Name -}}
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $relname }}
{{- end }}
在模板的顶层赋值了
$relname
。变量的作用域会是整个模板。
变量在range
循环中特别有用。可以用于类似列表的对象,以获取索引和值:
pople:
- name
- sex
- num
data:
pople: |-
{{- range $index, $value := .Values.pople }}
# 整型索引(从0开始)赋值给$index并将值赋值给$value
{{ $index }}: {{ $value }}
{{- end }}
# 此时我们可以获取到类似于带索引的列表
data:
pople: |-
0: name
1: sex
2: num
对于数据结构有key和value,可以使用range
获取key和value。
# values.yaml
favorite:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
# 此时我们可以连带着获取key和value
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
上with的例子中说在模板的顶层赋值了
$relname
。变量的作用域会是整个模板。而本次例子
$key
和$value
作用域会在{{ range... }}{{ end }}
块内。
这里有一个特殊的变量 $
这个变量一直是指向根的上下文。当在一个范围内循环同时要用一些根的一些变量很有用
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $.Release.Name }}
chart: {{ $.Chart.Name }}
{{- end }}
命名模板和引用
命名模板 (有时称作一个 部分 或一个 子模板)仅仅是在文件内部定义的模板,并使用了一个名字。有两种创建方式和几种不同的使用方法。
局部命名(每个模板文件中)
__helpers.tpl (类似于全局,__helpers.tpl是默认名称)
在
templates/
目录中,任何以下划线(_
)开始的文件(惯例使用__helpers.tpl
)
不管何种方式定义的子模板,模板名称都是全局的,名称相同时最后一个加载的覆盖之前的
命名模板通常用于多行需要被重复使用或被变量化的内容。
声明和管理模板的方法:
-
define
- 声明命名模块块
-
template
引用子模板,接受1或2个参数(命名模板名称和变量的获取范围)
无法被 其他函数修饰,(常见于缩进修饰)
block
-
include
(推荐)引用子模板,接受2个参数(命名模板名称和变量的获取范围)
可以被 其他函数修饰 (常见于缩进修饰)
定义和引用
用define
来声明模板
{{/* Generate basic labels */}} # 按照惯例我们应该使用注释说明作用
{{- define "MY.NAME" -}}
# body of template here
{{- end -}}
用template
和include
(推荐)来引用
{{ template "MY.NAME" }}
{{ include "MY.NAME" }}
template
示例
# 在主模板中
# templates/configmap.yaml
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-cm
{{ template "mychart.labels" }}
data:
myvalue: "Hello World"
注意:当一个(使用
define
创建的)命名模板被渲染时,会接收被template
或include
调用传入的内容。 若引用了对象,默认情况是无法用.
访问任何内容。所以引用对象时必须传递一个范围给模板
template
注意示例
# templates/configmap.yaml
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
chart: {{ .Chart.Name }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-cm
{{ template "mychart.labels" . }} # 将'.'根位置传递进去,这时渲染时就可以调用.Chart.Name了,否则报错
data:
myvalue: "Hello World"
为什么有了template
后还要增加一个include
方法?我们看个案例
{{- define "mychart.labels" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{ template "mychart.labels" . }}
data:
myvalue: "Hello World"
{{ template "mychart.labels" . }}
当我们尝试渲染上述模板时直接报错了
$ helm install mycm mychart/ --dry-run
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [ValidationError(ConfigMap): unknown field "app_name" in io.k8s.api.core.v1.ConfigMap, ValidationError(ConfigMap): unknown field "app_version" in io.k8s.api.core.v1.ConfigMap]
# 我们加个 --disable-openapi-validation 看看我们渲染了什么
$ helm install mycm mychart/ --dry-run --disable-openapi-validation
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mycm-configmap
labels:
app_name: mychart
app_version: "0.1.0" # 错乱的位置
data:
myvalue: "Hello World"
app_name: mychart # 错乱的位置
app_version: "0.1.0" # 错乱的位置
很显然这参数的摆放位置并不是我们想要的,这是因为被替换的模板中文本是左对齐的。由于template
是一个行为,不是方法,无法将 template
调用的输出传给其他方法,数据只是简单地按行插入。
这时引入了include , 它是一个方法可以被函数修饰,所以在helm中使用
include
被认为是更好的方式,可以更好地处理YAML文档的输出格式这时调整我们的模板,通过indent 函数修饰为正确的样子
{{- define "mychart.labels" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{ include "mychart.labels" . | indent 4 }}
data:
myvalue: "Hello World"
{{ include "mychart.labels" . | indent 2 }}
$ helm install mycm mychart/ --dry-run
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mycm-configmap
labels:
app_name: mychart
app_version: "0.1.0"
data:
myvalue: "Hello World"
app_name: mychart
app_version: "0.1.0"
在模板中获取文件内容
有时想导入的不是模板的文件,而是无需模板渲染的文件内容时使用此方式。
Helm 提供了通过.Files
对象访问文件的方法。不过,在我们使用模板示例之前,有些事情需要注意:
可以添加额外的文件到chart中。但是要小心,由于Kubernetes对象的限制,Chart必须小于1M。
-
通常处于安全考虑,一些文件无法通过
.Files
对象访问:无法访问
templates/
中的文件无法访问使用
.helmignore
排除的文件之Chart无法使用父Chart的文件
.Files
使用方法
.Files.Get
:获取文件内容-
.Files.Glob
:获取文件名和内容(支持正则指定多个文件)-
.Files.Glob.AsConfig
:获取后文件内容以configmap的形式展示 -
.Files.Glob.AsSecret
:获取后文件内容以secret的形式展示
-
.Files.Lines
:获取文件的每一行
路径方法
-
base
- 返回路径的最后一个元素(即文件名称)
-
dir
- 返回路径中的目录
-
ext
- 返回文件扩展名(即最后一个
.
和其后面的内容)
- 返回文件扩展名(即最后一个
-
isAbs
- 布尔值,是否为绝对路径
-
clean
- 清除路径中间部分的
..
和前一个路径,如/foo/bar/../hello.txt
,清除后为/foo/hello.txt
- 清除路径中间部分的
使用示例
读取三个文件使用.Files.Get
方法获取内容
# mychart/hello.txt
hello world
# mychart/nginx.conf
nginx conf ....
# mychart/conf.toml
message = hello from conf.toml
# mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- range tuple "hello.txt" "nginx.conf" "conf.toml" }}
{{ . }}: |-
{{ .Files.Get . }}
{{- end }}
token: |-
{{ .Files.Get "conf.toml" | b64enc }}
# 调试渲染模板
$ helm template --debug mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
hello.txt: |-
hello world
nginx.conf: |-
nginx conf ....
conf.toml: |-
message = hello from conf.toml
token: |-
bWVzc2FnZSA9IGhlbGxvIGZyb20gY29uZi50b21sCg==
使用.Files.Glob
获取多个Files
对象
这意味着
.Files.Glob
返回的对象还可以继续调用Files对象的方法
示例1
# mychart/hello.txt
hello world
# mychart/nginx.conf
nginx conf ....
# mychart/mysql.conf
mysql conf
# mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- range $path, $_ := .Files.Glob "**.conf" }}
path: {{ $path }}
{{ $path }}: {{ $.Files.Get $path }}
{{- end }}
# 调试渲染模板
helm template --debug mychart
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
path: mysql.conf
mysql.conf: mysql conf
path: nginx.conf
nginx.conf: nginx conf ....
示例2
# mychart/hello.txt
hello world
# mychart/nginx.conf
nginx conf ....
# mychart/mysql.conf
mysql conf
# mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{ (.Files.Glob "**.conf").AsConfig | indent 2 }}
{{ (.Files.Glob "**.conf").AsSecrets | indent 2 }}
# 调试渲染模板
$ helm template --debug mychart
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
mysql.conf: |
mysql conf
nginx.conf: |
nginx conf ....
mysql.conf: bXlzcWwgY29uZgo=
nginx.conf: bmdpbnggY29uZiAuLi4uCg==
使用.Files.Lines
配合range
遍历文件每一行
# mychart/lines.txt
the line1
the line2
the line3
the line4
# mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
{{- range $index, $line := .Files.Lines "lines.txt" }}
{{- if $line }}
{{ $index }}: {{ $line | quote }}
{{- end }}
{{- end }}
lines.txt: {{ range .Files.Lines "lines.txt" }}
{{ . }}{{ end }}
# 调试渲染模板
$ helm template --debug mychart
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
0: "the line1"
1: "the line2"
2: "the line3"
3: "the line4"
lines.txt:
the line1
the line2
the line3
the line4
路径示例
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
base: {{ "/root/wzp.txt" | base }}
dir: {{ "/root/wzp.txt" | dir }}
dir: {{ "mychart/template/wzp.txt" | dir }}
clean: {{ "/root/wzp.txt" | clean }}
clean: {{ "/opt/test/../mq/wzp.txt" | clean }}
clean: {{ "opt/test/../mq/wzp.txt" | clean }}
ext: {{ "/root/wzp.txt" | ext }}
isAbs: {{ "/root/wzp.txt" | isAbs }}
# 调试渲染模板
$ helm template --debug mychart
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: RELEASE-NAME-configmap
data:
base: wzp.txt
dir: /root
dir: mychart/template
clean: /root/wzp.txt
clean: /opt/mq/wzp.txt
clean: opt/mq/wzp.txt
ext: .txt
isAbs: true
模板调试技巧
helm lint
用于验证chart是否遵循最佳实践的首选工具。helm template --debug
在本地测试渲染chart模板。helm install --dry-run --debug
:服务器渲染模板,并返回生成的清单文件。helm get manifest
: 查看安装在服务器上的模板。
另外一个可能在我们渲染模板由于格式错误等问题,无法返回我们yaml清单,这时可以通过--dry-run --disable-openapi-validation
来显示最终的效果方便我们排查错误
helm install xxx -f mychart/ --dry-run --disable-openapi-validation
Chart开发小技巧
1. 模板渲染required
在正常情况下,模板渲染所需的特定值。如果这个值是空的,模板渲染会出错并打印用户提交的错误信息。
这时如果可以提前预见有些情况某些值就是可能由于使用上的不小心会存在空值,可以通过required
来声明,且在为空时打印自定义提示
例如
# 正常情况下,当.Values.who不存在时,渲染模板会直接报错
value: {{ .Values.who }}
# 当.Values.who不存在时会打印错误信息
value: {{ required "A valid .Values.who entry required!" .Values.who }}
2. 字符串引号括起来,但整型不用
由于chart不会提前声明变量类型,类似于python自动识别,所以如果确认是字符串应当使用引号括起来保证,在得到数字时也是字符串类型而不是整形
# 使用字符串数据时,将字符串括起来而不是露在外面
name: {{ .Values.MyName | quote }}
# 使用整型时 不要把值括起来。在很多场景中那样会导致Kubernetes内解析失败。
port: {{ .Values.Port }}
# 当然使用整形时,不要括起来不适用于环境变量为整形时,即使表现为整型
env:
- name: HOST
value: "http://host"
- name: PORT
value: "1234"
3. image_pull_secret
在拉取私有仓库的镜像时,通过要挂载一个secret用于存放镜像仓库地址、用户名和密码,但创建时需要用base64
跑一会影响速度,所以我们可以通过模板用于承载秘钥
# values.yaml
registry: quay.io
username: someone
password: someone
email: wzp@163.com
# 声明命名模板
{{- define "imagePullSecret" }}
{{- with .Values.imageCredentials }}
{{- printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":\"%s\",\"email\":\"%s\",\"auth\":\"%s\"}}}" .registry .username .password .email (printf "%s:%s" .username .password | b64enc) | b64enc }}
{{- end }}
{{- end }}
# templates/registry-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ template "imagePullSecret" . }}
- 滚动部署
由于配置configmap
或secret
作为配置文件注入容器以及其他外部依赖更新导致经常需要滚动部署pod。
但可能存在helm upgrade
更新chart时,由于delpyment
本身没有更改并使用原有配置保持运行,导致部署不一致。
此时可以通过sha256sum
方法保证在另一个文件发生更改时deployment
也跟着变更
kind: Deployment
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]
# 另外一种纯随机的方式
kind: Deployment
spec:
template:
metadata:
annotations:
rollme: {{ randAlphaNum 5 | quote }}
[...]
- 避免全部卸载
有时在执行helm uninstall
时有些资源不应该被卸载。Chart的开发者可以在资源中添加额外的说明避免被卸载。
kind: Secret
metadata:
annotations:
"helm.sh/resource-policy": keep
[...]
"helm.sh/resource-policy": keep 允许指示Helm操作(比如
helm uninstall
,helm upgrade
或helm rollback
)要删除时跳过删除这个资源,然而,这个资源会变成孤立的。Helm不再以任何方式管理它。如果在已经卸载的但保留资源的版本上使用
helm install --replace
会出问题。
Chart 钩子
Helm 提供了一个 hook 机制允许chart开发者在发布生命周期的某些点进行干预。比如你可以使用hook用于:
安装时在加载其他chart之前加载配置映射或密钥
安装新chart之前执行备份数据库的任务,然后在升级之后执行第二个任务用于存储数据。
在删除发布之前执行一个任务以便在删除服务之前退出滚动。
钩子的工作方式与常规模板类似,但因为Helm对其不同的使用方式,会有一些特殊的注释。
大部分函数
逻辑比较函数
大部分都返回一个布尔值
函数名 | 说明 |
---|---|
eq | 用于判断两个参数是否相等 |
ne | 用于判断两个参数是否不相等 |
lt | 用于判断第一个参数是否小于第二个参数 |
le | 用于判断第一个参数是否小于等于第二个参数 |
gt | 用于判断第一个参数是否大于第二个参数 |
ge | 用于判断第一个参数是否大于等于第二个参数 |
and | 返回两个参数的逻辑与结果(布尔值) |
or | 返回两个参数的逻辑或结果(布尔值) |
not | 用于对参数的布尔值取反 |
default | 用于设置默认值,参数为空时使用默认值 |
empty | 用于判断给定的参数是否为空 |
coalesce | 用于扫描一个给定的列表,并返回第一个非空数值 |
ternary | 接收两个参数和一个test,如果test为true返回第一个参数,为false返回第二个参数 |
字符串函数
函数名 | 说明 | |
---|---|---|
将所有参数格式化输出 | ||
println | 将所有参数格式化输出,并且每个字符串后增加一个空格,结尾增加一个换行符 | |
printf | 将所有参数格式化输出,并且支持占位符 {{ printf "the float is %.2f" 3.1415 }} | |
trim | 去除字符串两边的空格 | |
trimAll | 用于移除字符串中指定字符 | |
trimPrefix | 用于移除字符串指定的前缀 | |
trimSuffix | 用于移除字符串指定的后缀 | |
lower | 字符串字母全部大写 | |
upper | 字符串字母全部小写 | |
title | 字符串首字母转换成大写 | |
untitle | 字符串首字母转换成小写 | |
snakecase | 用于将驼峰写法转换成下划线命名法 | |
camelcase | 用于将下换线名称法转成驼峰命名 | |
kebabcase | 用于将驼峰写法转换成纵横杠写法 | |
swapcase | 用于切换字符串大小写 a)大写字母变小写 b)首字母变小写 c)空格后或开头的小写字母转换成大写 d)其他小写字母换成大写 | |
substr | 用于切割字符串(指定切割起、始位置),并返回切割后的字符串 | |
trunc | 用于截断字符串,用正整数或负整数,表示从左向右或从右向左截取字符的个数 | |
abbrev | 切割字符串,保留指定长度,结尾用...,省略号占三个长度 | |
randAlpha | 使用a-zA-Z,生成随机字符串 | |
randAlphaNum | 使用0-9a-zA-Z,生成随机字符串 | |
randNumeric | 使用0-9,生成随机字符串 | |
randAscii | 使用所有ASCII字符,生成随机字符串 | |
contains | 用于测试一个字符串是否包含在另一个中 | |
hasPrefix | 用于测试一个字符串是否是指定的前缀 | |
hasSuffix | 用于测试一个字符串是否是指定的后缀 | |
repeat | 用于将指定字符串重复n次 | |
nospace | 去掉字符串的所有空格 | |
initials | 截取指定字符串的每个单词的首字母,并拼接成新的字符串 | |
wrapWith | 用于在文档字符串中指定的列添加内容 | |
quote | 给字符串增加双引号 | |
squote | 给字符串增加单引号 | |
cat | 合并多个字符串,并用空格分隔 | |
replace | 执行字符串替换,三个参数:待替换的字符,替换后的字符串,源字符串 | |
shuffle | 用于对字符串重新排序, | |
indent | 用于指定字符串缩进指定长度,以上一行为标准开始缩进 | |
nindent | 用于指定字符串缩进指定长度,以行首为标准开始缩进,且会在上方添加一个新行 | |
plural | 判断字符串长度,根据值不同,返回不同的字符串 {{ len "a" | plural "one" "many" }} a) 长度为1,返回第一个字符串 b)长度不为1,返回第二个字符串 |
类型转换和正则函数
函数名 | 说明 |
---|---|
kindOf | 判断字符串类型 |
atoi | 将字符串转换为整型 |
float64 | 转换为float64类型 |
int | 转换为整型 |
int64 | 转换为int64类型 |
toString | 转换成字符串 |
toDecimal | 将 unix 八进制转成 int64 |
toStrings | 将列表、切片、数组转换成字符串列表 |
toJson | 将列表、切片、数组、字典或对象转换成Json |
toPrettyJson | 将列表、切片、数组、字典或对象转换成格式化Json |
toRawJson | 将列表、切片、数组、字典或对象转换成格式化Json(不转义HTML字符) |
regexFind | 根据正则匹配查找匹配的内容,并返回第一个匹配的结果 |
regexFindAll | 用于获取字符串中匹配正则表达式的所有子字符串,并在该函数的最后指定一个整数来表示返回多少个正则匹配的字符串 |
regexMatch | 用指定正则,匹配字符串,匹配正常返回true 匹配邮箱:{{ regexMatch "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,}$" "test@xxx.com" }} |
regexReplaceAll | 用指定的字符串替换正则匹配到的字符 |
regexReplaceAllLiteral | 将通过正则匹配的内容,替换成其他内容 |
regexSplit | 指定一个分隔符,将正则匹配到的内容替换为分隔符,切割字符串,并返回指定的分片,-1表示所有 |
各个regex开头的都有must版 | 例如:mustRegexMatch、mustRegexFindAll 区别在于有表达式错误时,不带must的直接报错,must版会想模板引擎返回错误 |
加密函数和编解码函数
函数名 | 说明 |
---|---|
sha1sum | 用于计算字符串的SHA1值进行加密 |
sha256sum | 用于计算字符串的SHA256值进行加密 |
adler32sum | 用于计算字符串的Adler-32校验和进行加密 |
htpasswd | 根据传入的username和paasword生成一个密码的bcrypt哈希值,可以用于HTTP Server的基础认证 |
encryptAES | 加密函数,使用一个AES-256 CBC加密文本并返回一个base64编码字符串 |
decryptAES | 解密函数,接收一个AES-256 CBC编码的字符串返回解密文本 |
b64enc | 将字符串以base64进行编码 |
b32enc | 将字符串以base32进行编码 |
日期函数
now | 返回当前日期和时间 | |
---|---|---|
date | 将日期信息格式化,必须使用'2006-01-02'或'2006/01/02'来表明,否则出错 {{ now | date "02/01/2006" }} |
dateInZone | 与date基本一致,增加了指定时区 {{ dateInZone "2006-01-02" (now) "UTC" }} | |
duration | 将给定的秒数转换为好阅读类型,如95 -> 1m35s | |
durationRound | 用于取整保留最大的时间单位,2h10m5s -> 2h | |
unixEpoch | 返回时间戳,now | unixEpoch |
dateModify | 将给定时间加减后返回新的时间 now | dateModify "-2h" |
toDate | 将给定字符串转换成时间格式,toDate "2006-01-02" "2023-01-01" |
字典函数
dict | 创建一个字典,key必须是字符串 {{- $myDict := "k1" "v1" "k2" "v2" }} |
---|---|
get | 通过key获取字典的值 |
set | 向已有字典新加键值对,也可以修改原来的 |
unset | 删除字典中指定的key |
keys | 获取一个或多个字典中的所有key,并返回一个列表 |
haskey | 判断字典中是否存在指定key |
pluck | 根据一个key在多个字典中获取所有匹配的value,返回value组成的列表 |
merge | 合并一个或多个字典,key相同时以第一个为准 |
mergeOverwrite | 合并一个或多个字典,key相同时以最后一个为准 |
values | 用于获取一个字典的所有值返回一个列表 |
pick | 根据字典指定的key获取value,返回一个新字典 |
omit | 与pick相反,忽略指定key,获取未指定的key和value,返回新字典 |
deepCopy | 深度拷贝一个字典 |
列表函数
list | 生成一个列表 {{ $myList := list 1 2 3 "onw" "two" }} |
---|---|
first | 获取列表的第一个值 |
rest | 获取除第一个值以外的所有值 |
last | 获取列表的最后一个值 |
initial | 获取列表中除最后一个以外的所有值 |
append | 在已有列表中追加一项(最后) |
prepend | 在列表的最前面加入一个新值 |
concat | 将任意数量的列表合并一个新的列表 |
reverse | 返回一个反转后的列表 |
uniq | 去除列表的重复项 |
without | 用于过滤掉某个值(即不要某个值) |
has | 判断一个值是否包含在列表中 |
compact | 删除一个列表中的空值 |
slice | 用于对列表切片, slice [n] [m] |
until | 用于构建一个指定整数范围内的列表 |
untilStep | 与Until类似,可以指定步长,untilStep [n] [m] [步长] |
seq | 生成指定范围内的整数列表 |