TT Lab
はじめる
学ぶ 学習パス コース

Helmチャートの作成とデプロイ

テンプレート関数でvaluesを安全に差し込む

TT Labで続きを見る

目標

テンプレート関数でvaluesを安全にマニフェストへ差し込み、値がないとき・必須のとき・ブロックのときに、それぞれどんな道具を使うべきかを、手で身につけます。

なぜ重要なのか

Helmテンプレートは、YAMLエディターではなく文字列ジェネレーターです。最終結果が文字列として完成してからはじめて、YAMLパーサーがそれを読みます。そのため、インデントが2スペースずれると、エラーが出る代わりに、ブロックが丸ごと消えたり、見当違いの親の下に入ったりします。toYamlで展開したブロックにnindentを必ず付ける理由が、ここにあります。indentは前の改行を作ってくれないので、最初の行が前のキーにそのままくっつきます。値の設計も同じです。defaultは「なくてもよい値」に、requiredは「なければデプロイしてはいけない値」に使います。この2つを逆にすると、誤ったデフォルト値で静かにデプロイされるか、逆に誰にも使えないチャートになります。最後に、値の優先度は、低いものから、チャートのvalues.yaml、-fで渡したファイル群(左から右)、--setの順であり、マップは深くマージされますが、リストは丸ごと置き換えられるという点を、必ず覚えておいてください。

ステップ

  1. /root/helm/tpl/labhub-apiにチャートを作成してください(helm create labhub-apiから始めても構いません)。values.yamlのimage.repositoryはnginx、image.tagは"1.27"にし、Chart.yamlのappVersionは"1.26"にします。そのあと、helm template labhub-api /root/helm/tpl/labhub-api > /root/helm/tpl/out/base.yamlでレンダリングを保存してください。結果にDeploymentがあり、コンテナイメージが저장소:태그(プレースホルダーはリポジトリとタグです)の形式である必要があり、波括弧や<no value>が残っていてはいけません。
  2. コンテナイメージのタグを{{ .Values.image.tag | default .Chart.AppVersion }}のように書き、helm template labhub-api /root/helm/tpl/labhub-api --set image.tag="" > /root/helm/tpl/out/default.yamlを保存してください。タグを空にしたのに、イメージにはnginx:1.26のようにタグが付いている必要があり、デフォルト値としてlatestを使ってはいけません。
  3. values.yamlにingress.enabled: trueとingress.host: api.labhub.localを置き、/root/helm/tpl/labhub-api/templates/ingress.yamlで、ホストをrequiredで囲んでください(例: {{ required "ingress.host 를 반드시 지정하세요" .Values.ingress.host }}。韓国語の文は「ingress.hostを必ず指定してください」という意味です)。そのあと、helm template labhub-api /root/helm/tpl/labhub-api --set ingress.host="" > /root/helm/tpl/out/required-error.txt 2>&1でわざと失敗させて、その出力を保存してください。ファイルには、レンダリングの失敗メッセージと一緒に、どのキーが欠けているかが見える必要があります。
  4. values.yamlのresourcesを、limits.cpu: 500m、limits.memory: 512Mi、requests.cpu: 100m、requests.memory: 128Miで埋め、Deploymentのコンテナで{{- toYaml .Values.resources | nindent 12 }}の形で、丸ごと渡してください。/root/helm/tpl/out/base.yamlをもう一度レンダリングすると、コンテナのresources.limits.cpuとresources.requests.memoryが見える必要があります。
  5. values.yamlにenvをマップとして置き、APP_MODE: server、LOG_LEVEL: info、TZ: Asia/Seoulの3つを定義してください。Deploymentでrange $k, $v := .Values.envを使って、コンテナのenvのリストを作ります。/root/helm/tpl/out/base.yamlのコンテナのenvはちょうど3つで、そのうちの1つの名前がLOG_LEVELである必要があります。
  6. /root/helm/tpl/labhub-api/templates/_helpers.tplに、defineでチャートの識別文字列(名前-バージョン)を作り、Deploymentのmetadata.annotationsにlabhub.io/chart: {{ include "labhub-api.chart" . }}を付けてください。そして、/root/helm/tpl/out/include-note.txtに、templateとincludeの違いを1行で書いてください。includeは結果を文字列として返すので、パイプでつないで後処理(インデント)ができる、という内容が入っている必要があります。
  7. /root/helm/tpl/values-base.yamlにreplicaCount: 2、env.LOG_LEVEL: info、image.tag: baseを、/root/helm/tpl/values-stage.yamlにreplicaCount: 4、env.LOG_LEVEL: debug、image.tag: stageを置いてください。そのあと、helm template labhub-api /root/helm/tpl/labhub-api -f /root/helm/tpl/values-base.yaml -f /root/helm/tpl/values-stage.yaml --set image.tag=cli > /root/helm/tpl/out/merged.yamlを実行してください。結果のreplicasは4、LOG_LEVELはdebug、イメージタグはcliである必要があります。最後に、/root/helm/tpl/out/precedence.txtに、優先度を低いものから1行ずつ書いてください: チャートのvalues.yaml、-f values-base.yaml、-f values-stage.yaml、--set。
  8. ConfigMapテンプレートを追加して、values.yamlのconfigマップをdataとして出力し、Podテンプレートのmetadata.annotationsにchecksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}を付けてください。Podレベルのspec.template.spec.securityContext.runAsNonRootは、trueになる必要があります。完成したレンダリングを/root/helm/tpl/out/final.yamlに保存してください(オブジェクトが3つ以上、コンテナのenvが3つ以上、<no value>なし)。そして、helm lint /root/helm/tpl/labhub-api > /root/helm/tpl/out/lint.txtも保存し、[ERROR]がない必要があります。

参考

valuesを参照してレンダリングする

/root/helm/tpl/labhub-apiにチャートを作成してください(helm create labhub-apiから始めても構いません)。values.yamlのimage.repositoryはnginx、image.tagは"1.27"にし、Chart.yamlのappVersionは"1.26"にします。そのあと、helm template labhub-api /root/helm/tpl/labhub-api > /root/helm/tpl/out/base.yamlでレンダリングを保存してください。結果にDeploymentがあり、コンテナイメージが저장소:태그(プレースホルダーはリポジトリとタグです)の形式である必要があり、波括弧や<no value>が残っていてはいけません。

イメージは、リポジトリとタグをそれぞれvaluesから取得して組み合わせます。レンダリング結果に波括弧や「」が残っていたら、参照したキーがvaluesにないという意味です。

defaultで空の値を埋める

コンテナイメージのタグを{{ .Values.image.tag | default .Chart.AppVersion }}のように書き、helm template labhub-api /root/helm/tpl/labhub-api --set image.tag="" > /root/helm/tpl/out/default.yamlを保存してください。タグを空にしたのに、イメージにはnginx:1.26のようにタグが付いている必要があり、デフォルト値としてlatestを使ってはいけません。

タグを空にしてレンダリングしても、イメージにタグが付く必要があります。代わりに使う値をChart.yamlから取得すれば、チャートとアプリのバージョンが自然に合います。latestは答えではありません。

requiredで必須の値を強制する

values.yamlにingress.enabled: trueとingress.host: api.labhub.localを置き、/root/helm/tpl/labhub-api/templates/ingress.yamlで、ホストをrequiredで囲んでください(例: {{ required "ingress.host 를 반드시 지정하세요" .Values.ingress.host }}。韓国語の文は「ingress.hostを必ず指定してください」という意味です)。そのあと、helm template labhub-api /root/helm/tpl/labhub-api --set ingress.host="" > /root/helm/tpl/out/required-error.txt 2>&1でわざと失敗させて、その出力を保存してください。ファイルには、レンダリングの失敗メッセージと一緒に、どのキーが欠けているかが見える必要があります。

必須の値が空なら、レンダリング自体が失敗する必要があります。エラーメッセージには、どのキーが欠けているかを書いてください。失敗の出力は標準エラー出力に出るので、保存するときに一緒に受け取る必要があります。

toYamlとnindentでブロックを渡す

values.yamlのresourcesを、limits.cpu: 500m、limits.memory: 512Mi、requests.cpu: 100m、requests.memory: 128Miで埋め、Deploymentのコンテナで{{- toYaml .Values.resources | nindent 12 }}の形で、丸ごと渡してください。/root/helm/tpl/out/base.yamlをもう一度レンダリングすると、コンテナのresources.limits.cpuとresources.requests.memoryが見える必要があります。

リソース制限のように丸ごと渡すブロックは、1行ずつ書きません。前に改行が必要かどうかが、2つのインデント関数の違いです。

rangeで環境変数を展開する

values.yamlにenvをマップとして置き、APP_MODE: server、LOG_LEVEL: info、TZ: Asia/Seoulの3つを定義してください。Deploymentでrange $k, $v := .Values.envを使って、コンテナのenvのリストを作ります。/root/helm/tpl/out/base.yamlのコンテナのenvはちょうど3つで、そのうちの1つの名前がLOG_LEVELである必要があります。

valuesのマップを、キーと値の2つの変数で受け取って繰り返します。値は引用符で囲むのが安全です。ちょうど3つが出力される必要があります。

名前付きテンプレートを定義してincludeする

/root/helm/tpl/labhub-api/templates/_helpers.tplに、defineでチャートの識別文字列(名前-バージョン)を作り、Deploymentのmetadata.annotationsにlabhub.io/chart: {{ include "labhub-api.chart" . }}を付けてください。そして、/root/helm/tpl/out/include-note.txtに、templateとincludeの違いを1行で書いてください。includeは結果を文字列として返すので、パイプでつないで後処理(インデント)ができる、という内容が入っている必要があります。

defineで作った断片を、アノテーションの場所に差し込みます。2つの呼び出し方のうち、パイプでつなげて書けるのはどちらか、その理由は何かを、メモに残してください。

値の優先度を確認する

/root/helm/tpl/values-base.yamlにreplicaCount: 2、env.LOG_LEVEL: info、image.tag: baseを、/root/helm/tpl/values-stage.yamlにreplicaCount: 4、env.LOG_LEVEL: debug、image.tag: stageを置いてください。そのあと、helm template labhub-api /root/helm/tpl/labhub-api -f /root/helm/tpl/values-base.yaml -f /root/helm/tpl/values-stage.yaml --set image.tag=cli > /root/helm/tpl/out/merged.yamlを実行してください。結果のreplicasは4、LOG_LEVELはdebug、イメージタグはcliである必要があります。最後に、/root/helm/tpl/out/precedence.txtに、優先度を低いものから1行ずつ書いてください: チャートのvalues.yaml、-f values-base.yaml、-f values-stage.yaml、--set。

値ファイル2つとコマンドラインのオプションを、一度に掛けてみてください。値ファイルは、渡した順序に意味があります。結果を見て、低いものから順序を書いてください。

設定ハッシュとセキュリティコンテキストまで付ける

ConfigMapテンプレートを追加して、values.yamlのconfigマップをdataとして出力し、Podテンプレートのmetadata.annotationsにchecksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}を付けてください。Podレベルのspec.template.spec.securityContext.runAsNonRootは、trueになる必要があります。完成したレンダリングを/root/helm/tpl/out/final.yamlに保存してください(オブジェクトが3つ以上、コンテナのenvが3つ以上、<no value>なし)。そして、helm lint /root/helm/tpl/labhub-api > /root/helm/tpl/out/lint.txtも保存し、[ERROR]がない必要があります。

ConfigMapの内容が変わってもPodが変わらない問題を防ぐ慣例があります。レンダリングされた設定ファイルのハッシュを、Podのアノテーションに入れてください。Podレベルのセキュリティ設定も一緒に埋めます。