チャート — マニフェストの束ではなくパッケージだ
一言でいうと
チャートは、YAMLを集めたフォルダーではなく、バージョンとデフォルト値と使い方を一緒に収めたパッケージです。
なぜ必要なのか
最初は、kubectl apply -f deployment.yaml1つで十分です。ところが、開発・ステージ・本番の3つの環境ができた瞬間、ファイルが3つに分かれます。最初はレプリカ数だけが違いますが、半年が過ぎると、3つのファイルは互いに別の生き物になっています。本番にだけ付いているアノテーション、開発にだけ残った古いイメージタグ、どちらが正しいのか誰にもわからないリソース制限。ここでの本当の問題は、ファイルが3つあるという事実ではなく、この中のどれを変えてよい値なのかが、どこにも書かれていないという点です。
チャートは、その問いに構造で答えます。変えてよい値はvalues.yamlに、変わってはいけない形はtemplates/に、この一式が何で何番目のバージョンなのかはChart.yamlに書きます。3つの場所の役割が違うという事実そのものが、ドキュメントになります。新しく来た人がvalues.yamlだけを開けば、「自分が触れるノブ」がすべて見えます。
どう動くのか
チャートのディレクトリには、場所ごとに決まった意味があります。
| 場所 | 何か |
|---|---|
Chart.yaml |
チャートの身分証。名前、バージョン、appVersion、依存関係 |
values.yaml |
ユーザーが読んで直す唯一のインターフェース。デフォルト値 |
templates/ |
レンダリングされてマニフェストになるファイル |
templates/_helpers.tpl |
アンダースコアで始まる。マニフェストではなく、名前付きテンプレートの定義だけを入れる |
templates/NOTES.txt |
インストール直後に人が読む案内文 |
charts/ |
依存チャート(サブチャート)が置かれる場所 |
crds/ |
installのときだけ適用され、upgradeとuninstallでは触れない特別な領域 |
.helmignore |
パッケージングから除くもの |
Chart.yamlでよく混乱するのが2つのバージョンです。versionはチャート自体のSemVerで、テンプレートやデフォルト値の構造を変えたときに上げます。appVersionは、そのチャートがデプロイするアプリケーションのバージョンで、イメージタグのデフォルト値やapp.kubernetes.io/versionラベルに流れ込みます。2つは独立して動きます。アプリはそのままなのに、ラベルを1つ直すためにチャートのバージョンだけが上がることは、ごく正常です。apiVersionはHelm 3では必ずv2で、typeは、実際にリソースを作るapplicationと、名前付きテンプレートだけを提供するlibraryのどちらかです。
ラベルには、Kubernetesの公式標準があります。app.kubernetes.io/name、instance、version、managed-by、そしてhelm.sh/chartです。これらのラベルを手で繰り返し書くと、必ずずれるので、_helpers.tplの1か所にまとめます。ここで重要な設計が1つ出てきます。共通ラベルとセレクターラベルは必ず分離しなければなりません。Deploymentのspec.selectorは、作ったあとに変更できないフィールドですが、共通ラベルにはチャートのバージョンとアプリのバージョンが混ざっています。セレクターにそれをそのまま使うと、チャートのバージョンを上げた瞬間にセレクターが変わり、アップグレードがAPIサーバーに拒否されます。そのため、セレクターには、決して変わらない2つ(name、instance)だけを入れます。
現場での姿
1つ目は、63文字の壁です。リソース名は、たいていrelease名とチャート名をつなげて作ります。チームでpayments-api-canary-eu-westのようなrelease名を使い始めると、ある日突然、名前がルールを超えてインストールが失敗します。そのため、名前のヘルパーには、慣例としてtrunc 63とtrimSuffix "-"が付いています。切り落としたあとにハイフンで終わると、それも有効ではないからです。
2つ目は、コメントのないvalues.yamlです。チャートを使う人が読むのは、テンプレートではなくvalues.yaml1つです。ここにコメントがないと、ユーザーは「この値を変えると何が起きるのか」を、テンプレートを調べて突き止めなければなりません。値の名前をうまく付け、コメントを書くほうが、ドキュメントを別に書くよりはるかに長持ちします。
3つ目は、lintは構文チェッカーではないということです。helm lintはYAMLが壊れていないかも見ますが、実際には、慣例を守っているかをより多く見ます。アイコンがない、推奨ラベルが抜けているといった警告がほとんどです。警告を無視する習慣がつくと、本当のエラーがその中に埋もれます。
チャートを他の人に渡すときに備えるべきこと
チャートを自分のチームだけが使うときは、雑に作っても動きますが、他の人が使い始めると、約束したことのないものに依存されるようになり、変更できなくなります。最初にいくつか決めておけば、その問題が減ります。
values.yamlがそのままドキュメントです。すべてのキーにデフォルト値を書いておけば、使う人は何を変えられるかを、ファイル1つでわかります。コメントで単位と許可される値を書きます。なくてもよいキーを最初から省いておくと、使う人は、そういうキーがあることさえ知りません。
values.schema.jsonで、誤った値を事前に防ぎます。型が間違っていたり、必須のキーがなかったりすると、レンダリングの前に失敗するので、クラスターに変なものが載りません。
Chart.yamlの2つのバージョンは別物です。versionはチャート自体のバージョンで、appVersionは収めているアプリケーションのバージョンです。チャートだけを直したなら、前者だけを上げます。2つを一緒に動かすと、チャートの修正とアプリのデプロイを区別できなくなります。
名前は、ヘルパー1か所で作ります。_helpers.tplのfullnameをすべてのリソースに使わせれば、release名が変わっても、リソース名が一貫してついてきます。リソースごとに名前を直接書くと、どこかは必ずずれます。
ラベルは標準に従います。app.kubernetes.io/name、instance、version、component、managed-byを付ければ、ほかのツールが認識します。そして、セレクターに使うラベルは、決して変えません。Deploymentのselectorは不変なので、変えるとアップグレードが失敗し、削除してから作り直す必要があります。
NOTES.txtに次の行動を書きます。インストール後に画面に出る唯一の案内です。接続方法、確認コマンド、よくある間違いを1つ書けば十分です。
依存関係はChart.lockで固定します。dependenciesに範囲を書いて、ロックファイルをコミットしないと、同じチャートが、昨日とは異なるサブチャートを取り込みます。
次のラボですること
/root/helm/lab/labhub-webにチャートのスケルトンを作り、Chart.yamlとvalues.yamlを自分で埋めます。名前とラベルをヘルパーとして切り出して、すべてのオブジェクトに標準ラベルが付くようにし、インストール案内文をレンダリングして保存します。最後に、値を上書きしたレンダリングとデフォルトのレンダリングを並べて、ServiceのセレクターとPodのラベルが本当に一致しているかを確認します。