作者ZHDYA,曾运营 “云原生个锤子” 达3000+人,专注免费分享一些DEVOPS/运维/自动化/K8S生态方面的实战技巧,我们一起前行学习! 最近更新主要围绕:Kubernetes、持久化存储、Helm、CICD、Ingress-nginx、监控告警、应用可观察性等相关文章。
一、初识Helm
1.1、Helm 架构
- Helm -- Helm 是一个命令行下的客户端工具。主要用于 Kubernetes 应用程序 Chart 的创建、打包、发布以及创建和管理本地和远程的 Chart 仓库。
- Chart -- Chart 代表着 Helm 包。一系列用于描述 k8s 资源相关文件的集合。
- Release -- Release 是运行在 Kubernetes 集群中的 chart 的实例。一个 chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 release。
1.2、Helm 安装
下载地址:https://github.com/helm/helm/releases
# 下载包
$ wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz
# 解压压缩包
$ tar -xf helm-v3.9.4-linux-amd64.tar.gz
# 放到可执行目录
$ mv linux-amd64/helm /usr/local/bin/helm
# 验证
$ helm version
1.3、helm开发入门
$ helm create 创建Chart示例
$ helm install 部署
$ helm upgrade 更新
$ helm rollback 回滚
$ helm uninstall 卸载
$ helm create myapp # 创建一个示例chart
myapp/
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
如果你查看一下mychart/templates/
目录,你会注意到那里已经有一些文件了。
- charts:目录里存放这个chart依赖的所有子chart。
- Chart.yaml:用于描述这个 Chart的基本信息,包括名字、描述信息以及版本等。#只用于描述。
- values.yaml :用于存储 templates 目录中模板文件中用到变量的值。
- templates: 目录里面存放所有yaml模板文件。#deployment.yaml service.yaml ingress.yaml等
- NOTES.txt :用于介绍Chart帮助信息, helm install 部署后展示给用户。例如:如何使用这个 Chart、列出缺省的设置等。
- _helpers.tpl:放置模板的地方,可以在整个chart中重复使用。
1.4、手动自己的chart应用
- 1、创建目录;
- 2、创建helm所需的文件;
- 3、写入数据;
- 4、--dry-run检查验证;
- 5、部署;
安装chart:
通过如下命令,将把chart通过自定义的模板进行渲染。但不会安装chart,它将返回渲染后的模板给你:
$ helm install myfristhelm ./mychart --dry-run
二、Helm 基础语法
2.1、内置对象,下面是常用的:
内置 | 作用 |
---|---|
Release.Name | release 名称 |
Release.Time | release 的时间 |
Release.Namespace | release 的命名空间 |
Release.Service | release 服务的名称 |
Release.Revision | release 的修订版本号,从1开始累加 |
2.2、常用的内置函数
1、quote and squote
该函数将值转 换成字符串 用 双引号(quote
) 或者 单引号(squote
) 括起来。示例如下:
./values.yaml
name: soulchild
favorite:
drink: coffee
food: pizza
./configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | quote }}
food: {{ .Values.favorite.food | upper | squote }}
2、default
这个函数允许你在模板中指定一个默认值,以防这个值被忽略。
# 如果.Values.favorite.drink是非空值,则使用它,否则会返回tea。
drink: {{ .Values.favorite.drink | default "tea" | quote }}
3、indent
和nindent
indent
/ nindent
都是缩进字符串,主要区别在于nindent会在缩进前多添加一个换行符
{{ .Values.resources.memory.limits | indent 4 }}
上述结果会在当前位置开始缩进4个空格。
{{ .Values.resources.memory.limits | nindent 4 }}
上述结果会在换行后的开头位置开始缩进4个空格。
4、date
date函数格式化日期,日期格式化为YEAR-MONTH-DAY:
now | date "2006-01-02"
5、lower
将整个字符串转换成小写:
lower "HELLO"
结果为: hello
6、upper
将整个字符串转换成大写:
upper "hello"
结果为: HELLO
7、title
首字母转换成大写:
title "hello world"
结果为: Hello World
8、toYaml
引用一块YAML内容
在values.yaml里写结构化数据,引用内容块
在values增加resources资源配额。
./values.yaml
name: soulchild
favorite:
drink: coffee
food: pizza
resources:
limits:
cpu: 200m
memory: 500m
requests:
cpu: 200m
memory: 500m
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | quote }}
food: {{ .Values.favorite.food | upper | squote }}
resources: {{ toYaml .Values.resouces | nindent 4 }}
2.3、条件语句
运算符:
eq: 等于(equal to)
ne: 不等于(not equal to)
lt: 小于(less than)
le: 小于等于(less than or equal to)
gt: 大于(greater than)
ge: 大于等于(greater than or equal to)
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)
【示例】:要求.Values.favorite.drink的值等于coffee,则输出mug: true
./values.yaml
name: soulchild
favorite:
drink: coffee
food: pizza
./configmap.yaml
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 and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}mug: true{{ end }}
注意:空白行
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}}
当模板引擎运行时,它会删除{{
和}}
中的内容,但保留其余空白。通过新增{- if ...}} 的方式消除此空行,修改后:
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: "PIZZA"mug:true
,因为它消除了两边的换行。
2.4、变更作用域 with
这个用来 控制变量范围。
with 的语法与 if 语句类似:
{{ with PIPELINE }}
# restricted scope
{{ end }}
作用域可以被改变。with允许你为特定对象设定当前作用域(.
)。比如,我们已经在使用.Values.favorite。 修改配置映射中的.的作用域指向.Values.favorite:
./values.yaml
name: soulchild
favorite:
drink: coffee
food: pizza
./configmap.yaml
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 }} //release不在作用域内
{{- end }}
这样会报错因为Release.Name
不在.
限定的作用域内。但是如果对调最后两行就是正常的, 因为在{{ end }}之后作用域被重置了。
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
release: {{ .Release.Name }} //脱离作用域,可以获取到Release.Name
或者,我们可以使用$
从父作用域中访问Release.Name对象。当模板开始执行后$
会被映射到根作用域,且执行过程中不会更改。
下面这种方式也可以正常工作:
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $.Release.Name }} //$类似全局参数
{{- end }}
2.5、rang循环语句
在一个集合中迭代的方式是使用range
操作符。
./values.yaml
favorite:
drink: coffee
food: pizza
pizzaTypes:
- mushrooms
- cheese
- peppers
- onions
现在我们有了一个pizzaTypes列表(模板中称为切片)。修改模板把这个列表打印到配置映射中:
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 }}
toppings: |-
{{- range .Values.pizzaTypes }}
- {{ . | title | quote }}
{{- end }}
该range函数将遍历pizzaTypes列表。每次通过循环,.的值都会发生改变,即 第一次.为mushrooms。将第二个迭代为cheese,依此类推。第二步继续使用后续title及quote函数:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: drinking-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
toppings: |-
- "Mushrooms"
- "Cheese"
- "Peppers"
- "Onions"
**注意:**YAML中的|-
标记意为采用多行字符串。这对于在清单中嵌入大数据块是一种有用的技术,如例子中所示。
有时,在模板中快速创建一个列表,然后遍历该列表是很有用的。Helm模板有一个名为list
的函数。
sizes: |-
{{- range list "small" "medium" "large" }}
- {{ . }}
{{- end }}
结果:
sizes: |-
- small
- medium
- large
2.6、命名模板
命名模板类似于开发语言中的函数,指一段可以直接被另一段程序或代码引用的程序或代码。
在编写chart时,可以将一些重复使用的内容写在命名模板文件中供公共使用,这样可减少重 复编写程序段和简化代码结构。
命名模块使用define定义,template(不支持管道)或 include 引入,在templates目录中默 认下划线开头的文件为公共模板(_helpers.tpl)。
# cat templates/_helpers.tpl
{{- define "fullname" -}}
{{- .Chart.Name -}}-{{ .Release.Name }}
{{- end -}}
# cat templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: {{ .Release.Name |indent 6}}
app: {{ .Release.Name |nindent 6}}
name: {{ include "fullname" . }}
1、用define和template声明和使用模板
define操作允许我们在模板文件中创建一个命名模板,语法格式 如下:
{{- define "MY.NAME" }}
# body of template here
{{- end }}
比如我们可以定义一个模板用来封装控制器的标签:
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
现在我们将模板嵌入到了已有的配置映射中,然后使用template
包含进来:
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }} //用于将日期格式化为 HTML 可接受的格式。
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
当模板引擎读取该文件时,它会存储mychart.labels的引用直到template "mychart.labels"被调用。 然后会按行渲染模板,因此结果类似这样:
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myfirsthelm-configmap
labels:
generator: helm
date: 2023-03-25
data:
myvalue: "Hello World"
drink: "coffee"
food: "pizza"
注意:define不会有输出,除非像本示例一样用模板调用它。
按照惯例,Helm chart将这些模板放置在局部文件中,一般是_helpers.tpl
。
{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
2、include 方法
以下是 include 指令的语法:
{{- include "path/to/template" . }}
其中,第一个参数是要包含的子模板的路径(可以是相对路径或绝对路径)。第二个参数是当前上下文中可用的数据。
注意,当引用相对路径时,Helm 会在指定路径中寻找一个名为 _helpers.tpl
的文件,并将其视为项的一部分,以便在包含的模板中使用。
以下是一个具体的示例,其中 {{- include "configmap.tpl" . }}
指令包含了 templates/configmap.tpl
文件中的模板:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Values.configMapName }}
data:
{{- include "configmap.tpl" . }}
include
相较于使用template,在helm中使用include被认为是更好的方式 只是为了更好地处理YAML文档的输出格式。
2.7、NOTES.txt文件
在helm install 或 helm upgrade命令的最后,Helm会打印出对用户有用的信息。 使用模板可以高度自定义这部分信息。
要在chart添加安装说明,只需创建templates/NOTES.txt
文件即可。该文件是纯文本,但会像模板一样处理, 所有正常的模板函数和对象都是可用的。让我们创建一个简单的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 }}
执行 helm install myfirsthelm ./mychart
会在底部看到:
RESOURCES:
==> v1/Secret
NAME TYPE DATA AGE
myfirsthelm-secret Opaque 1 0s
==> v1/ConfigMap
NAME DATA AGE
myfirsthelm-configmap 3 0s
NOTES:
Thank you for installing mychart.
Your release is named myfirsthelm.
To learn more about the release, try:
$ helm status myfirsthelm
$ helm get all myfirsthelm
使用NOTES.txt这种方式是给用户提供关于如何使用新安装的chart细节信息的好方法。尽管并不是必需的,强烈建议创建一个NOTES.txt
文件。
三、Helm模板调试
1、helm --set
函数
通过 --set
选项,用户可以设置一些应用程序的值,这些值是可以替代values.yaml中的默认值;
例如,如果用户需要在部署 Helm Chart 时动态地设置 Redis 密码和端口号,可以使用以下命令:
$ helm install my-release bitnami/redis --set password=thisispwd
这将为 Redis Chart 安装一个新的 Release,并将密码设置为“secretpassword”
2、helm --dry-run
函数
调试模板可能很棘手,因为渲染后的模板发送给了Kubernetes API server,可能会以格式化以外的原因拒绝YAML文件。以下命令有助于调试:
helm install --dry-run
或helm template --debug
:我们已经看过这个技巧了, 这是让服务器渲染模板的好方法,然后返回生成的清单文件。helm get manifest
: 这是查看安装在服务器上的模板的好方法。
当你的YAML文件解析失败,但你想知道生成了什么,检索YAML一个简单的方式是注释掉模板中有问题的部分, 然后重新运行 helm install --dry-run
;
**++公众号内有更多技术类的合集哦++**🤙🤙🤙