こんにちは。KOUKIです。
とある企業でWebエンジニアをやっています。
今回は、HelmのTemplateについて深く学びましょう。
<目次>
検証環境
- helm v3
- mac
- minikube
Kubernetesに関しては、こちらも参考にしてください。
ワークスペースの作成
以下のコマンドで、ワークスペースを作成します。
1 2 3 |
# playgroundフォルダが作成される $ helm create playground Creating playground |
テンプレート基礎
このplayground/templatesの中にNOTES.txtがあるのですが、以下の通り書き換えてみましょう。
1 2 3 |
The name of this chart is {{ .Chart.Name }}, its version is {{ .Chart.Version }} and the bundled applicaation version is {{ .Chart.AppVersion }} |
公式サイトによるとChartは、Chart.yamlを指すようです。※他にも沢山あるのでチェックした方がいいです
https://helm.sh/docs/chart_template_guide/builtin_objects/#helm
Chart
: The contents of theChart.yaml
file. Any data inChart.yaml
will be accessible here. For example{{ .Chart.Name }}-{{ .Chart.Version }}
will print out themychart-0.1.0
.
ちなみに、Chart.yamlの中身は以下です。
1 2 3 4 5 6 7 8 9 |
apiVersion: v2 name: playground description: A Helm chart for Kubernetes type: application version: 0.1.0 appVersion: "1.16.0" |
この値を参照できる様ですね。
「–dry-run」オプションを付けてデバックしてみましょう。
1 2 3 4 5 6 7 8 |
$ helm install test --dry-run . NAME: test .... NOTES: The name of this chart is playground, its version is 0.1.0 and the bundled applicaation version is 1.16.0 |
NOTES.txtに書かれた文字列が出力されていることがわかります。
Releaseオブジェクト
公式サイトによるとReleaseオブジェクトというものが組み込まれているようなので、確認してみましょう。
Release
: This object describes the release itself. It has several objects inside of it:Release.Name
: The release nameRelease.Namespace
: The namespace to be released into (if the manifest doesn’t override)Release.IsUpgrade
: This is set totrue
if the current operation is an upgrade or rollback.Release.IsInstall
: This is set totrue
if the current operation is an install.Release.Revision
: The revision number for this release. On install, this is 1, and it is incremented with each upgrade and rollback.Release.Service
: The service that is rendering the present template. On Helm, this is alwaysHelm
.
NOTES.txtに以下の文言を追加します。
1 2 3 4 5 6 |
... The release name is {{ .Release.Name }} and it will be installed to the {{ .Release.Namespace }} namespace. Is this a new installation? {{ .Release.IsInstall }} Is this an upgrade? {{ .Release.IsUpgrade }} |
1 2 3 4 5 6 7 |
$ helm install test --dry-run . ... The release name is test and it will be installed to the default namespace. Is this a new installation? true Is this an upgrade? false |
Template関数
HelmはGo言語で実装されています。つまり、TemplateもまたGoに準拠するということです。
Sprig Functiion Documentationに、Template関数がまとめられています。
toYamlとnindent
自動生成されたファイルの一つに「deployment.yaml」ファイルがあります。
このファイルの以下の箇所に注目しましょう。
1 2 |
resources: {{- toYaml .Values.resources | nindent 12 }} |
toYamlはオブジェクトをYaml形式で展開してくれます。
nindentは、インデントを示しています。上記の例の場合は、12のインデントですね。
「.Values」とある通り「values.yaml」ファイルを参照しているので、values.yamlのresourcesに以下の設定を行います。
1 2 3 4 5 6 7 |
resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi |
NOTES.txtも以下のように書き換えます。
1 2 |
resources: {{ .Values.resources }} |
デバッキングしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ helm install test --dry-run . ... spec: replicas: 1 selector: ... resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi ... NOTES: resources: map[limits:map[cpu:100m memory:128Mi] requests:map[cpu:100m memory:128Mi]] |
resourcesが2つ出力されていると思いますが、片方はYAML形式、もう片方はTEXT形式で出力されています。
この違いが、toYamlのnindentです。
NOTES.txtを以下に書き換えます。
1 2 |
resources: {{ toYaml .Values.resources | nindent 4}} |
1 2 3 4 5 6 7 8 9 10 11 |
$ helm install test --dry-run . NOTES: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi |
結構いい感じになりましたが、resourcesとlimitsの間が空いてるのが気になります。
nindentをindentに変更しましょう。
1 2 |
resources: {{ toYaml .Values.resources | indent 4}} |
1 2 3 4 5 6 7 8 9 10 |
helm install test --dry-run . NOTES: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi |
いい感じですね! nindentのnは、改行(newline)のnっぽいですね。
ちなみにdeployment.yamlの方では、「-」がついていたと思います。これは、インデントをちょうど良い感じに調整してくれる機能があるようなので試してみましょう。
1 2 |
resources: {{- toYaml .Values.resources | indent 4}} |
1 2 3 4 5 6 7 8 9 |
$ helm install test --dry-run . NOTES: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi |
少し変になりましたね。。 nindentに戻して(ついでにインデント数を2にする)、もう一度実行してみましょう。
1 2 |
resources: {{- toYaml .Values.resources | nindent 2}} |
1 2 3 4 5 6 7 8 9 10 |
$ helm install test --dry-run . NOTES: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi |
綺麗な感じになりましたね。
補足ですが、以前勉強した「–set」オプションで値の上書きも可能です。
1 2 3 4 5 6 7 8 9 10 |
$ helm install test --dry-run --set resources.limits.cpu="120m" . NOTES: resources: limits: cpu: 120m memory: 128Mi requests: cpu: 100m memory: 128Mi |
ConfigMapsとSecrets
Kubernetesでもよく使うConfigmapとSecretsの扱い方を見ていきましょう。
まずは、ConfigMapsから。
1 |
$ touch templates/configmap.yaml |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
apiVersion: v1 kind: ConfigMap metadata: name: {{ include "playground.fullname" .}} data: default.json: | { "network": { "port": "80" }, "environment": { "SSL": false } } |
default.jsonは別ファイルに保存したいですね。
1 |
$ touch default.json |
1 2 3 4 5 6 7 8 |
{ "network": { "port": "80" }, "environment": { "SSL": false } } |
Helmでは他のファイルをimportするときFilesオブジェクトを使います。
1 2 3 4 5 6 7 |
apiVersion: v1 kind: ConfigMap metadata: name: {{ include "playground.fullname" . }} data: default.json: | {{ .Files.Get "default.json" | indent 4 }} |
こういうの結構便利ですよね。環境変数を別ファイルに保存したいとかよくありますから。
次は、Secretsです。
1 |
$ touch templates/secret.yaml |
1 2 3 4 5 6 7 8 |
apiVersion: v1 kind: Secret metadata: name: {{ include "playground.fullname" . }} type: Opaque data: # APIKEYをこのまま載せるのはセキュリティ的にアウト。これはあくまでサンプル APIKEY: {{ "8oZ73ujfLImshH6xaf8wypGd8o6kp1U0NPSjsnQ6wvUt4FaLLO" | b64enc }} |
deployment.yamlにvolumeMountsしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
containers: ... volumeMounts: - name: config mountPath: /app/config/default.json subPath: default.json env: - name: APIKEY valueFrom: secretKeyRef: name: {{ include "playground.fullname" . }} key: APIKEY ... tolerations: {{- toYaml . | nindent 8 }} {{- end }} # 追加 volumes: - name: config configMap: name: {{ include "playground.fullname" . }} |
Chart.yamlも変更します。
1 2 3 4 5 6 7 8 9 10 |
apiVersion: v2 name: playground description: A Helm chart for Kubernetes type: application version: 0.1.0 appVersion: "2.0.0" |
values.yamlのimageも変更します。
1 2 3 |
image: # repository: nginx repository: afakharany/hellonodejs:2.0.0 |
下記のコマンドを実行しましょう。
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
$ helm install myapp01 --dry-run . NAME: myapp01 LAST DEPLOYED: Thu Nov 25 15:52:00 2021 NAMESPACE: default STATUS: pending-install REVISION: 1 HOOKS: --- ... --- # Source: playground/templates/secret.yaml apiVersion: v1 kind: Secret metadata: name: myapp01-playground type: Opaque data: # APIKEYをこのまま載せるのはセキュリティ的にアウト。これはあくまでサンプル APIKEY: OG9aNzN1amZMSW1zaEg2eGFmOHd5cEdkOG82a3AxVTBOUFNqc25RNnd2VXQ0RmFMTE8= --- # Source: playground/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: myapp01-playground data: default.json: | { "network": { "port": "80" }, "environment": { "SSL": false } } --- ... --- # Source: playground/templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: myapp01-playground labels: helm.sh/chart: playground-0.1.0 app.kubernetes.io/name: playground app.kubernetes.io/instance: myapp01 app.kubernetes.io/version: "2.0.0" app.kubernetes.io/managed-by: Helm spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: playground app.kubernetes.io/instance: myapp01 template: metadata: labels: app.kubernetes.io/name: playground app.kubernetes.io/instance: myapp01 spec: serviceAccountName: myapp01-playground securityContext: {} containers: - name: playground securityContext: {} image: "afakharany/hellonodejs:2.0.0:2.0.0" imagePullPolicy: IfNotPresent volumeMounts: - name: config mountPath: /app/config/default.json subPath: default.json env: - name: APIKEY valueFrom: secretKeyRef: name: myapp01-playground key: APIKEY ports: - name: http containerPort: 80 protocol: TCP livenessProbe: httpGet: path: / port: http readinessProbe: httpGet: path: / port: http resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi volumes: - name: config configMap: name: myapp01-playground ... |
問題なさそうですね。
では、実際にデプロイしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ helm install myapp01 . NAME: myapp01 LAST DEPLOYED: Thu Nov 25 15:54:39 2021 NAMESPACE: default STATUS: deployed REVISION: 1 NOTES: resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi |
1 2 3 |
$ kubectl get po NAME READY STATUS RESTARTS AGE myapp01-playground-6b4ffcdb75-lwwhv 0/1 InvalidImageName 0 13s |
InvalidImageNameになってしまいました!
すみません。values.yamlのimageにバージョンの指定はいらなかったです。以下に直して下さい。
1 2 3 |
image: # repository: nginx repository: afakharany/hellonodejs |
deployementをみるとChartのバージョンから取得するようになってますね。
1 |
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" |
修正したらupgradeです。
1 2 3 4 5 6 7 8 9 10 |
$ helm upgrade myapp01 . $ kubectl get po NAME READY STATUS RESTARTS AGE myapp01-playground-6f5cd6899f-8jn4n 1/1 Running 0 2m20s # ポートフォワーディング $ kubectl port-forward myapp01-playground-6f5cd6899f-8jn4n 8080:80 Forwarding from 127.0.0.1:8080 -> 80 Forwarding from [::1]:8080 -> 80 |
悲しいことに下記の通りInvalid Requestになってしまいました(原因は不明です)が、起動はできたのでひとまずよしとしましょう。

Configフォルダの作成
Configフォルダを作成し、そこにdefault.jsonファイルを移動させましょう。
1 2 3 |
$ mkdir config $ cd config/ $ mv ../default.json . |
そして、さらにJSONファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ for f in {1..5} > do > touch $f.json && echo \{\"name\":\"File $f\"\} > $f.json > done $ ls -l total 48 -rw-r--r-- 1 hoge staff 18 11 26 04:52 1.json -rw-r--r-- 1 <meta charset="utf-8">hoge staff 18 11 26 04:52 2.json -rw-r--r-- 1 <meta charset="utf-8">hoge staff 18 11 26 04:52 3.json -rw-r--r-- 1 <meta charset="utf-8">hoge staff 18 11 26 04:52 4.json -rw-r--r-- 1 <meta charset="utf-8">hoge staff 18 11 26 04:52 5.json -rw-r--r-- 1 <meta charset="utf-8">hoge staff 73 11 25 15:23 default.json |
Configフォルダをconfigmap.yamlから読み込みます。
1 2 3 4 5 6 7 8 |
apiVersion: v1 kind: ConfigMap metadata: name: {{ include "playground.fullname" .}} data: default.json: | # {{ .Files.Get "default.json" | indent 4 }} {{ .Files.Glob "config/*" | indent 4 }} |
Globにすると複数ファイルを指定できるようです。config配下の全て(*)を指定しました。
デバッキングしましょう。
1 2 3 |
$ cd .. $ helm upgrade myapp01 . --dry-run Error: UPGRADE FAILED: template: playground/templates/configmap.yaml:8:35: executing "playground/templates/configmap.yaml" at <4>: wrong type for value; expected string; got engine.files |
エラーが出ました。実は今の実装のままだとFileオブジェクトが返却されるため、コマンドが実行できないのです。
configmap.yamlの設定を以下の通りに書き換えます。
1 |
{{ (.Files.Glob "config/*").AsConfig | indent 4 }} |
上記の様にAsConfigを追加しました。ちなみに、AsSecretsもあります。
もう一度、コマンドを実行します。
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 |
$ helm upgrade myapp01 . --dry-run Release "myapp01" has been upgraded. Happy Helming! ... --- # Source: playground/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: myapp01-playground data: default.json: | 1.json: | {"name":"File 1"} 2.json: | {"name":"File 2"} 3.json: | {"name":"File 3"} 4.json: | {"name":"File 4"} 5.json: | {"name":"File 5"} default.json: "{\n\t\"network\": {\n\t\t\"port\": \"80\"\n\t},\n\t\"environment\": {\n\t\t\"SSL\": false\n\t}\n}\n" --- ... |
今度は、OKですね。
secret.yamlも書き換えてみましょう。
1 2 3 4 5 6 7 8 9 |
apiVersion: v1 kind: Secret metadata: name: {{ include "playground.fullname" .}} type: Opaque data: # APIKEYをこのまま載せるのはセキュリティ的にアウト。これはあくまでサンプル # APIKEY: {{ "8oZ73ujfLImshH6xaf8wypGd8o6kp1U0NPSjsnQ6wvUt4FaLLO" | b64enc }} {{ (.Files.Glob "config/*").AsSecrets | indent 4 }} |
AsSecretsを使ってます。ニュアンス的に、セキュアにしてくれる感がビンビンですよね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<meta charset="utf-8">$ helm upgrade myapp01 . --dry-run kind: Secret metadata: name: myapp01-playground type: Opaque data: # APIKEYをこのまま載せるのはセキュリティ的にアウト。これはあくまでサンプル # APIKEY: OG9aNzN1amZMSW1zaEg2eGFmOHd5cEdkOG82a3AxVTBOUFNqc25RNnd2VXQ0RmFMTE8= 1.json: eyJuYW1lIjoiRmlsZSAxIn0K 2.json: eyJuYW1lIjoiRmlsZSAyIn0K 3.json: eyJuYW1lIjoiRmlsZSAzIn0K 4.json: eyJuYW1lIjoiRmlsZSA0In0K 5.json: eyJuYW1lIjoiRmlsZSA1In0K default.json: ewoJIm5ldHdvcmsiOiB7CgkJInBvcnQiOiAiODAiCgl9LAoJImVudmlyb25tZW50IjogewoJCSJTU0wiOiBmYWxzZQoJfQp9Cg== |
OKですね。ちゃんとエンコードされてます。
1 2 |
$ base64 -d <<< eyJuYW1lIjoiRmlsZSA1In0K {"name":"File 5"} |
次回
次回も引き続き、Helmのテンプレートについて学んでいきましょう!
コメントを残す
コメントを投稿するにはログインしてください。