helm的简介

网易公开课,开课啦!

主讲内容:docker/kubernetes 云原生技术,大数据架构,分布式微服务,自动化测试、运维。

腾讯课堂:点击进入
网易课堂:点击进入

7月1号-7月29号 8折优惠!!!
7月1号-7月29号 8折优惠!!!
7月1号-7月29号 8折优惠!!!


全栈工程师开发手册 (作者:栾鹏)
架构系列文章

Helm简介

Helm之于Kubernetes好比yum之于RHEL,或者apt-get之于Ubuntu。Helm使用Chart帮助我们管理应用,Chart就好像RPM一样,里面描述了应用及其依赖关系。主要概念:

  • Chart:Helm管理的应用部署包,一个结构相对固定的目录或者tgz压缩文件,Chart之间可相互依赖
  • Release:Chart部署之后的事例,每一次helm install就会生成一个新的release

2、部署

参考:https://blog.csdn.net/luanpeng825485697/article/details/80873236

2.5、注意事项

  • 如果启用的RBAC,在 helm init 时指定sa
  • 多使用 helm -hhelm [command] -h

3、Chart

Helm管理的kubernetes资源包称之为Chart,Chart是一个结构相对固定的文件目录。一个单独的Chart可以用于部署一下简单的Kubernetes资源,如:memcached,又或者更复杂的应用,如完整的Web应用:HTTP servers, databases, caches等等。
Helm的结构:

3.1、Chart的结构

[root@k8s-master mychart]# tree mychart/
mychart                              # Chart的名字,也就是目录的名字
├── charts                           # Chart所依赖的子Chart
│   ├── jenkins-0.14.0.tgz           # 被“mychart”依赖的其中一个subChart
│   ├── mysubchart                   # 被“mychart”依赖的另一个subChart
│   │   ├── charts                   
│   │   ├── Chart.yaml
│   │   ├── templates
│   │   │   └── configmap.yaml
│   │   └── values.yaml
│   └── redis-1.1.17.tgz             
├── Chart.yaml                       # 记录关于该Chart的描述信息:比如名称、版本等等
├── config1.toml                     # 其他文件:可以是任何文件
├── config2.toml                     # 其他文件:可以是任何文件
├── requirements.yaml                # 记录该Chart的依赖,类似pom.xml
├── templates                        # 存放模版文件,模板也就是将k8s的yml文件参数化,最终还是会被helm处理成k8s的正常yml文件,然后用来部署对应的资源
│   ├── configmap.yaml               # 一个ConfigMap资源模版
│   ├── _helpers.tpl                 # "_"开头的文件不会本部署到k8s上,可以用于定于通用信息,在其他地方应用,如loables
│   └── NOTES.txt                    # 在执行helm instll安装此Chart之后会被输出到屏幕的一些自定义信息
└── values.yaml                      # 参数定义文件,这里定义的参数最终会应用到模版中

Chart可以是目录,也可以是tgz格式的压缩包。
charts目录和requirements.yaml文件到区别就和lib和pom.xml的区别类似,一个用于存放,一个用于描述

3.1.1、创建一个Chart

[root@k8s-master charts]# pwd
/root/charts

[root@k8s-master charts]# helm create mychart-2

[root@k8s-master charts]# ll
drwxr-xr-x. 4 root root  93 3月  12 10:48 mychart-2

[root@k8s-master charts]# tree mychart-2
mychart-2
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── ingress.yaml
│   ├── NOTES.txt
│   └── service.yaml
└── values.yaml

2 directories, 7 files

### 为了后面的实验,我们先删除helm默认生成的文件 ###
[root@k8s-master charts]# rm -rf mychart-2/templates/*

helm会帮我们创建一下必要的模版文件,简单的Chart直接修改这些文件即可

3.1.2、文件:Chart.yaml

[root@k8s-master charts]# vi mychart-2/Chart.yaml apiVersion: v1
appVersion: "1.0"description: A Helm chart for Kubernetes
name: mychart-2version: 0.1.0
  • apiVersion:官网文档上没有,helm create创建的的模版中有,指k8s API的版本
  • appVersion:当前Chart包含的应用的版本,比如redis、apache等
  • description:当前Chart的描述信息
  • name:当前Chart的名称
  • version:当前Chart的版本,版本规范,打包后的文件名:name-version.tgz,如:nginx-1.2.3.tgz

这里仅列举了重要的数据项,还有很多可选项:keywords、home、maintainers等,参考。这些内容在打包分享时很有用。

3.1.3、文件:values.yaml

此文件主要用来存放Chart的默认值,这些值会被应用到模版中,替换掉模版中的变量,生成最终k8s的yaml部署文件,例如:

[root@k8s-master charts]# vi mychart/values.yamlfavorite:
  drink: coffee
  food: pizza

[root@k8s-master charts]# vi mychart/templates/configmap.yaml apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ quote .Values.favorite.drink }}
  food: {{ .Values.favorite.food}}

在这个例子中,我们有一个configmap.yaml模版,模版中点了一个ConfigMap,模版中包含了很多 {{}} ,这些 {{}} 中以**.Values开头的会被替换成values.yaml中的值,.Release**开头的会被替换成release相关的变量值,Release是内置变量中的一员。quote是一个function,当然还有很多其他的function。
这里为什么说默认值呢,因为在执行 helm install 时,可以通过 helm install --set tags.front-end=true --set subchart2.enabled=false 这种方式来改变实际的值。
关于template的详细内容,将在下文阐述。

3.1.4.1、内置变量
  • Release.Name: 实例的名称(注意:不是应用的名称)
  • Release.Time: 应用实例最后被更新的时间. 就是实例对象的 Last Released 属性.
  • Release.Namespace: 应用实例的命名空间.
  • Release.Service: 进行发布的服务。通常这是Tiller .
  • Release.IsUpgrade: 如果当前对实例的操作是更新或者回滚,这个变量的值就会被置为true.
  • Release.IsInstall: 如果当前对实例的操作是安装,则这边变量被置为true.
  • Release.Revision: 版本序号,从1开始,每次使用helm upgrade命令都会增加1.
  • Chart: Chart.yaml文件中的内容,所以我们可以使用Chart.Version表示应用版本,Chart.Maintainers函数应用的维护信息.
  • Files: A map-like object containing all non-special files in the chart. This will not give you access to templates, but will give you access to additional files that are present (unless they are excluded using .helmignore). Files can be accessed using {{index .Files "file.name"}} or using the {{.Files.Get name}} or {{.Files.GetString name}} functions. You can also access the contents of the file as []byte using {{.Files.GetBytes}}
  • Capabilities:一个map对象, ({{.Capabilities.KubeVersion}},包含kubernetes的版本信息 ({{.Capabilities.TillerVersion}}, 包含tiller的版本信息,({{.Capabilities.APIVersions.Has "batch/v1")包含kubernetes api版本信息
3.1.4.2、全局变量

Helm的变量是有作用域的:父Chart可以访问子Chart的值,而子Chart不能访问父Chart的值:

[root@k8s-master charts]# vi mychart-2/values.yaml
title: "MyChart-2"

mysubchart1:
  title: mysubchart1

mysubchart2:
  title: mysubchart2

[root@k8s-master charts]# vi mychart-2/charts/mysubchart1/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  title: {{ .Values.title }}

[root@k8s-master charts]# vi mychart-2/charts/mysubchart2/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  title: {{ .Values.title }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-2/
[debug] Created tunnel using local port: '37936'

[debug] SERVER: "127.0.0.1:37936"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-2

NAME:   cautious-hare
REVISION: 1
RELEASED: Mon Mar 12 13:51:29 2018
CHART: mychart-2-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
mysubchart1:
  global: {}
  title: mysubchart1
mysubchart2:
  global: {}
  title: mysubchart2
title: MyChart-2

HOOKS:MANIFEST:

---
# Source: mychart-2/charts/mysubchart1/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cautious-hare-configmap
data:
  title: mysubchart1
---
# Source: mychart-2/charts/mysubchart2/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cautious-hare-configmap
data:
  title: mysubchart2
---
# Source: mychart-2/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cautious-hare-configmap
data:
  title: MyChart-2
  mysubchart1: mysubchart1
  mysubchart2: mysubchart2

上例中,mychart-2/charts/mysubchart1/values.yaml和mychart-2/charts/mysubchart2/values.yaml为空白文件。mysubchart1和mysubchart2可以正常访问到mychart-2/values.yaml文件中定义的对应子Chart的内容。如果直接访问其他变量,比如:{{ .Values.mysubchart2.xxx }} 将得到一个错误。

你也可以理解为父charts覆盖了子charts。

如果在父Chart和子Chart中同时定义了相同的值,则父Chart覆盖子Chart:

[root@k8s-master charts]# vi mychart-2/values.yaml
title: "MyChart-2"

mysubchart1:
  title: mysubchart1

[root@k8s-master charts]# vi mychart-2/charts/mysubchart1/values.yaml 
title: xxxxxxx

[root@k8s-master charts]# vi mychart-2/charts/mysubchart2/values.yaml
title: nnnnnnnnnn

[root@k8s-master charts]# helm install --debug --dry-run mychart-2/
[debug] Created tunnel using local port: '35979'

[debug] SERVER: "127.0.0.1:35979"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-2

NAME:   nosy-lobster
REVISION: 1
RELEASED: Mon Mar 12 13:57:58 2018
CHART: mychart-2-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
mysubchart1:
  global: {}  
  title: mysubchart1
mysubchart2:
  global: {}  
  title: nnnnnnnnnn
title: MyChart-2

HOOKS:
MANIFEST:

---
# Source: mychart-2/charts/mysubchart1/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nosy-lobster-configmap
data:
  title: mysubchart1
---
# Source: mychart-2/charts/mysubchart2/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nosy-lobster-configmap
data:
  title: nnnnnnnnnn
---
# Source: mychart-2/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nosy-lobster-configmap
data:
  title: MyChart-2  mysubchart1: mysubchart1  mysubchart2: nnnnnnnnnn

可以在父Chart中定义全局变量,子Chart可以通过 .Values.global.name 访问全局变量:

[root@k8s-master charts]# vi mychart-2/values.yaml
global:
  app: MyTestAPP

title: "MyChart-2"

mysubchart1:
  title: mysubchart1

[root@k8s-master charts]# vi mychart-2/charts/mysubchart1/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  title: {{ .Values.title }}
  appName: {{ .Values.global.app }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-2/
[debug] Created tunnel using local port: '39141'

[debug] SERVER: "127.0.0.1:39141"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-2

NAME:   ignorant-gerbil
REVISION: 1
RELEASED: Mon Mar 12 14:03:02 2018
CHART: mychart-2-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
global:
  app: MyTestAPP
mysubchart1:
  global:
    app: MyTestAPP
  title: mysubchart1
mysubchart2:
  global:
    app: MyTestAPP
  title: nnnnnnnnnn
title: MyChart-2

HOOKS:
MANIFEST:

---# Source: mychart-2/charts/mysubchart1/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ignorant-gerbil-configmap
data:
  title: mysubchart1
  appName: MyTestAPP
---# Source: mychart-2/charts/mysubchart2/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ignorant-gerbil-configmap
data:
  title: nnnnnnnnnn
---# Source: mychart-2/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: ignorant-gerbil-configmap
data:
  title: MyChart-2
  mysubchart1: mysubchart1
  mysubchart2: nnnnnnnnnn

3.1.4、文件:requirements.yaml

这个文件需要手动创建,requirements.yaml文件用来定义Chart的依赖,直接将这些依赖放到chart目录下也是可以的。使用文件来管理是来会更加便利。简单文件的格式如下:

dependencies:
  - name: redis
    version: 1.1.17
    repository: https://kubernetes-charts.storage.googleapis.com

文件内容表明当前Chart依赖redis-1.1.17这个Chart,这个Chart的仓库地址是https://kubernetes-charts.storage.googleapis.com,这个地址必须已经添加到你的helm中,添加命令:helm repo add ,查看命令:helm repo list
然后我们执行 helm dependency update chartName 命令helm就会帮我们下载requirements.yaml文件中定义的其他Chart,这些Chart会存放在charts目录中。

[root@k8s-master charts]# helm dependency update mychart-2/
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading redis from repo https://kubernetes-charts.storage.googleapis.com
Deleting outdated charts

[root@k8s-master charts]# ll mychart-2/charts/
总用量 8
-rw-r--r--. 1 root root 6189 3月  12 10:54 redis-1.1.17.tgz

命令简写:helm dep up mychart-2/

下面是一个复杂一点的例子:

dependencies:
  - name: redis
    version: 1.1.17
    repository: https://kubernetes-charts.storage.googleapis.com
    condition: redis.enabled
    tags:
      - redis
  - name: jenkins
    version: 0.14.0
    repository: https://kubernetes-charts.storage.googleapis.com
    condition: jenkins.enabled
    tags:
      - jenkins
  - name: redis
    version: 1.1.17
    repository: https://kubernetes-charts.storage.googleapis.com
    alias: newname
    tags:
      - redis

在这个例子中,涉及到alias、condition、tags三个关键字,我们配合下面的values.yaml文件一项一项来解释,values.yaml文件内容:

redis:
  enabled: false

jenkins:
  enabled: false

tags:
  redis: false
  jenkins: true
  • alias:给依赖的Chart定义别名,官网上说:别名会在下载之后,以新名称存放在charts目录下,但是实时上并非如此。在我的实验过程中,上面的例子会下载三个Chart:redis、jenkins和redis,也就是redis下载了两次,但是在charts目录下只有redis和jenkins两个文件,没有newname。我在想alias目前可能只是记录在文件中,最后一个redis依然叫redis,而且将第一个redis覆盖了,所以只有两个文件。也可能是我的实验姿势不对。
  • condition:boolean值,用来决定当前依赖是否启用,如果condition后面的值不存在,比如redis.enabled或者jenkins.enabled未定义,则忽略condition。condition可定义多个值,用 “,” 分隔。如果第一个值存在,则忽略后面的值。
  • tags:boolean值,与condition类似,但是condition的优先级更高,如果同时定义了condition和tags,tags将被忽略。在不考虑condition时,tags中的任何一个值被定义为true,则启用该依赖
[root@k8s-master charts]# helm dep up mychart-2/
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 3 charts
Downloading redis from repo https://kubernetes-charts.storage.googleapis.com
Downloading jenkins from repo https://kubernetes-charts.storage.googleapis.com
Downloading redis from repo https://kubernetes-charts.storage.googleapis.com
Deleting outdated charts

[root@k8s-master charts]# ll mychart-2/charts/
总用量 24
-rw-r--r--. 1 root root 12685 3月  12 11:18 jenkins-0.14.0.tgz
-rw-r--r--. 1 root root  6189 3月  12 11:18 redis-1.1.17.tgz
  • 首先可以看到确实下载了三个Chart,redis两次,但是在charts目录下只有两个tgz文件,alias和官网上的描述有出入。
  • 其次我们定义的condition和tags并未生效,按照上面的规则,不应该有任何一个Chart本下载,但是所有的依赖都被下载了。实时并非如此,接着往下看。
[root@k8s-master charts]# helm install --debug --dry-run mychart-2/
[debug] Created tunnel using local port: '43383'

[debug] SERVER: "127.0.0.1:43383"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-2

NAME:   reeling-puffin
REVISION: 1
RELEASED: Mon Mar 12 11:24:30 2018
CHART: mychart-2-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
jenkins:
  enabled: false
redis:
  enabled: false
tags:
  jenkins: true
  redis: false

HOOKS:
MANIFEST:

helm install 用于在k8s上部署Chart,–debug –dry-run选项用于调试,加了两个选项之后,不会在k8s上部署任何资源,仅输出helm转换之后的模版信息和相关变量

### 修改values.yaml文件 ###
[root@k8s-master charts]# vi mychart-2/values.yaml
redis:
  enabled: false

jenkins:
  enabled: false

tags:
  redis: true
  jenkins: true

### 再次执行install ###
[root@k8s-master charts]# helm install --debug --dry-run mychart-2/
[debug] Created tunnel using local port: '45794'

[debug] SERVER: "127.0.0.1:45794"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-2

NAME:   sweet-hyena
REVISION: 1
RELEASED: Mon Mar 12 11:29:49 2018
CHART: mychart-2-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
jenkins:
  enabled: false
newname:
  global: {}
  image: bitnami/redis:4.0.8-r5
  imagePullPolicy: IfNotPresent
  metrics:
    annotations:
      prometheus.io/port: "9121"
      prometheus.io/scrape: "true"
    enabled: false
    image: oliver006/redis_exporter
    imagePullPolicy: IfNotPresent
    imageTag: v0.11
    resources: {}
  networkPolicy:
    allowExternal: true
    enabled: false
  nodeSelector: {}
  persistence:
    accessMode: ReadWriteOnce
    enabled: true
    path: /bitnami
    size: 8Gi
    subPath: ""
  podAnnotations: {}
  podLabels: {}
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
  securityContext:
    enabled: true
    fsGroup: 1001
    runAsUser: 1001
  service:
    annotations: {}
    loadBalancerIP: null
  serviceType: ClusterIP
  tolerations: []
  usePassword: true
redis:
  enabled: false
tags:
  jenkins: true
  redis: true

HOOKS:
MANIFEST:

---# Source: mychart-2/charts/newname/templates/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: sweet-hyena-newname
  labels:
    app: sweet-hyena-newname
    chart: "newname-1.1.17"
    release: "sweet-hyena"
    heritage: "Tiller"
type: Opaque
data:
  redis-password: "anVXdzdJQjJRTg=="---# Source: mychart-2/charts/newname/templates/pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: sweet-hyena-newname
  labels:
    app: sweet-hyena-newname
    chart: "newname-1.1.17"
    release: "sweet-hyena"
    heritage: "Tiller"
spec:
  accessModes:
    - "ReadWriteOnce"
  resources:
    requests:
      storage: "8Gi"---# Source: mychart-2/charts/newname/templates/svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: sweet-hyena-newname
  labels:
    app: sweet-hyena-newname
    chart: "newname-1.1.17"
    release: "sweet-hyena"
    heritage: "Tiller"
  annotations:
spec:
  type: ClusterIP
  ports:
  - name: redis
    port: 6379
    targetPort: redis
  selector:
    app: sweet-hyena-newname
---# Source: mychart-2/charts/newname/templates/deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: sweet-hyena-newname
  labels:
    app: sweet-hyena-newname
    chart: "newname-1.1.17"
    release: "sweet-hyena"
    heritage: "Tiller"
spec:
  template:
    metadata:
      labels:
        app: sweet-hyena-newname
    spec:
      securityContext:
        fsGroup: 1001
        runAsUser: 1001
      containers:
      - name: sweet-hyena-newname
        image: "bitnami/redis:4.0.8-r5"
        imagePullPolicy: "IfNotPresent"
        env:
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: sweet-hyena-newname
              key: redis-password
        ports:
        - name: redis
          containerPort: 6379
        livenessProbe:
          exec:
            command:
            - redis-cli
            - ping
          initialDelaySeconds: 30
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command:
            - redis-cli
            - ping
          initialDelaySeconds: 5
          timeoutSeconds: 1
        resources:
          requests:
            cpu: 100m
            memory: 256Mi

        volumeMounts:
        - name: redis-data
          mountPath: /bitnami
          subPath: 
      volumes:
      - name: redis-data
        persistentVolumeClaim:
          claimName: sweet-hyena-newname

忽略输出内容的细节,注意看 COMPUTED VALUES# Source:,这里只有newname的相关资源和计算值,行为正好和我们预期的一致。说明condition和tags只在install时有效,在dependency update时无效。
还有一点,执行 helm install 之前,如果requirements.yaml中有charts中不存在的依赖,必须先执行 helm dep up 更新所有的依赖,否则会提示:

[debug] Created tunnel using local port: '45706'

[debug] SERVER: "127.0.0.1:45706"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-2

Error: found in requirements.yaml, but missing in charts/ directory: redis, jenkins, redis

3.1.5、目录:charts

目录charts用来存放当前Chart依赖的其他Chart,这些被依赖的Chart可以是上文requirements.yaml文件中描述的,也可以直接在charts目录下创建新的charts

[root@k8s-master charts]# cd mychart-2/charts/
[root@k8s-master charts]# pwd
/root/charts/mychart-2/charts
[root@k8s-master charts]# helm create mysubchart-1Creating mysubchart-1
[root@k8s-master charts]# helm create mysubchart-2Creating mysubchart-2
[root@k8s-master charts]# ll
drwxr-xr-x. 4 root root 93 3月  12 12:10 mysubchart-1
drwxr-xr-x. 4 root root 93 3月  12 12:10 mysubchart-2

每个subChart都是一个结构完整的Chart,可以是目录,也可以是tgz文件,tgz文件可以用 tar -zxvf 命令解压
此时若部署mychart-2,也会连同mysubchart-1和mysubchart-2一同部署到k8s上

####3.1.6、目录:templates

templates的具体内容在下文专门作为一个章节来阐述。

3.2、Hooks

3.2.1、Hooks介绍

Hook允许我们干涉执行 helm install 的过程。正常情况下 helm install 的执行步骤如下:

  • User runs helm install foo
  • Chart is loaded into Tiller
  • After some verification, Tiller renders the foo templates
  • Tiller loads the resulting resources into Kubernetes
  • Tiller returns the release name (and other data) to the client
  • The client exits

而可用的hook包括:

  • pre-install: Executes after templates are rendered, but before any resources are created in Kubernetes.
  • post-install: Executes after all resources are loaded into Kubernetes
  • pre-delete: Executes on a deletion request before any resources are deleted from Kubernetes.
  • post-delete: Executes on a deletion request after all of the release’s resources have been deleted.
  • pre-upgrade: Executes on an upgrade request after templates are rendered, but before any resources are loaded into Kubernetes (e.g. before a Kubernetes apply operation).
  • post-upgrade: Executes on an upgrade after all resources have been upgraded.
  • pre-rollback: Executes on a rollback request after templates are rendered, but before any resources have been rolled back.
  • post-rollback: Executes on a rollback request after all resources have been modified.

在正常的执行步骤中加入 pre-installpost-install 之后:

  • User runs helm install foo
  • Chart is loaded into Tiller
  • After some verification, Tiller renders the foo templates
  • Tiller prepares to execute the pre-install hooks (loading hook resources into Kubernetes)
  • Tiller sorts hooks by weight (assigning a weight of 0 by default) and by name for those hooks with the same weight in ascending order.
  • Tiller then loads the hook with the lowest weight first (negative to positive)
  • Tiller waits until the hook is “Ready”
  • Tiller loads the resulting resources into Kubernetes. Note that if the --wait flag is set, Tiller will wait until all resources are in a ready state and will not run the post-install hook until they are ready.
  • Tiller executes the post-install hook (loading hook resources)
  • Tiller waits until the hook is “Ready”
  • Tiller returns the release name (and other data) to the client
  • The client exits

注意:hook是安装weight值的大小,由小到大排序之后,按顺序执行,如果weight值相同则按名称排序。
Hook通常可以用来备份/回复数据之类的操作。

3.2.2、定义一个hook

[root@k8s-master charts]# vi mychart-2/templates/pre-install-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: "{{.Release.Name}}"
  labels:
    heritage: {{.Release.Service | quote }}
    release: {{.Release.Name | quote }}
    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": pre-install
    "helm.sh/hook-weight": "-5"
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    metadata:
      name: "{{.Release.Name}}"
      labels:
        heritage: {{.Release.Service | quote }}
        release: {{.Release.Name | quote }}
        chart: "{{.Chart.Name}}-{{.Chart.Version}}"
    spec:
      restartPolicy: Never
      containers:
      - name: pre-install-job
        image: "alpine:3.7"
        command: ["/bin/sleep","{{default "20" .Values.sleepyTime}}"]

“helm.sh/hook”:定义一个hook,如果需要定义为多个hook,可以用逗号分隔:post-install,post-upgrade
“helm.sh/hook-weight”:定义weight,可以是正数或者负数
“helm.sh/hook-delete-policy”:定义hook的删除策略
测试:可以执行 helm install mychart-2 部署此Chart,用 kubectl get cm 观察k8s上资源出现的时间

3.3、Repo

Helm的repository是一个HTTP服务,可以用于管理Chart:

### 查看已添加的repository ###
[root@k8s-master charts]# helm repo list

### 使用帮助 ###
[root@k8s-master charts]# helm repo -h

### Chart打包 ###
[root@k8s-master charts]# helm package mychart-2/Successfully packaged chart and saved it to: /root/charts/mychart-2-0.1.0.tgz
drwxr-xr-x. 4 root root   143 3月  12 14:01 mychart-2
-rw-r--r--. 1 root root 19642 3月  12 14:33 mychart-2-0.1.0.tgz

Helm提供一个serve,用于管理本地Chart:

### 使用帮助 ###
[root@k8s-master charts]# helm serve -h

### 启动本地服务 ###
[root@k8s-master charts]# helm serve --address "0.0.0.0:8879"
Regenerating index. This may take a moment.
Now serving you on 0.0.0.0:8879

然后在浏览器中访问,地址:http://192.168.119.160:8879

4、Template

前面的内容也提到了模版,模版主要功能就是将k8s部署用的yaml文件中的会发生改变的内容参数话,将不变的内容固定到模版文件中。
对于一个Chart,所有的模版都存放在templates目录内,在部署时,templates目录内的所有内容(处理”_”开头的内容)都会在被处理后发生到k8s。

4.1、第一个模版

### 创建一个Chart ###
[root@k8s-master charts]# helm create mychart-3
Creating mychart-3### 删除helm生成的文件,从零开始 ###
[root@k8s-master charts]# rm -rf mychart-3/templates/*

### 创建第一个模版文件,一个简单的ConfigMap ###
[root@k8s-master charts]# vi mychart-3/templates/configmap.yamlapiVersion: v1
kind: ConfigMap
metadata:
  name: mychart-configmap
data:
  myvalue: "Hello World"

### 安装部署到k8s上 ###
[root@k8s-master charts]# helm install mychart-3/NAME:   veering-owl
LAST DEPLOYED: Mon Mar 12 15:09:37 2018NAMESPACE: defaultSTATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME               DATA  AGE
mychart-configmap  1     0s

### 查看release ###
[root@k8s-master charts]# helm ls 
NAME        REVISION    UPDATED                     STATUS      CHART           NAMESPACE
veering-owl 1           Mon Mar 12 15:09:37 2018    DEPLOYED    mychart-3-0.1.0 default 

### 查看k8s部署情况 ###
[root@k8s-master charts]# kubectl get cm
NAME                DATA      AGE
mychart-configmap   1         56s
[root@k8s-master charts]# kubectl describe cm mychart-configmap Name:         mychart-configmap
Namespace:    defaultLabels:       
Annotations:  

Data
====
myvalue:
----
Hello World
Events:  

### 删除release ###
[root@k8s-master charts]# helm del veering-owl
release "veering-owl" deleted
[root@k8s-master charts]# kubectl get cm
No resources found.
[root@k8s-master charts]# helm ls
[root@k8s-master charts]# helm ls -a
NAME        REVISION    UPDATED                     STATUS  CHART           NAMESPACE
veering-owl 1           Mon Mar 12 15:09:37 2018    DELETED mychart-3-0.1.0 default
[root@k8s-master charts]# helm del --purge veering-owl
release "veering-owl" deleted
[root@k8s-master charts]# helm ls -a

Release的名称在不指定的情况下,随机产生。
使用 helm get manifest releaseName 可以重新查看 install 时的输出。

上面的例子中,将name硬编码到文件中可能不是一个好主意,下面的配置使我们得到一个和release name名称相仿的CM:

[root@k8s-master charts]# vi mychart-3/templates/configmap.yamlapiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"

[root@k8s-master charts]# helm install mychart-3/NAME:   peddling-moose
LAST DEPLOYED: Mon Mar 12 15:37:46 2018NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                      DATA  AGE
peddling-moose-configmap  1     0s

[root@k8s-master charts]# helm ls
NAME            REVISION    UPDATED                     STATUS      CHART           NAMESPACE
peddling-moose  1           Mon Mar 12 15:37:46 2018    DEPLOYED    mychart-3-0.1.0 default  

[root@k8s-master charts]# kubectl get cm
NAME                       DATA      AGE
peddling-moose-configmap   1         12s

Release是helm的内置变量,上文有详细说明,或者官方文档
在模版中,使用闭合的 {{}} 块来引用变量
变量本身支持namespace,Release前面的 “.” 表示最高级namespace,后面的 “.” 作为分隔符

4.2、Values.yaml文件

关于values.yaml文件上文有简单说明过,这里做进一步说明:values.yaml文件中的内容可以通过Values对象引用,Values对象的内容包括:

  • 当前Chart的values.yaml文件
  • 如果当前Chart是一个子Chart,还包括父Chart的values.yaml文件
  • 执行 helm install 或者 helm update 时 -f 参数指定的yaml文件,例如:helm install -f myvals.yaml ./mychart
  • --set 参数指定的内容,例如:helm install --set foo=bar ./mychart

—set具有最高优先级

4.2.1、使用values.yaml文件中的变量

[root@k8s-master charts]# vi mychart-3/values.yaml
favoriteDrink: coffee

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favoriteDrink }}

### 使用默认值 ###
[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '46193'

[debug] SERVER: "127.0.0.1:46193"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   odd-wallaby
REVISION: 1
RELEASED: Mon Mar 12 16:06:58 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
favoriteDrink: coffee

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: odd-wallaby-configmap
data:
  myvalue: "Hello World"
  drink: coffee

### 使用 --set 赋值 ###
[root@k8s-master charts]# helm install --debug --dry-run --set drink:=slurm mychart-3/ 
[debug] Created tunnel using local port: '40468'

[debug] SERVER: "127.0.0.1:40468"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   volted-chinchilla
REVISION: 1
RELEASED: Mon Mar 12 16:09:16 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
'drink:': slurm

COMPUTED VALUES:
'drink:': slurm
favoriteDrink: coffee

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: volted-chinchilla-configmap
data:
  myvalue: "Hello World"
  drink: coffee

4.2.2、使用层次结构

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink }}
  food: {{ .Values.favorite.food }}

[root@k8s-master charts]# vi mychart-3/values.yaml
favorite:
  drink: coffee
  food: pizza

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '42576'

[debug] SERVER: "127.0.0.1:42576"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   whimsical-rabbit
REVISION: 1
RELEASED: Mon Mar 12 16:13:41 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
favorite:
  drink: coffee
  food: pizza

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: whimsical-rabbit-configmap
data:
  myvalue: "Hello World"
  drink: coffee
  food: pizza

4.2.3、删除一个已经定义的值

在下载或者安装Chart时,如果想删除掉某个已经定义在values.yaml文件中的值,可以将该值设置为 null:

[root@k8s-master charts]# helm install mychart-3/ --set favorite.food=null
NAME:   mothy-hare
LAST DEPLOYED: Mon Mar 12 16:32:37 2018
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                  DATA  AGE
mothy-hare-configmap  3     0s


[root@k8s-master charts]# kubectl get cm
NAME                   DATA      AGE
mothy-hare-configmap   3         5s
[root@k8s-master charts]# kubectl describe cm mothy-hare-configmap
Name:         mothy-hare-configmap
Namespace:    default
Labels:       
Annotations:  

Data
====drink:
----
coffee
food:
----

myvalue:
----
Hello World
Events:  

这个在使用—debug –dry-run时,对应的值会被设置为 “null”,在k8s上则被设置为空。

4.3、模版方法和管道

4.3.1、方法

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ quote .Values.favorite.drink }}

表现结果
apiVersion: v1
kind: ConfigMap
metadata:
  name: lunging-stingray-configmap
data:
  myvalue: "Hello World"
  drink: "coffee

在这个例子中,我们调用了quote方法,并为其传递了一个参数.Values.favorite.drink

Helm有超过60个可用的方法,其中一部分上Go template language自己定义的,大部分其他的Sprig template library。

4.3.2、管道

和UNIX操作系统类似,管道用于连接两个命令,将前一个命令的输出传递给后一个命令输入,对于上面的例子可以修改为:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | quote }}

另一个例子:

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | repeat 5 | quote }}
  food: {{ .Values.favorite.food | upper | quote }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '34727'

[debug] SERVER: "127.0.0.1:34727"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   looming-pig
REVISION: 1
RELEASED: Mon Mar 12 16:47:24 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
favorite:
  drink: coffee
  food: pizza

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: looming-pig-configmap
data:
  myvalue: "Hello World"
  drink: "coffeecoffeecoffeecoffeecoffee"
  food: "PIZZA"

4.3.3、使用Default方法

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | repeat 5 | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  game: {{ .Values.favorite.game | default "NFS" | quote }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '46170'

[debug] SERVER: "127.0.0.1:46170"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   cautious-opossum
REVISION: 1
RELEASED: Mon Mar 12 16:50:59 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
favorite:
  drink: coffee
  food: pizza

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: cautious-opossum-configmap
data:
  myvalue: "Hello World"
  drink: "coffeecoffeecoffeecoffeecoffee"
  food: "PIZZA"
  game: "NFS"

Values.yaml文件中并未定义game。

4.4、操作符

Helm实现了所有的操作符( eqneltgtandor等等),并且可以使用”()”进行分组

4.4.1、流程控制语句

Helm支持的流程控制语句包括:if/elsewithrange,接下来分别介绍

4.4.1.1、IF/ELSE
  • 结构
{{ if PIPELINE }}
  # Do something
{{ else if OTHER PIPELINE }}
  # Do something else
{{ else }}
  # Default case
{{ end }}
  • 举例
[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | repeat 5 | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  game: {{ .Values.favorite.game | default "NFS" | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}
  mug: true
  {{ end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '36351'

[debug] SERVER: "127.0.0.1:36351"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   bald-newt
REVISION: 1
RELEASED: Mon Mar 12 17:11:01 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
favorite:
  drink: coffee
  food: pizza

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: bald-newt-configmap
data:
  myvalue: "Hello World"
  drink: "coffeecoffeecoffeecoffeecoffee"
  food: "PIZZA"
  game: "NFS"

  mug: true

可以看到正确输出了mug: true,但是格式不好看,莫名其妙多个一个空白行,这是因为helm在处理{{}}时,单纯的替换掉了{{}}块,保留了换行字符。

  • 说明
    PIPELINE 为一个boolean值,下面的所有内容都将被当作false处理:

  • a boolean false

  • a numeric zero

  • an empty string

  • a nil (empty or null)

  • an empty collection (map, slice, tuple, dict, array)

4.4.1.2、控制空白字符

上面的例子中,多出了一行空白行,我们可以使用 {{- 去掉左边的空白字符,或者使用 -}} 去掉右边的空白字符,也可以同时使用,去掉两遍的空白字符。注意,换行也属于空白字符,例如:

aaa
{{- xxxx }}
bbb

将得到:

aaa
bbb

因为中间一行左边的换行符被去掉,但是右边的换行符被保留。
在看:

aaa
{{- xxxx -}}
bbb

将得到:

aaabbb

因为中间一行两遍的换行符都被去掉了。
因此,上面的例子可以修改为:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | repeat 5 | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  game: {{ .Values.favorite.game | default "NFS" | quote }}
  {{- if eq .Values.favorite.drink "coffee" }}
  mug: true
  {{- end }}
4.4.1.3、使用WITH修改作用域
  • 结构
{{ with PIPELINE }}
  # restricted scope
{{ end }}
  • 举例
[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | repeat 5 | quote }}
  food: {{ .food | upper | quote }}
  game: {{ .game | default "NFS" | quote }}
  {{- end }}


[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '40227'

[debug] SERVER: "127.0.0.1:40227"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   maudlin-yak
REVISION: 1
RELEASED: Mon Mar 12 17:28:58 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
favorite:
  drink: coffee
  food: pizza

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: maudlin-yak-configmap
data:
  myvalue: "Hello World"
  drink: "coffeecoffeecoffeecoffeecoffee"
  food: "PIZZA"
  game: "NFS"
  • 说明

With将默认的命名空间改变为”.Values.favorite”,使with块内部可以方便的引用变量。

  • 注意

With块内部不能在访问其他任何命令空间的内容,比如下面的写法将得到一个异常:

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | repeat 5 | quote }}
  food: {{ .food | upper | quote }}
  game: {{ .game | default "NFS" | quote }}
  release: {{ .Release.Name }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '38400'

[debug] SERVER: "127.0.0.1:38400"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

Error: render error in "mychart-3/templates/configmap.yaml": template: mychart-3/templates/configmap.yaml:11:22: executing "mychart-3/templates/configmap.yaml" at <.Release.Name>: can't evaluate field Name in type interface {}

可以修改为:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | repeat 5 | quote }}
  food: {{ .food | upper | quote }}
  game: {{ .game | default "NFS" | quote }}
  {{- end }}
  release: {{ .Release.Name }}
4.4.1.4、循环控制语句

Helm使用range来实现循环。

  • 举例
[root@k8s-master charts]# vi mychart-3/values.yaml
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  toppings: |-
    {{- range .Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '38008'

[debug] SERVER: "127.0.0.1:38008"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   illocutionary-molly
REVISION: 1
RELEASED: Mon Mar 12 17:43:35 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: illocutionary-molly-configmap
data:
  toppings: |-
    - "Mushrooms"    - "Cheese"    - "Peppers"    - "Onions"
  • 说明

Range块中,”.“表示当前值,title是一个首字母大写的function,toppings后面的”|”表示后面的内容是一个可择行的大字符串,在之后的”-“表示去掉末尾的空白字符。

  • 迭代元祖
  sizes: |-
    {{- range tuple "small" "medium" "large" }}
    - {{ . }}
    {{- end }}
  sizes: |-
    - small
    - medium
    - large

4.4.2、自定义变量

使用自定义变量可以更好的使用 withrange 。在上面的例子中,我们使用下面的内容会出现异常:

  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  release: {{ .Release.Name }}
  {{- end }}

因为Release.Name不再with块范围内,一种可以避免错误的办法就是在with块之外定义一个变量,然后将Release.Name的值赋给这个变量:

[root@k8s-master charts]# vi mychart-3/values.yaml
favorite:
  drink: coffee
  food: pizza

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  {{- $relname := .Release.Name -}}
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  release: {{ $relname }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '40191'

[debug] SERVER: "127.0.0.1:40191"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   moldy-labradoodle
REVISION: 1
RELEASED: Mon Mar 12 17:58:56 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
favorite:
  drink: coffee
  food: pizza

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: moldy-labradoodle-configmap
data:
  drink: "coffee"
  food: "PIZZA"
  release: moldy-labradoodle

另外一个例子:定义循环下标

[root@k8s-master charts]# vi mychart-3/values.yaml
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  {{- range $index, $topping := .Values.pizzaToppings }}
    {{ $index }}: {{ $topping }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '40932'

[debug] SERVER: "127.0.0.1:40932"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   wobbly-macaw
REVISION: 1
RELEASED: Mon Mar 12 18:02:04 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: wobbly-macaw-configmap
data:
    0: mushrooms
    1: cheese
    2: peppers
    3: onions

同理:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- range $key, $val := .Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end}}

将得到:

# Source: mychart/templates/configmap.yamlapiVersion: v1
kind: ConfigMap
metadata:
  name: eager-rabbit-configmap
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

4.4.3、命名模版

命名模版允许我们定义模版块,从而将完整的大模版拆分成数个小模版。将重复的部门抽取来定义成命名模版使我们的模版文件看起来更清晰。模版的命名规范如下:

  • 大多数包含kubernetes对象定义的模版文件都存放在templates目录下,install时会被部署到kubernetes上
  • NOTES.txt 是一个例外,它并不包含kubernetes对象定义
  • 以”_“开头的文件不对被传递给kubernetes,即使它包含kubernetes对象定义。这类文件通常会被其他模版引用

#####4.4.3.1、定义和引用模版

  • 结构
{{ define "MY_NAME" }}
  # body of template here
{{ end }}
  • 举例
[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
{{- define "my_labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "my_labels" }}
data:
  {{- range $index, $topping := .Values.pizzaToppings }}
    {{ $index }}: {{ $topping }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '39896'

[debug] SERVER: "127.0.0.1:39896"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   yucky-tuatara
REVISION: 1
RELEASED: Tue Mar 13 09:26:51 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: yucky-tuatara-configmap
  labels:
    generator: helm
    date: 2018-03-13
data:
    0: mushrooms
    1: cheese
    2: peppers
    3: onions
  • 说明

Helm建议我们将这类用来引用的模版定义到 _helpers.tpl 文件中:

[root@k8s-master charts]# vi mychart-3/templates/_helpers.tpl
{{- define "my_labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}{{- end }}

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "my_labels" }}
data:
  {{- range $index, $topping := .Values.pizzaToppings }}
    {{ $index }}: {{ $topping }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
4.4.3.2、为模版指定命名空间

默认情况下,命名模版不能够使用任何参数或者内建对象(如:Release、Values等),但是可以调用function,比如下面的例子:

[root@k8s-master charts]# vi mychart-3/templates/_helpers.tpl
{{/* Generate basic labels */}}{{- define "my_labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
    chart: {{ .Chart.Name }}
    version: {{ .Chart.Version }}{{- end }}

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "my_labels" }}
data:
  {{- range $index, $topping := .Values.pizzaToppings }}
    {{ $index }}: {{ $topping }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '44449'

[debug] SERVER: "127.0.0.1:44449"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   nonplussed-toucan
REVISION: 1
RELEASED: Tue Mar 13 11:03:16 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nonplussed-toucan-configmap
  labels:
    generator: helm
    date: 2018-03-13
    chart: 
    version: 
data:
    0: mushrooms
    1: cheese
    2: peppers
    3: onions

可以正常使用now和管道,但是 {{ .Chart.Name }}{{ .Chart.Version }} 的值为空

解决这个问题的方案就是在引用模版时,为其传递一个命名空间,将上述事例做如下改动:

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "my_labels" . }}
data:
  {{- range $index, $topping := .Values.pizzaToppings }}
    {{ $index }}: {{ $topping }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '35723'

[debug] SERVER: "127.0.0.1:35723"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   dinky-worm
REVISION: 1
RELEASED: Tue Mar 13 11:06:57 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---
# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: dinky-worm-configmap
  labels:
    generator: helm
    date: 2018-03-13
    chart: mychart-3
    version: 0.1.0
data:
    0: mushrooms
    1: cheese
    2: peppers
    3: onions

{{- template "my_labels" }} 修改为 {{- template "my_labels" . }} ,”.” 表示顶级命名空间,你也可以更具需要传递 .Values 或者 .Values.favorite 等任何你想要传递的范围。

4.4.3.3、使用INCLUDE引用模版

使用include引用模版允许我们使用管道和函数,看下面的例子:

### 假设有如下模版 ###
[root@k8s-master charts]# vi mychart-3/templates/_helpers.tpl
{{- define "mychart_app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}"
{{- end -}}

### 引用 ###
[root@k8s-master charts]# vi mychart-3/templates/configmap.yamlapiVersion: v1
kind: ConfigMapmetadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{ template "mychart_app" .}}
data:
  myvalue: "Hello World"

### 测试 ###
[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '38909'

[debug] SERVER: "127.0.0.1:38909"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   kind-giraffe
REVISION: 1RELEASED: Tue Mar 13 11:15:34 2018CHART: mychart-3-0.1.0USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:MANIFEST:

---
# Source: mychart-3/templates/configmap.yamlapiVersion: v1
kind: ConfigMapmetadata:
  name: kind-giraffe-configmap
  labels:
    app_name: mychart-3app_version: "0.1.0+1520910934"data:
  myvalue: "Hello World"
[root@k8s-master charts]# 

输出与预期有些出入,app_version: "0.1.0+1520910934" 没有对齐,因为模版中的内容是没有左边的空白符的,而我们也不能在定义模版时添加空白字符,并且 template 不支持函数,一种解决办法是:

### 修改上例引用 ###
[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
{{ include "mychart_app" . | indent 4 }}
data:
  myvalue: "Hello World"

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/

注意:{{ include "mychart_app" . | indent 4 }} 要顶格写,左边不要有空格

4.4.4、文件访问

除了命名模版,helm还可以直接应用文件。Helm提供 .Files 对象来支持文件访问,并给出以下建议:

  • 可以在你的Chart中添加额外的文件,这些文件会被发送到kubernetes,但是要小心,一个Chart必须要小于1M,因为这是kubernetes对象大小的限制。
  • 因为某些安全因素,有些文件无法通过 .Files 访问:
  • Charts do not preserve UNIX mode information, so file-level permissions will have no impact on the availability of a file when it comes to the .Files object.
4.4.4.1、基本事例
[root@k8s-master charts]# vi mychart-3/config1.toml
message = Hello from config 1

[root@k8s-master charts]# vi mychart-3/config2.toml
message = This is config 2

[root@k8s-master charts]# vi mychart-3/config3.toml
message = Goodbye from config 3

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  {{- $files := .Files }}
  {{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
  {{ . }}: |-
    {{ $files.Get . }}
  {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '42465'

[debug] SERVER: "127.0.0.1:42465"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   loopy-mastiff
REVISION: 1
RELEASED: Tue Mar 13 12:14:13 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: loopy-mastiff-configmap
data:
  config1.toml: |-
    message = Hello from config 1

  config2.toml: |-
    message = This is config 2

  config3.toml: |-
    message = Goodbye from config 3
4.4.4.2、路径匹配
### 修改上面的事例 ###
[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  {{- $files := .Files }}
  {{ range $path, $bytes := .Files.Glob "**.toml" }}
  {{ $path }}: |-
    {{ $files.Get $path }}
  {{ end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/

官网上的例子:

{{ range $path := .Files.Glob "**.yaml" }}{{ $path }}: |
{{ .Files.Get $path }}{{ end }}

是有问题的,此时$path为文件内容的字节码,而非路径,而且在循环内部不能使用 .Files ,当然也不排除我做实验时姿势不对的原因!

4.4.4.3、CONFIGMAP 和 SECRETS函数
### 两个便利的函数 ###
[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
{{ (.Files.Glob "**.toml").AsConfig | indent 2 }}
{{ (.Files.Glob "**.toml").AsSecrets | indent 2 }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '40984'

[debug] SERVER: "127.0.0.1:40984"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   mangy-antelope
REVISION: 1
RELEASED: Tue Mar 13 12:26:12 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mangy-antelope-configmap
data:
  config1.toml: |
    message = Hello from config 1
  config2.toml: |
    message = This is config 2
  config3.toml: |
    message = Goodbye from config 3

  config1.toml: bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
  config2.toml: bWVzc2FnZSA9IFRoaXMgaXMgY29uZmlnIDIK
  config3.toml: bWVzc2FnZSA9IEdvb2RieWUgZnJvbSBjb25maWcgMwo=
4.4.4.4、ENCODING
### 有一个便利的函数 ###
[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  token: |-
    {{ .Files.Get "config1.toml" | b64enc }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '40126'

[debug] SERVER: "127.0.0.1:40126"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   mortal-warthog
REVISION: 1
RELEASED: Tue Mar 13 12:28:25 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mortal-warthog-configmap
data:
  token: |-
    bWVzc2FnZSA9IEhlbGxvIGZyb20gY29uZmlnIDEK
4.4.4.5、逐行读取文件
[root@k8s-master charts]# vi mychart-3/config1.toml
message = Hello from config 1
message = Hello from config 2
message = Hello from config 3
message = Hello from config 4
message = Hello from config 5
message = Hello from config 6
message = Hello from config 7
message = Hello from config 8

[root@k8s-master charts]# vi mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  some-file.txt: |
    {{- range .Files.Lines "config1.toml" }}
    {{ . }}
    {{- end }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '43350'

[debug] SERVER: "127.0.0.1:43350"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

NAME:   giddy-rat
REVISION: 1
RELEASED: Tue Mar 13 12:32:59 2018
CHART: mychart-3-0.1.0
USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions

HOOKS:
MANIFEST:

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: giddy-rat-configmap
data:
  some-file.txt: |
    message = Hello from config 1
    message = Hello from config 2
    message = Hello from config 3
    message = Hello from config 4
    message = Hello from config 5
    message = Hello from config 6
    message = Hello from config 7
    message = Hello from config 8

4.4.5、创建NOTE.txt文件

NOTE.txt是一个文本文件,文件内容会在 chart install 或者 chart upgrade 之后输出到屏幕上,因此我们可以给出一些提示信息:

[root@k8s-master charts]# vi mychart-3/templates/NOTE.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 {{ .Release.Name }}

[root@k8s-master charts]# helm install --debug --dry-run mychart-3/
[debug] Created tunnel using local port: '34825'

[debug] SERVER: "127.0.0.1:34825"

[debug] Original chart version: ""
[debug] CHART PATH: /root/charts/mychart-3

Error: YAML parse error on mychart-3/templates/NOTE.txt: error converting YAML to JSON: yaml: line 4: mapping values are not allowed in this context

---# Source: mychart-3/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: whopping-wolf-configmap
data:
  some-file.txt: |
    message = Hello from config 1
    message = Hello from config 2
    message = Hello from config 3
    message = Hello from config 4
    message = Hello from config 5
    message = Hello from config 6
    message = Hello from config 7
    message = Hello from config 8


---# Source: mychart-3/templates/NOTE.txt
Thank you for installing mychart-3.

Your release is named whopping-wolf.

To learn more about the release, try:

  $ helm status whopping-wolf
  $ helm get whopping-wolf

4.4.6、子Chart和全局变量

关于全局变量的部分内容已经在Chart一节中介绍过,更多详细内容可参考:https://docs.helm.sh/chart_template_guide/#subcharts-and-global-values

5、附件下载

网盘链接:https://pan.baidu.com/s/1YUkLahajwqxH9hoPW74x9Q

解压:tar -zxvf tiller-v2.8.1-tar.gz
导入:docker load -i tiller-v2.8.1-tar

调试模板

调试模板可能会很棘手,因为模板在 Tiller 服务器而不是 Helm 客户端上渲染。然后渲染的模板被发送到 Kubernetes API 服务器,可能由于格式以外的原因,服务器可能会拒绝接收这些 YAML 文件。

有几个命令可以帮助您进行调试。

helm lint 是验证 chart 是否遵循最佳实践的首选工具
helm install --dry-run --debug:我们已经知道了这个窍门。这是让服务器渲染你的模板,然后返回结果清单文件的好方法。
helm get manifest:这是查看服务器上安装的模板的好方法。
当你的 YAML 没有解析,但想看看生成了什么时,检索 YAML 的一个简单方法是注释模板中的问题部分,然后重新运行 helm install --dry-run --debug

apiVersion: v1
# some: problem section
# {{ .Values.foo | quote }}

以上内容将被完整渲染并返回。

apiVersion: v1
# some: problem section
#  "bar"

这提供了一种快速查看生成的容的方式,而不会由于YAML分析错误而被阻止。

写了个python脚本,放在helm应用文件夹外面 修改代码中的文件夹名称.可以生成new文件夹,里面是配置好yml文件


import os,io
import shutil

helm_app = 'prometheus'

# 执行命令获取返回值
status = os.popen("helm install --dry-run --debug "+helm_app)
result = status.read()
status.close()
# print(result)

# 正则匹配输出文件
canwrite=False
nowpath = ""
nowfile=None
allline = result.split('\n')
if os.path.exists('new'):
    shutil.rmtree('new')
for line in allline:
    if(line.startswith('# Source: ')):
        if (nowpath):
            nowfile.close()

        nowpath = 'new/'+line.replace('# Source: ','')
        nowdir = os.path.dirname(nowpath)
        if(not os.path.exists(nowdir)):
            os.makedirs(nowdir)
        nowfile=open(nowpath,mode='w')
        canwrite=True

    if (canwrite and nowpath and nowfile):
        nowfile.write(line+"\n")

    print(line)

你可能感兴趣的:(架构)