こんにちは。KOUKIです。
とある企業でWebエンジニアをやっています。
前回は、HelmのTemplateについて学習をしました。
今回も引き続き、HelmのTemplateについて深く学びましょう!
<目次>
検証環境
- helm v3
- mac
- minikube
Kubernetesに関しては、こちらも参考にしてください。
条件分岐
Go言語やその他のプログラミング言語のように、HelmもIf文の条件分岐が可能です。
例えば、以前作成したmyappフォルダ内のserviceaccount.yamlファイルを開いてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
{{- if .Values.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount metadata: name: {{ include "myapp.serviceAccountName" . }} labels: {{- include "myapp.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} {{- end }} |
先頭の「{{- if .Values.serviceAccount.create -}}」ですが、これはvalues.yamlのserviceAccount.create↓を見てます。
1 2 3 4 |
serviceAccount: create: true annotations: {} name: "" |
ここではtrueが定義されているので、serviceAccountは作成されます。
例えば、今の状態でデバッキングしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# myappフォルダに移動 $ cd myapp/ # もしmyappフォルダを作っていない場合は helm create myapp # デバッキング $ helm install test --dry-run . # Source: myapp/templates/serviceaccount.yaml apiVersion: v1 kind: ServiceAccount metadata: name: test-myapp labels: helm.sh/chart: myapp-0.1.0 app.kubernetes.io/name: myapp app.kubernetes.io/instance: test app.kubernetes.io/version: "1.0.0" app.kubernetes.io/managed-by: Helm |
現状は、test-myappというserviceAccountが作成されるようです。
それでは、falseに変更してデバッキングしてみましょう。
1 2 3 4 |
serviceAccount: create: false annotations: {} name: "" |
1 2 3 4 5 6 7 8 9 10 11 |
$ helm install test --dry-run . ... --- # Source: myapp/templates/tests/test-connection.yaml ... --- # Source: myapp/templates/service.yaml ... --- # Source: myapp/templates/deployment.yaml ... |
serviceAccountの項目がなくなりました!
これは直感的なので、わかりやすいと思います。
If文を自分で定義
今度は、自分でIf文を定義してみましょう。
values.yamlの以下の値を見て下さい。
1 2 3 |
service: type: ClusterIP port: 80 |
service.typeにClusterIPが指定されています。
これを条件分岐を使って、ロジックを書いてみましょう。NOTES.txtを開いて下さい。
1 2 3 4 5 6 7 |
{{- if eq .Values.service.type "ClusterIP" -}} The service type is ClusterIP {{- else if eq .Values.service.type "NodePort" -}} The service type is NodePort {{- else -}} The service type is LoadBalancer {{- end }} |
定番のif-else if-else文ですね。デバッキングしてみましょう。
1 2 3 4 |
$ helm install test --dry-run . ... NOTES: The service type is ClusterIP |
OKですね。NodePortやその他の値に変えると条件分岐で別のメッセージが表示されます。
以下は、NodePortに変更した時の例です。
1 2 |
NOTES: The service type is NodePort |
ちなみに「eq」は「==」の意味です。「ne」にすると「!=」の意味になります。
プログラムと違うのはeqやneなどが、ifの直ぐ後に来ることですね。
例えば、こんな感じです。
1 2 3 4 5 |
<meta charset="utf-8"># &&条件分岐 {{- if and eq .Values.service.type "ClusterIP" .Values.Ingress.enabled -}} # 下記のイメージ # if Values.service.type == "ClusterIP" && Values.Ingress.enabled { ... } |
書き方が若干違うので、テンプレートを作り込みすぎると保守が大変そうだなと思います。
Validation – fail
Validationと呼んでいいのかわかりませんが、failは便利そうです。
NOTES.txtに以下を書き込みます。
1 2 3 |
{{- if not .Values.important -}} {{- fail "USE THIS VALUES BECAUSE OF IMPORTANT THEM" -}} {{- end }} |
1 2 |
$ helm install test --dry-run . Error: INSTALLATION FAILED: execution error at (myapp/templates/NOTES.txt:2:4): USE THIS VALUES BECAUSE OF IMPORTANT THEM |
ご覧の通り、デバッキングが強制終了しました。
絶対設定しておきたい値とか逆に設定しておきたくない値とかを定義するときに便利ですね。
ループを学ぼう
ど定番と言えるループを学びましょう!
values.yamlのserviceブロックに以下のプロパティを追加します。
1 2 3 4 5 6 |
service: type: ClusterIP port: 80 ssl_port: 443 ssl_target_port: 443 ssl_name: https |
続いて、service.yamlに下記のプロパティを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
apiVersion: v1 kind: Service metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port }} targetPort: http protocol: TCP name: http # 追加 - port: {{ .Values.service.ssl_port }} targetPort: {{ .Values.service.ssl_target_port }} name: {{ .Values.service.ssl_name}} selector: {{- include "myapp.selectorLabels" . | nindent 4 }} |
「追加」とコメントを打ったところがそれです。
この状態でデバッキングします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ helm install test --dry-run . # Source: myapp/templates/service.yaml ... spec: type: ClusterIP ports: - port: 80 targetPort: http protocol: TCP name: http # 追加 - port: 443 targetPort: 443 name: https |
OKですね。こうやって手で一つ一つ実装してもいいのですが、ループを使えばもっと画期的な方法で実装できます。
values.yamlのserviceブロックを下記のように書き換えます。
1 2 3 4 5 6 7 8 9 |
service: type: ClusterIP ports: - number: 80 targetPort: http name: http - number: 443 targetPort: https name: https |
続いて、service.yamlも書き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
apiVersion: v1 kind: Service metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: {{- range .Values.service.ports }} - port: {{ .number }} protocol: TCP targetPort: {{ .targetPort }} name: {{ .name }} {{- end }} selector: {{- include "myapp.selectorLabels" . | nindent 4 }} |
ループを使うには、range構文を利用します。
デバッキングしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ helm install test --dry-run . # Source: myapp/templates/service.yaml ... spec: type: ClusterIP ports: - port: 80 protocol: TCP targetPort: http name: http - port: 443 protocol: TCP targetPort: https name: https selector: ... |
OKですね。かなり楽になったことがわかると思います。
Helmのベストプラクティス
Helmを触ってみて、Template(service.yamlなど)は上記のように構文を活用しつつ中身を固定化させ、values.yamlの定義で内容(Portの増減など)を書き換えていく方法がベストかなと思いました。
だからこそ「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 |
# Portを増やしたい場合はTemplateに触らずにvalues.yamlの変更だけで対応できるようにする service: type: ClusterIP ports: - number: 80 targetPort: http name: http - number: 443 targetPort: https name: https # 追加 - number: 8080 targetPort: 8080 name: admin $ helm install test --dry-run . spec: type: ClusterIP ports: - port: 80 protocol: TCP targetPort: http name: http - port: 443 protocol: TCP targetPort: https name: https - port: 8080 protocol: TCP targetPort: 8080 name: admin |
–setオプションで設定の上書き
–setオプションを使って動的に変更できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ helm install test --dry-run --set "service.ports[0].number=8000" . # Source: myapp/templates/service.yaml apiVersion: v1 kind: Service ... spec: type: ClusterIP ports: - port: 8000 protocol: TCP targetPort: name: |
書き変わりました。しかし、他のportの設定が消えましたね。。
–setオプションは完全に上書きしてしまうようですね。
index構文
先ほど見た通り、portsはオブジェクトであり複数の値を保持しています。
この値の一つを選択し、パラメーターとして設定したい場合は、「index」を使います。
services.yamlのmetadataにannotationsを追加しましょう。
1 2 3 4 5 6 7 8 9 |
apiVersion: v1 kind: Service metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} annotations: # indexでオブジェクトを指定 mainPort: {{ index .Values.service.ports 0 }} |
デバッキングしてみましょう。
1 2 3 4 5 6 7 8 |
$ helm install test --dry-run . # Source: myapp/templates/service.yaml apiVersion: v1 ... annotations: # indexでオブジェクトを指定 mainPort: map[name:http number:80 targetPort:http] |
map型で出てきましたね。修正しましょう。
1 |
mainPort: "{{ (index .Values.service.ports 0).number }}" |
numberは、portsに指定したプロパティです。
デバッキングしましょう。
1 2 3 4 5 6 7 |
$ helm install test --dry-run . # Source: myapp/templates/service.yaml ... annotations: # indexでオブジェクトを指定 mainPort: "80" |
変数の定義
変数も定義できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# 変数を定義 {{- $ports := .Values.service.ports -}}s apiVersion: v1 kind: Service metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} annotations: # 変数を使用 mainPort: "{{ (index $ports 0).number }}" spec: type: {{ .Values.service.type }} ports: # 変数を使用 {{- range $ports }} - port: {{ .number }} protocol: TCP targetPort: {{ .targetPort }} name: {{ .name }} {{- end }} selector: {{- include "myapp.selectorLabels" . | nindent 4 }} |
使いやすいですね。
range文とif文のコラボレーション
rangeとifを掛け合わせてみましょう。
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 |
{{- $ports := .Values.service.ports -}}s apiVersion: v1 kind: Service metadata: name: {{ include "myapp.fullname" . }} labels: {{- include "myapp.labels" . | nindent 4 }} annotations: mainPort: "{{ (index $ports 0).number }}" spec: type: {{ .Values.service.type }} ports: # key valueを取得できる {{- range $key, $value := $ports }} # if文を追加 {{- if and (eq ($value.number | int) 8080) (eq $value.name "admin") }} # 8080ポートが使われているときはエラーを吐いて終了 {{- fail "NO USE 8080 PORTS" }} {{- end }} - port: {{ .number }} protocol: TCP targetPort: {{ .targetPort }} name: {{ .name }} {{- end }} selector: {{- include "myapp.selectorLabels" . | nindent 4 }} |
_helpersファイル
最後に、Chartを作成するときに一緒に生成される_helpers.tplファイルも知っておいた方が良いです。
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 |
{{/* Expand the name of the chart. */}} {{- define "myapp.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 "myapp.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 "myapp.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} {{- define "myapp.labels" -}} helm.sh/chart: {{ include "myapp.chart" . }} {{ include "myapp.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* Selector labels */}} {{- define "myapp.selectorLabels" -}} app.kubernetes.io/name: {{ include "myapp.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} {{- define "myapp.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} {{- default (include "myapp.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} |
内容を見ると、Chartで共通に使うものを定義しているみたいですね。
例えばですが「{{- define “myapp.name” -}}」と定義されてます。これをNOTES.txtから読み込んでみます。
1 |
{{ include "myapp.name" . }} |
前回追加したfailをコメントアウトし、デバッキングしてみましょう。
1 2 3 4 |
$ helm install test --dry-run . NOTES: myapp |
_helperに定義しておけば、どこでも自由に使えるというわけですね。
それを確かめるべく、自分でも値を設定してみます。
_helperに以下の文言を追加して下さい。
1 2 3 |
{{- define "customValue" -}} {{- default "test" }} {{- end }} |
NOTES.txtにも追加します。
1 2 |
{{ include "myapp.name" . }} {{ include "customValue" . }} |
デバッキングをします。
1 2 3 4 5 |
$ helm install test --dry-run . NOTES: myapp test |
OKですね。言い忘れてましたが、_helperの値を読み込むときは「include」構文を使うみたいです。
次回
次回は、Helmの依存関係、ライブラリ化、レポジトリなどについて学びましょう。
コメントを残す
コメントを投稿するにはログインしてください。