作者ZHDYA,曾运营 “云原生个锤子” 达3000+人,专注免费分享一些DEVOPS/运维/自动化/K8S生态方面的实战技巧,我们一起前行学习! qrcode_for_gh_68d1bee1a9e8_258.jpg 最近更新主要围绕:Kubernetes、持久化存储、Helm、CICD、Ingress-nginx、监控告警、应用可观察性等相关文章。

一、初识Helm

1.1、Helm 架构

8jZL2ziyGqoer1U.png

  • 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、indentnindent

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-runhelm template --debug:我们已经看过这个技巧了, 这是让服务器渲染模板的好方法,然后返回生成的清单文件。
  • helm get manifest: 这是查看安装在服务器上的模板的好方法。

当你的YAML文件解析失败,但你想知道生成了什么,检索YAML一个简单的方式是注释掉模板中有问题的部分, 然后重新运行 helm install --dry-run

**++公众号内有更多技术类的合集哦++**🤙🤙🤙 qrcode_for_gh_68d1bee1a9e8_258.jpg