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

KCA — Kyverno認定アソシエイト

generateはなぜ静かに失敗するのか

TT Labで続きを見る

一言でいうと

mutateは、admissionでオブジェクトを書き換えて返します。generateは、admissionでは何も作らずUpdateRequestを残し、そのあとbackgroundコントローラーが実際の生成を行います。この違いのため、generateは「admissionは成功するのにリソースだけができない」という、最も気づきにくい形で失敗します。

なぜ必要なのか

mutateがあるのは、検査だけでは人が疲れてしまうからです。「すべてのPodにチームラベルを付けてください」をvalidateだけで強制すると、数百のマニフェストに同じ行を入れることになり、入れ忘れたチームはデプロイが止まります。組織全体に例外なく適用される値なら、人に書かせるのではなく、システムが入れるほうが適切です。

generateは、別の問題を解きます。ネームスペースが作られた瞬間にしかわからない事実があります。新しいネームスペースにdefault-deny NetworkPolicyを入れたいなら、ネームスペースの作成イベントに反応しなければなりません。GitOpsでは「ネームスペースができたあと」というタイミングを表現しにくく、人に任せると忘れます。

どう動くのか

mutateの文法は2つです。patchStrategicMergeはKubernetesの戦略的マージパッチで、配列を名前キーでマージします。コンテナのリストにサイドカーを1つ加えるように、「既存を残して上乗せする」作業に向いています。patchesJson6902はRFC 6902のJSON Patchで、op/path/valueで正確な位置を指定します。パスがスラッシュ表記で、キーの中にスラッシュが入る場合は~1でエスケープしなければならない点が、実務でよく引っかかります。kca.io/ownerアノテーションを追加するなら、パスは/metadata/annotations/kca.io~1ownerになります。foreach mutateは、コレクションの要素ごとにパッチを適用します。

generateの文法は、data(ポリシーに値を直接書きます)とclone(別のネームスペースのリソースを複製します)の2つに分かれます。両方を同時に使うことはできません。ここにsynchronizeが加わります。trueなら、元が変わったときに生成物も一緒に変わり、生成物を手で書き換えると元に戻します。

問題は、この便利さが無料ではないことです。synchronizeは、対象のネームスペースの数だけ監視と書き込みを増やします。ネームスペースが5つのクラスターでは何も感じませんが、数百あるクラスターではbackgroundコントローラーの常時負荷になります。そして、このコントローラーは最小権限だけを持ってインストールされます。標準ではないリソースをgenerateし始めたら、そのリソースに対する権限を使う側が付与しなければならず、権限がなければadmissionは成功してリソースだけが黙って作られません。

そのため、診断の順序が決まっています。生成物が見えないときは、ポリシーのYAMLから見るのではなく、UpdateRequestを先に見ます。kubectl -n kyverno get updaterequestsが空ならmatchされていないということで、リクエストはあるのにリソースがなければ、backgroundコントローラーの権限か動作の問題です。この1回の分岐で、問題の範囲が半分になります。権限の確認は、kubectl auth can-i <동사> <리소스> --as system:serviceaccount:kyverno:kyverno-background-controller(プレースホルダーは動詞とリソースです)で行います。また、ネームスペースを作った直後にすぐ生成物を確認すると、まだ存在しない場合があることも覚えておく必要があります。これはバグではなく設計です。

最後に、mutateの運用上の代償にも触れておきます。mutateで入れた値はGitに現れません。Helm valuesに入れればレビューされ、タグでロールバックできますが、mutateで入れるとクラスターの中でしか見えません。半年後には、なぜこのアノテーションが付いているのか誰にもわからない状態になり、マニフェストと実際のオブジェクトが違うという事実が、GitOpsツールのドリフト検知と戦い続けます。組織全体に強制すべき値だけをmutateにし、チームが変更できるべき値はチャートに置くのが基準です。

現場での姿

著者のホームラボはGitOpsをArgoCDで回しており、10.0.0.201で起動しています。ここでmutateとGitOpsが衝突する様子を直接目にしますが、これは調整ループがGitのマニフェストとクラスターの実際の状態を絶えず比較するツールだからです。Kyvernoが入れたラベルやサイドカーはGitにないのでdiffに引っかかり、ツールがそれを消そうとすると、Kyvernoがまた入れます。解決策は、diffからそのパスを除外するか、mutateの代わりにチャートのデフォルト値に移すことですが、この判断を先送りすると、2つのコントローラーがお互いを元に戻し合う状態が長く続きます。

もう1つ、このクラスターで繰り返し確認したのが、権限と観測の関係です。GPU OperatorやKubeVirtの事故は、いずれも「状態表示は正常なのに実際には動作していない」という形でしたが、generateの静かな失敗もまったく同じ系統です。そのため、generateを使うポリシーをデプロイするときは、ポリシー自体より先に、backgroundコントローラーの権限を確認する手順を入れておくほうが適切です。

次のラボですること

/root/kca-mutate/に、ラベル注入とサイドカー注入のmutateルール、JSON Patchルール、NetworkPolicyを作るgenerateルール、ConfigMapを複製するcloneルールを書きます。そして、backgroundコントローラーが複製元を読み取れるように、RoleとRoleBinding、ServiceAccountを実際に作成し、auth can-iで検証します。