コンテンツにスキップ

関数@チャート

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. 変数

予約された変数

デプロイツール内 (例:ArgoCD、Flux) でHelmを使用する場合、そのツールの実行環境に応じたバージョン値になる。

変数名 デフォルト値
.Release.Name リリース名
.Release.Time リリースした時間
.Release.Namespace リリースされる名前空間
.Release.Service v3の場合Helmv2 リリースに使用したツール名
.Release.Revision リリースのリビジョン番号
.Release.IsUpgrade upgradeかrollback操作の場合、trueがセットされる
.Release.IsInstall install操作の場合、trueがセットされる
.Values valuesファイルもしくはユーザーから-fで指定されたファイルからテンプレートに渡される値。変数名は任意で定義。「Values.変数キー名」で変数の値を取得可能
.Chart.ApiVersion Chart.yamlに記載されるチャートAPIのバージョン
.Chart.Name Chart.yamlに記載されるチャートの名前
.Chart.Version Chart.yamlに記載されるチャート自体のバージョン
.Chart.KubeVersion Chart.yamlに記載される互換性のあるKubernetesバージョン
.Chart.Description Chart.yamlに記載されるチャートの概要一文
.Chart.Home Chart.yamlに記載されるサイトのURL
.Chart.Sources Chart.yamlに記載されるソースコードのURL
.Chart.Maintainers Chart.yamlに記載されるチャートのメンテナーの名前とEmail(配列)
.Chart.engine Chart.yamlに記載されるテンプレートエンジン
.Chart.Icon Chart.yamlに記載されるアイコンのURL
.Chart.AppVersion Chart.yamlに記載されるコンテナアプリのバージョン
.Chart.Deprecated Chart.yamlに記載されるチャートの推奨/非推奨(boolean値)
.Capabilities.APIVersions Kubernetesリソースのバージョン
.Capabilities.APIVersions.Has そのkube-apiserverのバージョンで、そのAPIバージョンを使用可能かどうか。例えば、EndpointSliceは特定のバージョン以降でしか使用できない。
- https://github.com/prometheus-community/helm-charts/blob/0e2aa2e9c47e3eb5fadbb0186808bc5996001afe/charts/kube-prometheus-stack/templates/prometheus-operator/clusterrole.yaml#L82-L91
.Capabilities.KubeVersion コンテキストのKubernetesバージョン
.Template.Name カレントパスの相対ファイルパス
.Template.BasePath チャートのtemplatesディレクトリの相対パス


ローカルスコープの変数

同じファイル内で使用できる変数を定義する。

{{- $domain := "https://{{ .Values.serviceName }}.argocd.com" }}


条件内スコープの変数

条件分岐で定義した変数は、{{- if }}から{{- end }}までしか使用できない。

{{- if .Values.isProduction }}

  {{- $prefix := "prd" }}

  ... # 変数を使用する。

{{- else }}

  {{- $prefix := "nonprd" }}

  ... # 変数を使用する。

{{- end }}

条件数が2`個しかない場合であれば、三項演算子の結果を変数に格納できる。

そのため、{{- if }}から{{- end }}の外側でも変数を使用できる。

# 実行環境はdevかprdしかないものとする。
# .values.environmentが "prd" の場合はprintf関数でサブドメインを作成し、それではない場合は空文字とする
{{- $subDomain := ternary (printf "%s." .values.environment ) "" .values.environment "prd" }}
url: https://{{ $subDomain }}{{.Values.serviceName }}.com


グローバルスコープの変数

テンプレートの関数 (例:includetemplate) 、_helpers.tplファイルで定義する。


02. Helmのコメントアウト

Helmのテンプレート内にコメントアウトを定義する。

*/}}にはスペースを含めずに、一繋ぎで定義する。

YAMLのコメントアウト (例:#) であると、テンプレートの出力時に、YAMLのコメントアウトとしてそのまま出力されてしまうため、注意する。

Helmのコメントの前に不要な改行が挿入されないように、{{-とする方が良い。

{{- /* コメント */}}

もしコメントの後にも改行が挿入されてしまう場合は、-}}も付ける。

{{- /* コメント */-}}

*/}}にはスペースを含めずに、一繋ぎで定義する。


03. テンプレート作成の関数

テンプレート作成の関数とは

templateディレクトリ配下のテンプレートを出力する。


include

▼ includeとは

define関数で定義したテンプレートを加工して出力する。

加工内容はパラメーターで設定できる。


template

▼ templateとは

define関数で定義したテンプレートをそのまま出力する。

template関数では出力内容を変数に格納できないため、これが可能なinclude関数が推奨である。


04. valuesファイルの関数

Values

▼ Valuesとは

valuesファイルの特定のキー値を出力する。

特定の条件下で、valuesファイルを2階層以上に設定できなくなる現象の理由がわかっていない...。

# valuesファイル
global:
  env: prd
  appName: foo
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.global.env }}-{{ .Values.global.appName }}-pod
  labels:
    app.kubernetes.io/name: {{ .Values.global.appName }}

▼ metadataキーで使用する場合の注意点

マニフェストの`.metadata.キーの値には文字列しか設定できない。

valuesファイルから出力した値が数字の場合、Helmは勝手にint型に変換しようとする。

そのため、metadataキーの値にint型を出力しようとしてエラーになってしまう。

int型にならないように、valuesファイルの出力先をダブルクオーテーションで囲うと良い。

# valuesファイル
metadata:
  labels:
    # マニフェストで、int型で出力しようとする。
    id: "1"
apiVersion: apps/v1
kind: Deployment
metadata:
  name: foo
  labels:
    # int型にならないように、ダブルクオーテーションで囲う。
    id: "{{ .Values.metadata.labels.id }}"


default

▼ defaultとは

出力された値が空文字 ("") やfalseの場合に、それを上書きしてデフォルト値として出力する。

キー自体は存在しなければならず、省略することはできないことに注意する。

{{.Values.foo | default "foo"}}

▼ キーが存在しなくてもデフォルト値を表現

isFooキーが存在し、またtrueだった場合にマニフェストを出力する。

これにより、キーが存在しなくともfalseが設定されているように振る舞える。

foo:
  isFoo: "true"
  params: FOO
bar:
  params: BAR
{{- if hasKey .Values.foo "isFoo" }}
{{- if eq .Values.foo.isFoo true }}
  ...
{{- end }}
{{- else }}
  ...
{{- end }}



toYaml

▼ toYamlとは

出力されたデータをそのままの形で出力する。

valuesファイルにmap型やlist型をそのまま出力する場合に使用する。

▼ map型の場合

map型を出力する。

# valuesファイル
parameters:
  foo: FOO
  bar: BAR
apiVersion: v1
kind: Secret
metadata:
  name: foo-secret
data: {{- toYaml .Values.parameters | nindent 2}}

▼ list型の場合

list型を出力する。

# valuesファイル
containers:
  - name: foo
    image: foo:latest
apiVersion: v1
kind: Pod
metadata:
  name: foo-pod
spec:
  containers: {{- toYaml .Values.containers | nindent 4}}


05. キーパスの関数

キーパスの関数とは

テンプレートやvaluesファイルを出力する時に、特定のキーパスにリクエストを送信する。


. (ドット)

▼ ドットとは

テンプレートの内容をルートから出力する。

{* tplファイル *}

{{- define "foo-template" }}

- foo: FOO
  bar: BAR

{{- end }}
baz:
{{- include "foo-template" . }}
# 結果
baz:
  - foo: FOO
    bar: BAR


$ (ドル)

▼ ドルとは

出力時に、yamlファイルのルートを明示的に出力する。

アクションの中でアクションで、yamlファイルのルートにリクエストしたい場合に役立つ。

*実装例*

range関数を使用すると、yamlファイルへのアクセスのルートが変わってしまう。

ルートを明示することにより、range関数内でもyamlファイルの正しいルートにリクエストを送信できるようなる。

{{- range $.Values.foo.namespaces }}
apiVersion: apps/v1
kind: Secret
metadata:
  name: {{ $.Values.global.env }}-foo-secret
  namespace: {{ . }}

  ...

{{- end }}


06. ループの関数

range

▼ rangeとは

同じ階層にある他のyamlファイルのキーとその値を格納し、foreach関数のように出力する。

▼ マップ型を扱う場合

マップ型を入力値として使用できる。

*実装例*

もし、以下のような平文ファイルがあるとする。

# plane.yamlファイル
foo: FOO
bar: bar

これをbase64方式で変換し、valuesファイルのconfigキー配下に定義したとする。

# valuesファイル
config: eHh4OiB5eXkKenp6OiBxcXEK

fromYaml関数を使用して、string型をmap型に変換する。

その後、range関数でキーと値を取得し、Secretのデータとして割り当てる。

{{- $decoded := .Values.config | b64dec | fromYaml }}
apiVersion: v1
kind: Secret
metadata:
  name: foo-secret
type: Opaque
data:
  {{- range $key, $value := ($decoded) }}
  {{ $key }}: {{ $value }}
  {{- end }}

*実装例*

マップ型の一番上層のキー名を反復的に取得する場合に役立つ。

config:
  foo:
    foo-sub: FOO-SUB
  bar:
    bar-sub: BAR-SUB
  baz:
    baz-sub: BAZ-SUB
{{- range $key, $value := .Values.config }}
{{- if or (eq $key "foo") (eq $key "baz") }}
apiVersion: v1
kind: ConfigMap
metadata:
  # foo-map、baz-map、を作成できる。
  # bar-mapは作成されない。
  name: {{ $key }}-map
data:
  ...
{{- end }}
{{- end }}

▼ 配列型を扱う場合

配列型を入力値として使用できる。

# valuesファイル
ipAddresses:
  - 192.168.0.1/32
  - 192.168.0.2/32
  - 192.168.0.3/32
apiVersion: v1
kind: ConfigMap
metadata:
  name: blocked-ip-addresses-config-map
data:
  # 『.』を指定し、反復的に出力する。
  ip-addresses: |
    {{- range .Values.ipAddresses }}
    - {{ . }}
    {{- end }}


with

YAMLの現在のパスを変更する。

foo:
  foo1: FOO1
  foo2: FOO2
apiVersion: v1
kind: ConfigMap
metadata:
  name: foo-config-map
data:
  # 現在のパスをfooに変更する。
  {{- with .Values.foo }}
  foo1: {{ .foo1 }}
  foo2: {{ .foo2 }}
  {{- end }}


required

▼ requiredとは

記入中...


07. データ型変換の関数

fromYaml

▼ fromYamlとは

string型をmap型に変換する。

*実装例*

もし、以下のような平文ファイルあるとする。

# plane.yamlファイル
foo: FOO
bar: bar

これをbase64方式で変換し、valuesファイルのconfigキー配下に定義したとする。

# valuesファイル
config: eHh4OiB5eXkKenp6OiBxcXEK

fromYaml関数を使用して、string型をmap型に変換する。

{{- $decoded := .Values.config | b64dec | fromYaml }}
apiVersion: v1
kind: Secret
metadata:
  name: foo-secret
type: Opaque
data:
  {{- range $key, $value := ($decoded) }}
  {{ $key }}: {{ $value }}
  {{- end }}


splitList

▼ splitListとは

string型を指定した文字で分割し、list型に変換する

url: https://github.com/hiroki-hasegawa/foo-repository.git
# printf関数を使用して、一度strig型に変換している。
{{- printf "%s" .Values.url | splitList "/"}}
# [https:  github.com hiroki-hasegawa foo-repository.git]


08. string型の関数

contains

指定した文字列が含まれているかを検証する。

{{- if contains .Values.env "prd"}}
... # prd という文字が含まれている場合

{{- else}}
... # prd という文字が含まれていない場合

{{- end}}

printf

▼ printfとは

様々なデータ型をstring型で出力する。

▼ エスケープ

Helmのテンプレート内に、アクションや変数以外の理由で{}を出力する場合 (例:Alertmanagerのアラートの変数出力の定義) 、これらとして認識されないようにエスケープする必要がある。

また、エスケープする場合は必ず改行 (||-|+) で出力する必要がある。エスケープのためにprintf関数を使用することもできる。

一方で、HelmではGoのテンプレートを使用していため、これと同じエスケープの方法 (例:{{`<記号を含む文字列全体>`}}{{"<記号>"}}) を使用できる。

エスケープしたい文字列にバッククオートが含まれる場合、『{{`<記号を含む文字列>`}}』を使用できず、他のエスケープ方法 ({{"<記号>"}}printf関数) が必要になる。

# Helmのテンプレート

# Alertmanagerの通知内容の定義は以下を参考にした。
# https://www.infinityworks.com/insights/slack-prometheus-alertmanager/

---
receivers:
  - name: slack_webhook
    slack_configs:
      - channel: prd
        send_resolved: "true"
        api_url: https://hooks.slack.com/services/*****
        # 波括弧 ({}) をエスケープするために、『{{``}}』とprintfを使用している。
        # エスケープする場合は、必ず改行で出力する必要がある。
        text: |
          {{`{{ range .Alerts }}`}}
          {{`*Summary:* {{ .Annotations.summary }}`}}
          {{ printf "*Severity:* `{{ .Labels.severity }}`" }}
          {{`*Description:* {{ .Annotations.description }}`}}
          *Details:*
          {{ printf "{{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}`" }}
          {{`{{ end }}`}}
          {{`{{ end }}`}}


trimSuffix

▼ trimSuffixとは

string型から指定した文字を削除し、再取得する。

url: https://github.com/hiroki-hasegawa/foo-repository.git
# printf関数を使用して、一度strig型に変換している。
{{- $list := printf "%s" .Values.url | splitList "/" }}

# [https:  github.com hiroki-hasegawa foo-repository.git]


# リポジトリ名のみを取得する。
{{- $repositoryName := last $list | trimSuffix ".git" }}

# foo-repository


08-02. list型の関数

last

▼ lastとは

list型の最後を取得する。

lists:
  - foo
  - bar
  - baz # これのみを取得する
{{.Values.lists | last}}
# baz


09. セキュリティに関する機能

b64enc

▼ b64encとは

base64方式でエンコードする。

Secretの.dataキーでは、他のKubernetesリソースへの出力時に自動的にbase64方式でデコードするようになっており、相性が良い。

*実装例*

# valuesファイル
username: hiroki-it
password: pass
apiVersion: v1
kind: Secret
metadata:
  name: foo-secret
data:
  # base64方式でエンコードする。
  username: {{.Values.username | b64enc}}
  password: {{.Values.password | b64enc}}

*実装例*

リポジトリの認証情報を管理するSecretを繰り返し作成する。

{{- range .Values.github.repositories }}
{{- /*
  URLからリポジトリ名を抽出する
  Kubernetesのリソース名では一部の文字や記号を使用できないため、これに対処する
  (例) "https://github.com/argoproj/argo-cd.git" から "argo-cd" のみを取得する
*/}}
{{- $repositoryName := printf "%s" . | splitList "/" | last | trimSuffix ".git" }}
{{- $name := regexReplaceAllLiteral "_" $repositoryName "-" }}


{{- /*
  URLのbase64方式エンコード値を取得する
  Kubernetesのリソース名では一部の文字や記号を使用できないため、これに対処する
  (例) "https://github.com/argoproj/argo-cd.git" から "ahr0chm6ly9naxrodwiuy29tl2fyz29wcm9ql2fyz28ty2quz2l0" を取得する
*/}}
{{- $lowerbase64EncodedUrl := . | b64enc | lower }}
{{- $id := regexReplaceAllLiteral "[^a-z0-9]" $lowerbase64EncodedUrl "" }}


{{- /*
  Secretの名前が必ず一意になるように、リポジトリ名とURLエンコード値で命名する
  (例) "https://github.com/argoproj/argo-cd.git" のSecretの名前は "argo-cd-ahr0chm6ly9naxrodwiuy29tl2fyz29wcm9ql2fyz28ty2quz2l0" になる
*/}}


apiVersion: v1
kind: Secret
metadata:
  name: foo-{{ $name }}-{{ $id }}
data:
  # base64方式でエンコードする。
  username: {{.Values.github.username | b64enc}}
  password: {{.Values.github.password | b64enc}}

{{- end }}

▼ 変化しない一意な名前

セキュリティではなく、変化しない一意な名前のKubernetesリソース (特にConfigMap、Secret) を作成できるように、接尾辞にbase64方式のエンコード値を使用する。

# valuesファイル
repositoryUrls:
  - https://github.com/argoproj/argo-cd
{{- range .Values.repositoryUrls | b64enc }}
apiVersion: v1
kind: ConfigMap
metadata:
  # 名前が一意になるようにする。
  name: foo-config-map-{{ . | b64enc }}
data:
  url: {{ . }}
{{- end }}


genCA

▼ genCA

SSL証明書とペアになる秘密鍵を作成する。

SSL証明書は.Cert、秘密鍵は.Keyでリクエストできる。

{{- $ca := genCA "foo-ca" 3650 }}
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
  name: foo-webhook
webhooks:
    clientConfig:
      # SSL証明書を出力する
      caBundle: {{ $ca.Cert | b64enc }}

...


sha256sum

▼ sha256sumとは

入力内容をハッシュ値に変換する。

SecretとConfigMapの設定値を変更した場合に、Podを配下にもつKubernetesリソース (例:Deployment、StatefulSet、DaemonSet) では、Podを再作成する必要がある。

これらのKubernetesリソースのPodTemplateの.metadata.annotationsキーにて、テンプレートの出力をsha256sumに入力する。

これにより、SecretとConfigMapを変更した場合に、ハッシュ値が変更される。

そのため、PodTemplateが変更されたことになり、Podも再作成できるようになる。

*実装例*

apiVersion: apps/v1
kind: Deployment
metadata:
  name: foo-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app.kubernetes.io/name: foo-pod
  template:
    metadata:
      annotations:
        checksum/secret: "{{ include (print $.Template.BasePath '/foo-secret.yaml') . | sha256sum }}"
        checksum/configmap: "{{ include (print $.Template.BasePath '/foo-configmap.yaml') . | sha256sum }}"
    spec:
      containers:
        - name: app
          image: app:1.0.0
          ports:
            - containerPort: 8080
          envFrom:
            - secretRef:
                name: foo-secret
            - configMapRef:
                name: foo-secret


10. インデントの関数

- (ハイフン)

{{-

{{-であると、テンプレートの出力時にこれより前のインデントを削除する。

*実装例*

{* tplファイル *}

  {{- define "foo-template" }}

- foo: FOO
  bar: BAR

  {{- end }}
baz: {{- include "foo-template" .}} # 『{{-』の前にあるインデントは削除される。
# 結果
baz:
  - foo: FOO
    bar: BAR

-}}

-}}であると改行コードを削除し、不要な改行が挿入されないようにする。

Helmの関数の中には処理結果に改行コードを挿入するものがあるため、これを削除したい場合に役立つ。

ただし基本的には、-}}は使用しない方が良いらしい。


indent

▼ indentとは

改行せずに、そのままスペースを挿入した上で、内容を出力する。

nindent関数とは、改行しない点で異なる。

*実装例*

{* tplファイル *}

{{- define "foo-template" }}

- foo: FOO
  bar: BAR

{{- end }}
# 2つ分のスペースを挿入した上で、出力する。
baz:
{{- include "foo-template" . | indent 2 }}
# 結果
baz:
  - foo: FOO
    bar: BAR


nindent

▼ nindentとは

改行しつつ、スペースを挿入した上で、内容を出力する。

indent関数とは、改行する点で異なる。

*実装例*

{* tplファイル *}

{{- define "foo-template" }}

- foo: FOO
  bar: BAR

{{- end }}
# 改行しつつ、2つ分のスペースを挿入した上で、出力する。
baz:
{{- include "foo-template" . | nindent 2 }}
# 結果
baz:
  # ここで改行が入る
  - foo: FOO
    bar: BAR


11. 検証の関数

hasKey

▼ hasKeyとは

指定したキーが存在する場合、trueを返却する。

キーが存在する場合にのみ、そのキー配下の構造を使用するような場面で役立つ。

一方で、別途enabledキーを用意するのもありである。

foo:
  baz:
    - FOO
    - BAR
foo:
{{- if hasKey .Values.foo "baz" }}
  baz:
    {{- range .Values.foo.baz }}
    - {{ . }}
    {{- end }}
  ...
{{- end }}

▼ indexでも代用可

index関数でhasKey関数を代用できる。

指定した文字列が存在する場合、trueを返却する。

foo:
  baz:
    - FOO
    - BAR
foo:
  {{- if (index .Values.foo "baz") }}
  baz:
    {{- range .Values.foo.baz }}
    - {{ . }}
    {{- end }}
  ...
{{- end }}


12. 条件分岐の関数

単一条件

eq演算子を使用する。

{{- if eq .Values.enableFoo true }}
  ...
{{- end }}


AND条件

and演算子と()記号を使用する。

{{- if and (eq .Values.enableFoo true) (eq .Values.enableBar true) }}
  ...
{{- end }}


OR条件

or演算子と()記号を使用する。

{{- if or (eq .Values.global.env "tes") (eq .Values.global.env "stg") }}
  ...
{{- end }}


NOT条件

ne演算子 (not equalの略) を使用する。

{{- if ne .Values.global.env "tes" }}
  ...
{{- end }}


13. ファイル系

Files

ファイルから内容を取得する。

.Files.Glob関数で複数のファイルをmap型で取得できる。

{{- range $filePath, $_ := .Files.Glob "dashboards/*.json" }}
{{- $dashboardName := regexReplaceAll "(^.*/)(.*)\\.json$" $path "${2}" }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-dashboard-{{ $dashboardName }}
  namespace: prometheus
  labels:
    grafana_dashboard: "1"
data:
  {{ $dashboardName }}.json: {{ $.Files.Get $filePath }}
---
{{- end }}

- - https://helm.sh/docs/chart_template_guide/function_list/#file-functions - https://helm.sh/docs/chart_template_guide/accessing_files/ - https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack/templates/grafana/dashboards-1.14 - https://stackoverflow.com/questions/64662568/how-can-i-use-a-json-file-in-my-configmap-yaml-helm - https://github.com/helm/helm/issues/4515#issuecomment-415303665


14. 例外処理系

fail

エラーメッセージを含む例外をスローし、意図的に処理を失敗させる。

他のメンバーに設計方針を強制したり、必須値を定義する場合に使用する。

{{- if not (contains .Values.foo $bar) }}
{{- fail "〇〇であると△△のため、barにはfooを含むようにしてください" }}
{{- else }}
...
{{- end }}