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

KCA — Kyverno認定アソシエイト

リクエストはどこで止まり、Kyvernoはどこに立っているのか

TT Labで続きを見る

一言でいうと

kubectl applyを1回実行すると、APIサーバーの中で6つの停留所を通ります。認証 → 認可 → mutating admission → スキーマ検証 → validating admission → etcdへの保存です。Kyvernoのmutateは3つ目の停留所に、validateは5つ目に位置します。この順序を知らないと、自分のポリシーに自分が引っかかります。

なぜ必要なのか

クラスターにルールを強制する方法は、もともと2つでした。1つはRBACで「誰が何をできるか」を制限すること、もう1つはレビューで人がマニフェストを見ることです。ところが、実務の要求の大半はその中間にあります。開発者がDeploymentを作ること自体は許可しなければなりませんが、そのDeploymentがリソースリミットなしで起動したり、latestタグを使ったり、privilegedで動いたりするのは止めなければなりません。RBACは動詞とリソースの種類までしか見ず、中身は見ないので、この要求を表現できません。そこでAPIサーバーは、認可の次に「リクエストの内容を見て判断する」拡張ポイントを用意しました。それがアドミッションWebhookです。

停留所に順序があるのにも理由があります。認証と認可を先に行うのは、身元もわからないリクエストに重い処理をしないためです。mutatingがvalidatingより前にあるのは、「直してから検査する」という流れが自然だからです。デフォルト値を埋めるWebhookが検査より後ろにあると、誰も通過できません。

どう動くのか

この順序から、実務上の結論がすぐに導かれます。validateルールが見るオブジェクトは、すでにmutateルールが手を加えたあとのオブジェクトです。サイドカーを注入するmutateポリシーと、すべてのコンテナにリソースリミットを求めるvalidateポリシーを同時に適用すると、注入されたサイドカーもリミットの検査対象になります。注入するスペックにリミットを入れていなければデプロイが止まり、そのときログには、ユーザーが書いてもいないコンテナ名が出力されます。サイドカー注入ポリシーにリソースリミットを一緒に入れるのは、好みの問題ではなく要件です。

Kyvernoは3つのコントローラーで構成され、それぞれ役割が違います。

コントローラー いつ動くか 何をするか
admission リクエストが届いたときにリアルタイム mutateとvalidateをWebhookで評価して応答します
background 定期的に、およびUpdateRequestが作られたとき 既存のリソースを走査し、generateルールの実際の生成と同期を行います
reports 評価結果がたまったとき PolicyReport / ClusterPolicyReportオブジェクトを作成して集計します

3つに分かれているのは、性格が違うからです。admissionはミリ秒以内に答えなければならず、backgroundは数分かかってもかまわず、reportsは書き込みが多くなります。1つのプロセスに入れると、レポートの集計が遅れたときにデプロイまで一緒に遅れます。generateルールがadmissionでリソースを直接作らず、UpdateRequestを残してからbackgroundが処理するのも、同じ理由です。

最後にfailurePolicyです。デフォルト値はFailで、これはWebhookが時間内に応答できなければリクエストを拒否するという意味です。セキュリティの観点では正しいデフォルト値ですが、可用性の観点では重い意味を持ちます。ワイルドカードですべてのリソースにマッチするポリシーを適用すると、すべてのリクエストがWebhookを通るようになってクラスター全体に遅延の負担がかかり、デフォルトのタイムアウト内に答えられなくなった瞬間にリクエストが拒否されます。つまり、Kyvernoが遅くなるとクラスターが遅くなるのではなく、クラスターが止まります。マッチを必要な種類とネームスペースに絞る作業は、パフォーマンスチューニングではなく可用性の作業です。Webhookのタイムアウトのデフォルト値は10秒で、許されるのは1–30秒の範囲だけです。

現場での姿

著者のホームラボは、コントロールプレーン3台とGPUワーカー4台で構成された7ノードのクラスターで、CNIはCilium 1.20 eBPFを使い、kube-proxyなしで動いています。ここで繰り返し確認した教訓が、「状態がReadyであることと、実際に動作することは別の命題だ」ということです。KubeVirtはコンポーネントがすべてAllComponentsReadyだったのにVMが起動せず、原因はvirt-launcher Podの仕様でボリュームマウントが抜けていたことでした。Gateway APIはCRDをv1.2にしておいたところ、tlsroutesとreferencegrantsがv1ではないとしてコントローラーが起動を拒否し、v1.6.1に上げなければなりませんでした。

アドミッションポリシーにもまったく同じ落とし穴があります。ポリシーオブジェクトが存在し、状態が正常に見えるからといって、そのポリシーが実際にリクエストを処理しているとは限りません。Webhook設定が登録されていないか、matchブロックが何も選んでいなければ、ポリシーは一覧に普通に表示されながら何もしません。そして、何にもマッチしないポリシーは、きちんと動いているポリシーと目では区別できません。これがこのコースで繰り返し強調する診断の原則であり、あとで学ぶローカル検証の習慣がその答えです。

次のクイズで確認すること

このモジュールは概念だけを扱います。次のモジュールからClusterPolicyを実際に書き始め、同じルールをKubernetes組み込みのValidatingAdmissionPolicyでも立てて、2つのアプローチを並べて比較します。