Loading

第23章:Helm应用包管理器

Helm应用包管理器

3.1 为什么需要Helm

K8S上的应用对象,都是由特定的资源描述组成,包括deployment、service等。都保存各自文件中或者集中写到一个配置文件。然后kubectl apply –f 部署。

yaml-all

如果应用只由一个或几个这样的服务组成,上面部署方式足够了。 而对于一个复杂的应用,会有很多类似上面的资源描述文件,例如微服务架构应用,组成应用的服务可能多达十个,几十个。如果有更新或回滚应用的需求,可能要修改和维护所涉及的大量资源文件,而这种组织和管理应用的方式就显得力不从心了。 且由于缺少对发布过的应用版本管理和控制,使Kubernetes上的应用维护和更新等面临诸多的挑战,主要面临以下问题:

(1) 如何将这些服务作为一个整体管理 (2) 这些资源文件如何高效复用 (3) 不支持应用级别的版本管理

3.2 Helm介绍

Helm是一个Kubernetes的包管理工具,就像Linux下的包管理器,如yum/apt等,可以很方便的将之前打包好的yaml文件部署到kubernetes上。Helm有3个重要概念如下: (1) helm # 一个命令行客户端工具,主要用于Kubernetes应用chart的创建、打包、发布和管理。 (2) Chart # 应用描述,一系列用于描述 k8s 资源相关文件的集合。 (3) Release # 基于Chart的部署实体,一个 chart 被 Helm 运行后将会生成对应的一个 release;将在k8s中创建出真实运行的资源对象。

3.3 Helm v3 变化

Helm目前有两个大版本:v2和v3,2019年11月,Helm团队发布 v3 版本,v3版本相比v2版本最主要变化如下。

1 架构变化

最明显的变化是 Tiller的删除,并大部分代码重构。

helm-arch

2 Release名称可以在不同命名空间重用

3 支持将 Chart 推送至 Docker 镜像仓库中

4 使用JSONSchema验证chart values

5 其他

(1) 为了更好地协调其他包管理者的措施 Helm CLI个别更名

helm delete  更名为 helm uninstall
helm inspect 更名为 helm show
helm fetch   更名为 helm pull

但以上旧的命令当前仍能使用。 (2) 移除了用于本地临时搭建 Chart Repositoryhelm serve 命令。 (3) 自动创建名称空间 在不存在的命名空间中创建发行版时,Helm 2创建了命名空间。Helm 3遵循其他Kubernetes对象的行为,如果命名空间不存在则返回错误。 (4) 不再需要requirements.yaml, 依赖关系是直接在chart.yaml中定义。

3.4 Helm客户端

1 部署Helm客户端

使用helm很简单,你只需要下载一个二进制客户端包即可,会通过kubeconfig配置(通常$HOME/.kube/config)来连接Kubernetes。 Helm客户端下载地址:https://github.com/helm/helm/releases/

# 下载后解压移动到/usr/bin/目录即可
# wget https://get.helm.sh/helm-v3.7.1-linux-amd64.tar.gz
# tar -zxf helm-v3.7.1-linux-amd64.tar.gz
# mv linux-amd64/helm /usr/bin/

2 Helm常用命令

命令 描述
completion 命令补全,source <(helm completion bash)
create 创建一个chart并指定名字
dependency 管理chart依赖
get 下载一个release。可用子命令:all、hooks、manifest、notes、values
history 获取release历史
install 安装一个chart
list 列出release
package 将chart目录打包到chart存档文件中
pull 从远程仓库中下载chart并解压到本地 # helm pull stable/mysql --untar
repo 添加,列出,移除,更新和索引chart仓库。可用子命令:add、index、list、remove、update
rollback 从之前版本回滚
search 根据关键字搜索chart。可用子命令:hub、repo
show 查看chart详细信息。可用子命令:all、chart、readme、values
status 显示已命名版本的状态
template 本地呈现模板
uninstall 卸载一个release
upgrade 更新一个release
version 查看helm客户端版本

3 配置Chart仓库

(1) 常用仓库 1)官方仓库网址: https://hub.kubeapps.com/ 2)其它仓库网址: https://artifacthub.io/

(2) 添加存储库并更新

# 添加阿里云chart存储仓库,版本比较老
# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# 更新仓库
# helm repo update

(3) 查看配置的存储库

# helm repo list

(4) 查找chart应用

helm search repo <应用名>

(5) 删除存储库

# helm repo remove <仓库名>

3.5 Helm基本使用

1 Helm管理应用生命周期

(1) 创建Chart应用示例
# helm create
(2) 部署
# helm install
(3) 更新
# helm upgrade
(4) 回滚
# helm rollback
(5) 卸载
# helm uninstall

2 使用chart部署一个应用

(1) 查找chart

# helm search repo mysql
NAME                         CHART VERSIONAPP VERSIONDESCRIPTION                                      
aliyun/mysql                 0.3.5                  Fast, reliable, scalable, and easy to use open-...
aliyun/percona               0.3.0                  free, fully compatible, enhanced, open source d...
aliyun/percona-xtradb-cluster0.0.2       5.7.19     free, fully compatible, enhanced, open source d...
aliyun/gcloud-sqlproxy       0.2.3                  Google Cloud SQL Proxy                           
aliyun/mariadb               2.1.6       10.1.31   Fast, reliable, scalable, and easy to use open-...
# 为什么mariadb也在列表中,因为他和mysql有关。

(2) 查看chart的基本信息

# helm show chart aliyun/mysql

(3) 安装chart应用

# helm install db aliyun/mysql -n default

(4) 安装后查看chart帮助信息

# helm status db -n default

3 安装前自定义chart配置选项

(1) chart 上面部署的mysql并没有成功,这是因为并不是所有的 chart 都能按照默认配置运行成功,可能会需要一些环境依赖,例如PV。 所以我们需要自定义 chart 配置选项(覆盖values.yaml),安装过程中有两种方法可以传递配置数据: --values 或 -f # 指定带有覆盖的YAML文件,这可以多次指定,最右边的文件优先。 --set # 在命令行上指定替代,如果两者都用,--set优先级高

# 把chart包下载下来查看详情
# helm pull aliyun/mysql --untar
# tree mysql/
mysql/
├── Chart.yaml
├── README.md
├── templates
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── NOTES.txt
│   ├── pvc.yaml
│   ├── secrets.yaml
│   └── svc.yaml
└── values.yaml

1 directory, 10 files

# helm show values ./mysql <=> # cat mysql/values.yaml # 查看yaml模板变量
# helm show chart ./mysql/ <=> # cat mysql/Chart.yaml # 查看chart基本信息

# 由于我这里使用的是k8s版本为1.20,Deployment yaml配置文件需要进行修改,否则无法部署
# vim mysql/templates/deployment.yaml

helm-aliyun-mysql

# cat > config.yaml << EOF
persistence:
  enabled: true
  accessMode: ReadWriteOnce
  size: 8Gi
mysqlUser: "k8s"
mysqlPassword: "123456"
mysqlDatabase: "k8s"
EOF

# kubectl create namespace mysql-db

# 创建MySQL用户k8s,并授予此用户访问新创建的k8s数据库的权限,并指定资源所在的名称空间,
# --set使用命令行替代变量,未指定的使用默认值。
# helm install db -f config.yaml -n mysql-db --set persistence.storageClass="managed-nfs-storage" ./mysql/

#  kubectl get pod -n mysql-db
NAME                        READY   STATUS    RESTARTS   AGE
db-mysql-5c695fcdfb-g6jt6   1/1     Running   0          77s

# helm list -n mysql-db
NAME    NAMESPACE    REVISION    UPDATED                                    STATUS      CHART          APP VERSION
db      mysql-db     1           2021-10-22 17:17:32.401870823 +0800 CST    deployed    mysql-0.3.5               

# kubectl get svc -n mysql-db
NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
db-mysql   ClusterIP   172.28.104.81   <none>        3306/TCP   2m38s

# yum install mysql -y
# mysql -uk8s -p123456 -h172.28.104.81 k8s
MySQL [k8s]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| k8s                |
+--------------------+
2 rows in set (0.00 sec)

mysql-aliyun-pvc-pv

# 卸载helm应用(pvc,pv,存储资源被删除)
# helm uninstall db -n mysql-db

(2) values.yaml参数格式对应set变量格式表

yaml-set

helm install命令可以从多个来源安装:chart存储库、本地chart存档(helm install mysql-0.3.5.tgz)、chart目录(helm install path/to/mysql)、完整的URL(helm install https://example.com/charts/mysql-0.3.5.tgz)

4 构建一个Helm Chart

(1) 自动生成目录

# helm create mychart
# tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 10 files

1)charts # 目录里存放这个chart依赖的所有子chart。 2)Chart.yaml # 用于描述这个Chart的基本信息,包括名字、描述信息以及版本等。 3)Templates # 目录里面存放所有yaml模板文件。 4)NOTES.txt # 用于介绍Chart帮助信息,helm install 部署后展示给用户。例如:如何使用这个 Chart、列出缺省的设置等。 5)_helpers.tpl # 放置模板助手的地方,可以在整个 chart 中重复使用。 6)values.yaml # 用于存储 templates 目录中模板文件中用到变量的值。

(2) 创建Chart后,接下来就是将其部署

# 修改service的type为NodePort,方便实验
# vim mychart/values.yaml
service:
  type: NodePort
  port: 80

# helm install web -n default mychart/

(3) 查看Release

# helm list -n default
NAMENAMESPACEREVISIONUPDATED                               STATUS CHART       APP VERSION
web default 1       2021-10-23 23:11:12.33497844 +0800 CSTdeployedmychart-0.1.01.16.0    

(4) 查看部署的pod

# kubectl get pod,svc -n default
NAME                                         READY   STATUS   RESTARTS   AGE
pod/nfs-client-provisioner-666969576d-k2pgw   1/1     Running   1         31h
pod/web-mychart-5b58c47cd6-nsk4r             1/1     Running   0         57s

NAME                 TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)       AGE
service/kubernetes   ClusterIP   172.28.0.1     <none>       443/TCP       33d
service/web-mychart   NodePort   172.28.48.236   <none>       80:31644/TCP   57s

(5) 打包推送到chart仓库共享给别人使用

# helm package mychart/
Successfully packaged chart and saved it to: /root/helm_dir/mychart-0.1.0.tgz

5 升级、回滚和删除

(1) 升级

1)使用Chart升级应用有两种方法: --values,-f # 指定yaml文件覆盖values.yaml文件中的值,没有覆盖的保持默认 --set # 在命令行上指定覆盖values.yaml文件中的值,没有覆盖的保持默认 注: 如果两种方法一起使用,--set优先级高 2)发布新版本的chart时,或者当您要更改发布的配置时,可以使用该helm upgrade 命令

# 第一种方式,将nginx服务升级到1.18版本
# vim values.yaml
image:
  tag: "1.18"
# helm upgrade -f values.yaml web mychart/ -n default

# 第二种方式,将nginx服务升级到1.19版本
# helm upgrade --set image.tag=1.19 web mychart/ -n default

(2) 回滚 如果在发布后没有达到预期的效果,则可以使用helm rollback回滚到之前的版本。 1)查看历史版本

# helm history web -n default
REVISIONUPDATED                 STATUS   CHART       APP VERSIONDESCRIPTION    
1       Sat Oct 23 23:28:45 2021supersededmychart-0.1.01.16.0     Install complete
2       Sat Oct 23 23:30:22 2021supersededmychart-0.1.01.16.0     Upgrade complete
3       Sat Oct 23 23:33:15 2021deployed mychart-0.1.01.16.0     Upgrade complete

注:
REVISION 1 表示的是nginx:1.16版本
REVISION 2 表示的是nginx:1.18版本
REVISION 3 表示的是nginx:1.19版本

2)将应用回滚到指定版本

# helm rollback web 2 -n default
Rollback was a success! Happy Helming!

注: 回滚到上一个版本使用如下命令
helm rollback web -n default

(3) 卸载chart应用

# helm uninstall web -n default
release "web" uninstalled

6 Helm工作流程

Helm-Workflow

3.6 Chart模板

1 介绍 Helm最核心的就是模板,即模板化的K8S YAML文件,它本质上就是一个Go的template模板,Helm在Go template模板的基础上,还会增加 很多东西,如一些自定义的元数据信息、扩展的库以及一些类似于编程形式的工作流,例如条件语句、管道等等,这些东西都会使得我们的 模板变得更加丰富。 通过模板实现Chart的高效复用,当部署多个应用时,可以将差异化的字段进行模板化,在部署时使用 -f 或者 --set 动态覆盖默认值,从而适配多个应用。

helm-template

# cd /root/helm_dir/mychart/
# cat > values.yaml << EOF
replicas: 1
label:
  project: ms
  app: nginx
image: nginx
imageTag: "1.20"
EOF

# rm -rf templates/*
# cat > templates/deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-deployment
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      project: {{ .Values.label.project }}
      app: {{ .Values.label.app }}
  template:
    metadata:
      labels:
        project: {{ .Values.label.project }}
        app: {{ .Values.label.app }}
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
EOF

2 内置对象

Release对象获取发布记录信息。 在上面示例中,模板文件中.Release.Values是Helm内置对象,顶级开头写。

(1) Release对象 使用 {{.Release.Name}}将 release 的名称插入到模板中,下面是一些常用的内置对象。

Release.Name release 名称
Release.Name release 的名称
Release.Time release 的时间
Release.Namespace release 的命名空间
Release.Service release 的服务名称
Release.Revision release 的修订版本号,从1开始累加

(2) Values对象

1)Values对象是为Chart模板提供值,这个对象的值有3个来源: A、chart 包中的 values.yaml 文件 B、通过 helm install 或者 helm upgrade 的 -f或者 --values参数传入的自定义的 yaml 文件 C、通过 --set 参数传入的值 2)chart 的 values.yaml 提供的值可以被用户提供的 values 文件覆盖,而该文件同样可以被 --set提供的参数所覆盖。

(3) Chart对象

可以通过Chart对象访问Chart.yaml文件的内容,例如: {{ .Chart.AppVersion }}

3 调试

Helm也提供了--dry-run --debug调试参数,帮助你验证模板正确性,在执行helm install时候带上这两个参数就可以把对应的 values值和渲染的资源清单打印出来,而不会真正的去部署一个release。

比如我们来调试上面创建的 chart 包:

# helm install --dry-run --debug web ./mychart/ -n default
install.go:178: [debug] Original chart version: ""
install.go:199: [debug] CHART PATH: /root/helm_dir/mychart

NAME: web
LAST DEPLOYED: Sun Oct 24 19:46:42 2021
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
image: nginx
imageTag: "1.20"
label:
  app: nginx
  project: ms
replicas: 1

HOOKS:
MANIFEST:
---
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      project: ms
      app: nginx
  template:
    metadata:
      labels:
        project: ms
        app: nginx
    spec:
      containers:
      - image: nginx:1.20
        name: nginx

4 函数与管道

前面讲的模块,其实就是将变量值传给模板引擎进行渲染,模板引擎还支持对拿到数据进行二次处理。 (1) 常用函数

quote         # 将值转换为字符串,即加双引号
default      # 设置默认值,如果获取的值为空则为默认值
indent、nindent# 缩进字符串
toYaml      # 引用一块YAML内容

其他函数:
upper、title等

(2) quote,将值转换为字符串,即加双引号

例如:从 .Values 中读取的值变成字符串,可以使用quote函数实现。 nodeSelector标签的值用了true正常使用会报错,这是因为它是关键字,需要加引号才可以。

# vim values.yaml
nodeSelector:
  gpu: true

# vim templates/deployment.yaml
......
nodeSelector:
  disktype: {{ quote .Values.nodeSelector.gpu }}

(3) default,设置默认值,如果获取的值为空则为默认值 示例:以防止忘记定义而导致模板文件缺少字段无法创建资源,这时可以为字段定义一个默认值。

# vim templates/deployment.yaml
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}

注:
这里用到了管道符"|",前面的值传递后函数验证是否为空。

(4) indent和nindent函数都是缩进字符串,主要区别在于nindent会在缩进前多添加一个换行符 示例:

# vim templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: {{ .Release.Name | indent 0 }}
......

(5) toYaml,引用一块YAML内容

示例:在values.yaml里写结构化数据,引用内容块

# vim values.yaml
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

# vim templates/deployment.yaml
......
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
        resources: {{ toYaml .Values.resources | nindent 10 }}

(6) 其他函数

# vim templates/deployment.yaml
......
{{ upper .Values.resources }}# 大写
{{ title .Values.resources }}# 首字母大写

5 流程控制

流程控制是为模板提供了一种能力,满足更复杂的数据逻辑处理,Helm模板语言提供的控制语句有,if/else 条件判断、range 循环、with 指定变量作用域。

(1) 流程控制之 if/else 1)if/else 块是用于在模板中有条件的包含文本块的方法

helm-if-else

条件判断,根据不同的条件做不同的行为,语法如下:
{{ if <表达式> }}
# 做某事
{{ else if <表达式> }}
# 做某事
{{ else }}
# 默认
{{ end }}

2)示例 部署一个应用,在没明确启用ingress时,默认情况下不启用。

# vim values.yaml
ingress:
  enabled: false
  annotations:
     kubernetes.io/ingress.class: nginx
     kubernetes.io/tls-acme: "true"
  hosts:
     host: www.lc.com
     path: /
  tls:
    secretName: chart-example-tls
    hosts: www.lc.com

# vim templates/ingress.yaml
{{ if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  labels: {{ include "labels" . | nindent 4 }}
  name: {{ include "fullname" . }}
  annotations: {{ toYaml .Values.ingress.annotations | nindent 4 }}
spec:
  {{- if .Values.ingress.tls }}
  tls:
  - hosts:
      - {{ .Values.ingress.tls.hosts }}
    secretName: {{ .Values.ingress.tls.secretName }} 
  {{- end }}
  rules:
  - host: {{ .Values.ingress.hosts.host }}
    http:
      paths:
      - path: {{ .Values.ingress.hosts.path }}
        pathType: Prefix
        backend:
          service:
            name: {{ include "fullname" . }} 
            port:
              number: {{ .Values.service.port }}
{{ end }}

# 测试
# helm install test --set ingress.enabled=true --dry-run ./mychart/

# 如果值为以下几种情况则为false
一个布尔类型 false
一个数字 0
一个空的字符串
一个 nil(空或 null)
一个空的集合( map、 slice、 tuple、 dict、 array)
除了上面的这些情况外,其他所有条件都为真。

# 条件表达式也支持操作符
eq 等于
ne 不等于
lt 小于
gt 大于
and 逻辑与
or 逻辑或

3)消除判断空格

示例:如果是一个空的集合则不启用资源配额

# vim values.yaml
resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

# vim templates/deployment.yaml
......
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
        {{ if .Values.resources }}
        resources: {{ toYaml .Values.resources | nindent 10 }}
        {{ end }}

验证渲染结果:
# helm install test --dry-run ./mychart/
渲染结果会发现有多余的空行,这是因为模板渲染时会将指令删除,所以原有的位置就空白了,可以使用横杠"-"消除空行。
"{{-" 表示删除左边的所有空格,直到非空格字符,"-}}" 表示删除右边的所有空格,还包括换行符、TAB字符。

示例:
# vim templates/deployment.yaml
......
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
        {{- if .Values.resources }}
        resources: {{ toYaml .Values.resources | nindent 10 }}
        {{- end }}

4)判断一个空的数组

# vim values.yaml
resources: {}

# vim templates/deployment.yaml
......
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
        {{- if .Values.resources }}
        resources:
{{ toYaml .Values.resources | indent 10 }}
        {{- end }}

(2) range

helm-range

循环: 一般用于遍历序列结构的数据,例如序列、键值。

语法:
{{ range <值> }}
# 引用内容
{{ end }}

示例: 遍历数据
在 Helm 模板语言中,使用 range 关键字来进行循环操作,我们在 values.yaml 文件中添加上一个变量列表
# cat values.yaml
env:
  - 1
  - 2
  - 3

# vim templates/deployment.yaml
......
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
        env:
          {{- range $k,$v := .Values.env }}
          - name: {{ $k }}
            value: {{ quote $v }}
          {{- end }}

# 循环打印该列表
# vim templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}
data:
  test: |
  {{- range .Values.env }}
    {{ . }}
  {{- end }}

注:
循环内部我们使用的是一个".",这是因为当前的作用域就在当前循环内,这个"."引用的是当前读取的元素。

(3) with with: 控制变量作用域 之前我们的 {{ .Release.xxx }}或者 {{ .Values.xxx }},其中的 .就是表示对当前范围的引用, .Values就是告诉模板在当 前范围中查找 Values对象的值,而 with语句就可以来控制变量的作用域范围。

语法:
{{ with <值> }}
# 限制范围
{{ end }}

with语句可以允许将当前范围 . 设置为特定的对象,比如使用.Values.nodeSelecotr,我们可以使用 with来将. 范围指向 .Values.nodeSelecotr

# vim values.yaml
nodeSelector:
  team: a
  gpu: yes

# vim templates/deployment.yaml
......
    spec:
      {{- $ReleaseName := .Release.Name -}}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        team: {{ .team }}
        gpu: {{ .gpu }}
        test: {{ $ReleaseName }}
      {{- end }}
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
......
注: 
上面增加了一个"{{- with .Values.label }} xxx {{- end }}"的一个块,这样的话就可以在当前的块里面直接引用".team"".gpu"了。"with"是一个循环构造,使用".Values.nodeSelector"中的值,将其转换为Yaml。
with 块限制了变量作用域,也就是无法直接引用模板对象,例如".Values"".Release",with 语句块内不能再使用
".Release.Name"对象,否则报错,我们可以将该对象赋值给一个变量来解决这个问题,可以在 with 语句上面增加一句
"{{- $ReleaseName := .Release.Name -}}",其中"$ReleaseName"就是后面的对象的一个引用变量,它的形式就
是"$name",赋值操作使用":=",这样 with 语句块内部的"$ReleaseName"变量仍然指向的是".Release.Name"。

# 优化一下
# vim templates/deployment.yaml
......
    spec:
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
......
注: toYaml之后的点是循环中".Values.nodeSelector"的当前值

6 变量 变量在实际应用中不多,但有时候结合withrange能更好的处理数据。

获取列表键值

# vim values.yaml
env:
  NAME: "gateway"
  JAVA_OPTS: "-Xmx1G"
  hello: world

# vim templates/deployment.yaml
......
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
        env:
          {{- range $k,$v := .Values.env }}
          - name: {{ $k }}
            value: {{ quote $v }}
          {{- end }}
注: 上面在 range 循环中使用 "$key""$value" 两个变量来接收后面列表循环的键和值。
# 输出结果
env:
  - name: JAVA_OPTS
    value: "-Xmx1G"
  - name: NAME
    value: "gateway"


# vim templates/deployment.yaml
......
      containers:
      - image: {{ .Values.image }}:{{ .Values.imageTag }}
        name: nginx
        env:
          - name: NAME
            {{- if eq .Values.env.hello "world" }}
            value: true
            {{- else }}
            value: false
            {{- end }}
# 输出结果
env:
  - name: NAME
    value: true

7 命名模板 命名模板类似于开发语言中的函数,指一段可以直接被另一段程序或代码引用的程序或代码。在编写chart时,可以将一些重复使用的内容 写在命名模板文件中供公共使用,这样可减少重复编写程序段和简化代码结构。命名模块使用define字段定义,模板中使用template字段 或include字段引入,在templates目录中默认下划线开头的文件为公共模板(_helpers.tpl)。

示例: 将资源名称生成指令放到公共模板文件中作为所有资源名称
# vim templates/_helpers.tpl
{{- define "fullname" -}}
{{- .Chart.Name }}-{{ .Release.Name }}
{{- end }}

{{- define "labels" -}}
app: {{ template "fullname" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
release: {{ .Release.Name }}
{{- end }}

# vim templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ template "fullname" . }}
  labels:
    {{- include "labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicas }}
......

# 得到的结果为
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mychart-test
  labels:
    app: mychart-test
    chart: mychart-0.1.0
    release: test
spec:
  replicas: 1
......

template指令是将一个模板包含在另一个模板中的方法,但是template函数不能用于Go模板管道,为了解决该问题,引入了 include 指令。
上面包含一个名为 labels 的模板,然后将值 "." 传递给模板,最后将该模板的输出传递给 nindent 函数。

3.7 写一个通用的Chart

1 创建步骤

(1) 先创建模板示例 helm create demo
(2) 修改Chart.yaml,Values.yaml,参考示例预留变动的字段值
(3) 在templates目录下准备部署应用所需的yaml文件,并添加指令引用Values.yaml字段
(4) 将重复使用的内容放到命名模板文件中
(5) 使用Chart结合参数部署多个同类服务

2 查看Chart的目录结构

# tree demo
demo
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── ingress.yaml
│   ├── NOTES.txt
│   └── service.yaml
└── values.yaml

1 directory, 7 files

3 Chart.yaml

# cat demo/Chart.yaml
apiVersion: v2
appVersion: 0.1.0
description: java demo
name: demo
type: application
version: 0.1.0


创建Chart时的默认配置:
# egrep -v "^$|^#" mychart/Chart.yaml
apiVersion: v2
# Chart的api版本

name: mychart
# Chart的名称

description: A Helm chart for Kubernetes
# Chart的描述信息

type: application
# Chart可以是"应用程序"或"库"Chart。
# 应用程序Chart是一组模板,可以打包到要部署的版本化归档中。
# 库为Chart开发人员提供了有用的实用程序或函数。
# 它们作为应用程序Chart的依赖项包含在其中,用于将这些实用程序和函数注入渲染管道。
# 库Chart不定义任何模板,因此无法部署。

version: 0.1.0
# 这是Chart版本。
# 每次更改Chart及其模板(包括应用程序版本)时,此版本号应增加。
# 版本应遵循语义版本控制(https://semver.org/)。

appVersion: "1.16.0"
# 这是正在部署的应用程序的版本号。
# 每次更改应用程序时,此版本号都应递增。
# 版本不应遵循语义版本控制。
# 它们应该反映应用程序正在使用的版本。
# 建议将其与引号一起使用。

4 values.yaml

# cat demo/values.yaml
image:
  pullPolicy: IfNotPresent
  repository: lizhenliang/java-demo
  tag: latest
imagePullSecrets: []
ingress:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: 100m
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
  enabled: true
  host: example.lc.com
  tls:
    secretName: example-lc-com-tls
nodeSelector: {}
replicaCount: 3
resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 100m
    memory: 128Mi
service:
  port: 80
  type: ClusterIP
tolerations: []

5 templates

(1) deployment.yaml
# cat demo/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "demo.fullname" . }}
  labels:
    {{- include "demo.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "demo.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "demo.selectorLabels" . | nindent 8 }}
    spec:
    {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
    {{- end }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}

(2) _helpers.tpl
# cat demo/templates/_helpers.tpl
{{- define "demo.fullname" -}}
{{- .Chart.Name -}}-{{ .Release.Name }}
{{- end -}}

{{/*
公用标签
*/}}
{{- define "demo.labels" -}}
app: {{ template "demo.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
{{- end -}}

{{/*
标签选择器
*/}}
{{- define "demo.selectorLabels" -}}
app: {{ template "demo.fullname" . }}
release: "{{ .Release.Name }}"
{{- end -}}

(3) ingress.yaml
# cat demo/templates/ingress.yaml
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: {{ include "demo.fullname" . }} 
  labels:
    {{- include "demo.labels" . | nindent 4 }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
{{- if .Values.ingress.tls }}
  tls:
    - hosts:
        - {{ .Values.ingress.host }} 
      secretName: {{ .Values.ingress.tls.secretName }}
{{- end }}
  rules:
    - host: {{ .Values.ingress.host }} 
      http:
        paths:
        - path: / 
          backend:
            serviceName: {{ include "demo.fullname" . }} 
            servicePort: {{ .Values.service.port }}
{{- end }}

(4) NOTES.txt
# cat demo/templates/NOTES.txt
访问地址:
{{- if .Values.ingress.enabled }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ .Values.ingress.host }}
{{- end }}
{{- if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "demo.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- end }}

(5) service.yaml
# cat demo/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ include "demo.fullname" . }}
  labels:
    {{- include "demo.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "demo.selectorLabels" . | nindent 4 }}


posted @ 2021-10-27 00:31  云起时。  阅读(1077)  评论(0编辑  收藏  举报