Helm template 有講沒講差不多

 ·  ☕ 5 

由於協助自己公司內訓使用 helm 管理 Kubernetes Applicetion ,就非常的簡單的寫了怎麼看 Helm 的架構與 template 。

目錄結構

可以先看一下這一個目錄結構

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

首先我們會看到 chart,yaml 這個檔案,裡面記錄著這個 chart 是第幾版,是一個library 還是一個 application ,部署的 application 是幾版,另外最重要的是 apiVersion 是在定義這個chart 是 helm v2 還是 v3 ,因為 helm v2 跟 v3 不相容這邊要特別注意!

可來看一下chart.yaml 裡面寫了什麼。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v2
name: helm-example
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
appVersion: 1.16.0

value

value.yaml 這個檔案定義了這個 chart 會用到的變數,基本上是提供給 _helpers.tplDeployment.yaml…做使用的。
我們來看一下value.yaml 裡面寫了些什麼

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# Default values for helm-example.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: ""

imagePullSecrets: []
nameOverride: "example-helm-playground"
fullnameOverride: ""

serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name: ""

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    - host: chart-example.local
      paths: []
  tls: []
  #  - secretName: chart-example-tls
  #    hosts:
  #      - chart-example.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

nodeSelector: {}

tolerations: []

affinity: {}

可以看到裡面定義了很多參數,這些參數基本上 基本上 基本上 基本上 會套用到剛剛說到的_helpers.tplDeployment.yaml…做使用的,有些沒寫好的在這邊就算在這邊寫說我Deployment要使用什麼參數也沒用xDD

templates

templates 底下會放 Kubernetes要使用到的資源,如Deployment、configmap、service等等,這些資源都是一個樣板。
有一個比較特別的檔案是_helpers.tpl,裡面放了一些變數,可以讓整個template底下的物件共享這些變數。

我們可以把文件點開來看裡面裡面是什麼一回事。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{{/*
{{/*
Expand the name of the chart.
*/}}
{{- define "helm-example.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "helm-example.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "helm-example.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "helm-example.labels" -}}
helm.sh/chart: {{ include "helm-example.chart" . }}
{{ include "helm-example.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "helm-example.selectorLabels" -}}
app.kubernetes.io/name: {{ include "helm-example.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "helm-example.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "helm-example.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

簡單來說就是定義了好幾個全域參數,這邊我幫大家整理一下這幾個參數。

  • lafite-chatbot.name

    • 條件是如果 value 的 nameOverride 有定義
      • 就用 nameOverride 取代 .Chart.Name
    • 如果長度超過63個字
      • 後面的字都不要
    • 如果最後一個字元有
      • 把該字元修剪掉
  • helm-example.fullname

    • 如果 Values.fullnameOverride 有定義的話
      • .Values.fullnameOverride
        • 如果長度超過63個字
          • 後面的字都不要
        • 如果最後一個字元有
          • 把該字元修剪掉
    • $name
      • 如果 Values.fullnameOverride 有定義的話
        • 就用 nameOverride 取代 .Chart.Name
    • 如果 .Release.Name 有包含 $name 的話
      • 如果長度超過63個字
        • 後面的字都不要
      • 如果最後一個字元有
        • 把該字元修剪掉
    • 最後按照上面的規則印出 .Release.Name-$name
      • 如果長度超過63個字
        • 後面的字都不要
      • 如果最後一個字元有
        • 把該字元修剪掉
  • helm-example.chart

    • 印出 .Chart.Name-.Chart.Version
      • 如果字元有遇到+
        • 轉換成_
      • 如果長度超過63個字
        • 後面的字都不要
      • 如果最後一個字元有
        • 把該字元修剪掉
  • helm-example.labels

    • helm.sh/chart : 加上之前定義的 helm-example.chart
    • 如果有定義 .Chart.AppVersion
      • 那就加上 app.kubernetes.io/version : .Chart.AppVersion 的雙引號
    • app.kubernetes.io/managed-by: {{ .Release.Service }}
  • `helm-example.selectorLabels

    • app.kubernetes.io/name: 加上之前定義的 helm-example.name
    • app.kubernetes.io/instance: 加上之前定義的 Release.Name
  • helm-example.serviceAccountName

    • 如果.Values.serviceAccount.create 有定義的話
      • 預設使用之前有定義的 helm-example.fullname
        • 不然就是用 .Values.serviceAccount.name
    • 預設使用default
      • 不然就是 .Values.serviceAccount.name

了解了基礎的目錄結構後可以再深入地看一下元件的樣板

Deployment

這邊遇到有用go template 的段落才會進行解說

使用 _helpter.tpl

1
2
3
4
metadata:
  name: {{ include "helm-example.fullname" . }}
  labels:
    {{- include "helm-example.labels" . | nindent 4 }}

先看到 name 的部分 這邊他會直接套用 _helpter.tpl 內的 fullname

再來看到 labels 的部分 這邊他會直接套用 _helpter.tpl 內的 labels 並且透過 nindent function 將每個開頭空四格

使用 value

1
2
3
{{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
{{- end }}

接下來看到 replicas 的部分如果在 Values 沒有定義 autoscaling.enabled 就會採用 Values 內的 replicaCount。

使用 go template function

1
2
3
selector:
    matchLabels:
      {{- include "helm-example.selectorLabels" . | nindent 6 }}

在這個部分 matchLabels 直接套用 _helpter.tpl 內的 selectorLabels 並且透過 nindent function 將每個開頭空四格。

使用 go template to yaml

1
2
3
4
5
    metadata:
    {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
    {{- end }}

如果有在 value 寫 podAnnotations 這裡就會跑for each 把內容物把內容物,透過 nindent function 將每個開頭空四格。
例如你在value內定義以下這個範例

1
2
podAnnotations:
  prometheus.io/scrape: "true"

Helm 會幫你轉換成以下格式

1
2
3
4
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"

只要了解上述這幾點並且搭配 go template 就可以做出非常多的變化,唯一缺點大概就是就是可讀性非常差吧。

Note

Note 是記錄這個 chart 是怎麼用的,直接來看範例。

1
2
3
4
5
6
7
8
9
{{- if .Values.ingress.enabled }}
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
  {{- range .paths }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
  {{- end }}
{{- end }}
...

這邊如果我們有在 value 定義 ingress 定義 ingress.enabled 的話。
就會用 for range 去遞迴取出 value 底下的 ingress 的所有 host
並且用它組合成該 http:// ……的形式
讓使用者知道這服務該怎麼存取。

結語

go-template 非常不直觀在幫自己公司內訓的時候不太好 debug 找括弧看變數等,都不是容易維護。


Meng Ze Li
Meng Ze Li
Kubernetes / DevOps / Backend