コンテンツにスキップ

関数@チャート

はじめに

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


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" }}


変数のスコープ

条件分岐の前に := で値を代入し、条件分岐内で = でこれを代入する。

これにより、条件分岐の外に変数のスコープを広げられる。

# 変数を定義する
{{- $prefix := "" }}

{{- if .Values.isProduction }}

  # 再代入する
  {{- $prefix = "prd" }}

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

{{- else if }}

  # 再代入する
  {{- $prefix = "stg" }}

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

{{- else }}

  # 再代入する
  {{- $prefix = "dev" }}

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

{{- 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 if }}
...
{{- 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 関数で囲う場合は、区切り記号 (---) を入れる
{{- 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 関数で囲う場合は、区切り記号 (---) を入れる
{{- 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 関数で囲う場合は、区切り記号 (---) を入れる
{{- range .Values.repositoryUrls | b64enc }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  # 名前が一意になるようにする。
  name: foo-config-map-{{ . | b64enc }}
data:
  url: {{ . }}
{{- end }}


genCA

▼ genCA

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

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

{{- $ca := genCA "foo-ca" 3650 }}
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
  name: foo-webhook
webhooks:
    clientConfig:
      # サーバー証明書を出力する
      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.Get 関数でファイルから取得できる。

# マニフェスト全体を range 関数で囲う場合は、区切り記号 (---) を入れる
{{- range $file, $_ := .Files.Glob "dashboards/*.json" }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-dashboard-{{- base $file | replace ".json" "" }}
  namespace: prometheus
  labels:
    grafana_dashboard: "1"
data:
  {{ base $file }}:
{{ tpl ($.Files.Get $file) $ | toYaml | indent 4 }}
{{- 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 }}