この記事は、 @suin による記事、"Kubernetes: Validating Webhookの「並行性」の落とし穴" の問題が、Kyvernoでも発生しうるか調査したものです。
KyvernoのValidation Webhookの動作方式
(How Kyverno Works | Kyverno) 図: Kyverno Admission Controllerの全体構成図。KyvernoはKubernetesの動的Admission Controllerとして動作し、APIサーバからのAdmissionReview要求(バリデーションやミューテーション)をWebhookサーバで受け取り、内部のポリシーエンジン(Engine)で該当ポリシーを評価して結果(許可/拒否)を返します (How Kyverno Works | Kyverno) (How Kyverno Works | Kyverno)。図の上部はKubernetes APIサーバにおけるリクエスト処理(認証・認可、Mutating Webhook、Schema検証、Validating Webhook経由でetcdに永続化)を示し、下部の青い部分がKyvernoの各コンポーネント(Admission Controllerや各種コントローラー)です。KyvernoのAdmission Controller(Webhookサーバ)はHTTP経由でAdmissionReviewリクエストを受信し、Engineに渡してポリシー適用を行います (How Kyverno Works | Kyverno)。
KyvernoはKubernetesクラスター内で稼働するポリシーエンジンであり、Validating Admission Webhookとしての役割を持ちます (How Kyverno Works | Kyverno)。Kubernetes APIサーバでリソース作成や更新のリクエストが発生すると、APIサーバは事前に登録されたKyvernoのWebhookサービスにAdmissionReviewリクエストを送信します。KyvernoのWebhookサーバ(Admission Controller)はそのリクエストを受け取り、リクエスト内のオブジェクトやユーザ情報に基づいて適用可能なポリシー(ClusterPolicyリソース)を全て抽出します。次に、それらポリシーの各ルールを評価し、違反しているルールがあるかをチェックします。もし**“validation”ルール**(検証ルール)で違反が見つかり、かつそのポリシーの動作モードがEnforce(違反時にリソース拒否)の場合、Kyvernoはそのリソースを拒否する決定を行います。一方、違反が見つからないか、違反してもポリシーがAudit(監査モード。違反しても許可)である場合には、リソース作成を許可します。これらの判断結果はAdmissionReviewのレスポンス(AdmissionResponse)としてAPIサーバに返され、許可であればリソースはKubernetesのetcdに永続化されます。
KyvernoのValidation Webhook処理フローを整理すると以下のようになります:
- Webhookリクエスト受信: KyvernoのAdmission Controller(Webhookサーバ)は、APIサーバからのAdmissionReviewリクエストをTLS経由のHTTPコールバックで受信します。リクエストには、操作種別(CREATE/UPDATE/DELETEなど)や対象リソースの内容、新旧オブジェクト情報等が含まれます。
-
ポリシーのマッチング: Kyvernoは内部に保持する全ポリシー(ClusterPolicy)の中から、そのリクエストのリソースに適用すべきものを特定します(
match
やexclude
の条件に基づきフィルタリング)。ポリシーは起動時や更新時にKyvernoがキャッシュしており、Webhook処理時に高速に検索できるようになっています (The need for speed: optimizing Kyverno’s performance | CNCF)。 - ルールの評価: 該当する各ポリシー内のバリデーションルールを順次評価します。例えばリソースの特定フィールド値パターンチェックや他リソース参照(後述)など、ルール定義に従って現在のリソースがポリシーを満たすか検証します。Kyvernoはポリシー内でJSONパスやJMESPathを用いた高度なマッチング、条件式(if-then-else)、外部データ参照などをサポートしており (How Kyverno Works | Kyverno)、これらを組み合わせてリソースの妥当性をチェックします。
- 結果の集約と応答: 全ての適用ルールを評価し、**違反が一つでもあればAdmissionResponseを“不許可”**に設定します(違反メッセージを含む)。違反が無い場合は許可とします。加えてKyvernoは結果に応じてKubernetesイベントを発行したり、AdmissionReportリソース(ポリシー適用結果の記録)を生成します (High Availability | Kyverno)。ただしAdmissionReview自体のレスポンスは即座にAPIサーバへ返される同期処理です。
- APIサーバ側での処理継続: APIサーバはKyvernoなど全てのValidating Webhookから許可応答を受け取った場合にのみリソースを永続化します (Admission Controllers 101 | Kyverno)(いずれかが拒否すればリソース作成は失敗します)。Kyvernoから許可された場合でも、他のバリデーションWebhook(OPA/Gatekeeper等)があればそれらも同様に評価される点に注意が必要です。
なお、Kyverno自体はミューテーション(変換)ポリシーも扱いますが、Mutating Webhookはバリデーションとは別にリクエスト到達時に最初に実行されます (Admission Controllers 101 | Kyverno) (Admission Controllers 101 | Kyverno)。Mutating Webhookは設定された順序で直列に呼び出され(名前順でシリアライズ実行)、オブジェクトに対する変更を行った上で次のWebhookに渡されます (Admission Controllers 101 | Kyverno)。一方、KyvernoのValidating Webhook(および他の全てのValidating Webhook)はミューテーション完了後に並行して呼び出される設計です (Admission Controllers 101 | Kyverno)。つまり、Kyvernoのバリデーションフェーズでは他のWebhookと同時並行で動作し、かつKyverno内でも複数のリクエスト処理が同時進行し得るという点が重要です。この並行処理の仕組みと考慮事項について、次節で詳しく解説します。
リクエスト処理の並行性: KyvernoのWebhookサーバ実装はGo言語のHTTPサーバを用いており、リクエスト毎に新たなゴルーチン(軽量スレッド)が生成され処理が進みます。したがって、複数のAdmissionリクエストが同時に届いた場合でもKyvernoはそれらを直列化せず並行処理します。例えばKyvernoを高可用性のため複数レプリカで動かしている場合や、Kubernetes APIサーバが短時間に複数のリソース変更を発行した場合でも、Kyverno側ではそれぞれのリクエストハンドラが独立して実行されます。その結果、あるリソースのバリデーション処理中に別のリソースの処理が同時進行することがありえます。この動作はKubernetesのダイナミックAdmissionコントローラー全般に共通する特性で、Kyverno固有の挙動ではありません (Admission Controllers 101 | Kyverno)。
このときKyverno内部で競合状態が発生しないように、共有データには適切なロック機構が実装されています。例えば、ポリシーのキャッシュや一部設定はWebhookリクエスト処理と別スレッドで更新される可能性があるため、Kyvernoでは読取と書込の排他制御にRead-Writeミューテックス(RWMutex)を導入しています (
File kyverno.changes of Package kyverno -
openSUSE Build Service
)。実際、Kyverno 1.5以前では並行アクセスによるマップの不整合から“fatal error: concurrent map read and map write”というクラッシュが発生するバグが報告されており、Issueに対する修正で該当箇所にロック処理が追加されました (kyverno/kyverno v1.6.2 on GitHub) (
File kyverno.changes of Package kyverno -
openSUSE Build Service
)。このようにKyvernoはリクエスト間の順序制御(直列化)は行わないものの、内部状態の一貫性維持のためのスレッドセーフな実装がなされています。
2. 記事"Kubernetes: Validating Webhookの「並行性」の落とし穴"で挙げられている問題とn
"Kubernetes: Validating Webhookの「並行性」の落とし穴"では、KubernetesにおけるValidating Webhookの並行性について検証し、リソース間の相関バリデーションの信頼性に関する落とし穴を指摘しています。記事では、独自に用意したValidating Webhookサーバ(リクエスト毎に5秒間スリープして応答するよう実装)をKindクラスタ上にデプロイし、10個のリソース(ConfigMap)を同時に作成するスクリプトを実行することで実験を行っています。その結果、Webhookサーバ側のログでは全てのリクエストがほぼ同時刻に開始し、約5秒後に同時に終了していることが観測されました (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。もしWebhook処理が直列実行であれば10件で合計約50秒かかるはずですが、実際には開始から終了まで一斉に進行しており、Validating Webhookが高い並行性を持っていることが確認できたと報告されています (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。
この検証結果を踏まえ、Qiita記事ではValidating Webhookの並行動作がもたらす以下のような問題点を整理しています:
-
リソース間バリデーションの制約: 複数リソースにまたがる整合性チェック(例: 名前やフィールド値の一意性検証)は、Validating Webhookだけでは100%の信頼性を保証できません (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。例えば「クラスター内で名前がユニークであること」をチェックするWebhookを実装しても、2つの競合するリソースが同時に作成された場合、それぞれのWebhookリクエストは**「相手がまだ存在しない」状態で検証を行ってしまい**、双方とも重複を検知できずに許可されてしまう可能性があります (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。つまり、Validating Webhookのバリデーションだけではリソース間の一意性を厳密に保証することはできません。
-
競合状態(Race Condition)の発生: 上記のように並行実行されるWebhookでは、検証ロジック内で競合状態が発生し得ます (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。一例として、あるフィールド値がクラスタ全体でユニークかを確認するようなバリデーションでは、リクエストのタイミング次第で本来検出すべき重複を検出できないケース(またはその逆の一時的な不整合検知)が起こりえます。このような競合はシステムのタイミングに依存するため、テストでは再現しにくく本番環境で顕在化する恐れがあります。
-
補完的なアプローチの必要性: 上記の理由から、リソース間整合性を保証するにはWebhook単独では不十分であり、コントローラーによる補完が必要とされています (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。Qiita記事では、Validating Webhookでリアルタイムに基本的なチェックを行いつつ、別途カスタムコントローラー等で定期的に全体の状態をスキャンし、結果整合的に矛盾を検出・是正するアプローチが推奨されています (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。Kubernetesは元来「最終的な一貫性」を前提とした分散システム(CAP定理におけるAP寄り)であり、完全な直列動作よりも可用性・スケーラビリティを重視した設計になっているため (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)、この特性に合わせてAdmission Webhookと後続処理を組み合わせることが望ましいと結論付けられています。
以上の点をKyvernoに照らして考えると、KyvernoのValidation Webhookも基本的にKubernetesのWebhookの並行動作仕様に従うため、Qiita記事で指摘された並行性起因の問題はKyverno環境でも起こり得ます。すなわち、Kyvernoを用いてリソース間の関係性を検証するポリシーを記述した場合でも、同時並行なリソース作成によって検証抜け漏れが発生する可能性があります。Kyverno自体に特殊なロック機構や順序制御が実装されていない以上(前述の通り、内部データの整合性保持のみを目的にロックはありますがリクエストの直列化はしません)、この挙動はKubernetesの設計上避けられないものです (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。
例えば、Kyvernoには「Ingressのホスト名+パスの組み合わせがクラスタ内でユニークであること」を保証するポリシーのサンプルがあります (Unique Ingress Host and Path | Kyverno)(後述コード例参照)。このポリシーは新規Ingress作成時に既存のIngress一覧を取得し、同じホスト・パスを持つものが存在しないか検証するものですが、Ingressを2つ同時に作成された場合にはお互いの存在を認識できずに双方作成を許可してしまう可能性があります。実際、Qiita記事で述べられているように2つのリソースが同時にバリデーションされると競合が検出できないケースが起こり得るため (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)、Kyvernoで同種の検証ロジックを実装した場合も全く同じ並行性の課題に直面します。
3. 公式ドキュメントとソースコードの調査
Kyverno公式ドキュメントにも、Admission Webhookの動作や並行処理に関する情報が記載されています。まず、前提としてKubernetesの動的AdmissionコントローラーはMutating WebhookとValidating Webhookで挙動が異なり、後者は並行的に呼び出されることが強調されています (Admission Controllers 101 | Kyverno)。Kyvernoはまさにこの動的Admissionコントローラーとして動作しており、公式サイトの「Admission Controllers 101」の解説でも**「バリデーションフェーズでは全てのValidatingWebhookConfigurationが同時に呼び出される」と明言されています (Admission Controllers 101 | Kyverno)。したがってKyvernoのValidation Webhookも例外ではなく、他のWebhookと同様にリクエストの並行処理が行われる**設計になっています。
Kyverno自体のアーキテクチャについては、「Introduction」ドキュメント内でその概要とコンポーネントが説明されています。KyvernoはAdmissionReviewリクエストを受け取るWebhookサーバ(Admission Controller)と、ポリシー適用ロジックを実行するエンジン、そしてポリシーやWebhook設定を管理するコントローラー群から構成されています (How Kyverno Works | Kyverno)。Webhookサーバは前述の通りHTTPリクエストハンドリングにGoの標準ライブラリ(またはそれに準じたルーター)を使用しており、コード上ではリスナーを立ち上げて並行にリクエストを受け付ける実装になっています。KyvernoのGitHubリポジトリを確認すると、ListenAndServeTLS
でWebhookサーバを起動する処理がゴルーチンでラップされており、メインスレッドとは非同期にサーバループが走るようになっています (kyverno/pkg/webhooks/server.go at main · kyverno/kyverno · GitHub)。このサーバは内部でhttprouter
(高速なHTTPルーターライブラリ)を用いてエンドポイントを登録しており、/mutate
や/validate
など各Webhookパスに対応するハンドラがセットアップされています (kyverno/pkg/webhooks/server.go at main · kyverno/kyverno · GitHub) (kyverno/pkg/webhooks/server.go at main · kyverno/kyverno · GitHub)。結果として、受信したAdmissionReviewは対応するハンドラ内で処理され、その間に別のリクエストが来れば別ゴルーチンで並行して別のハンドラ処理が進む構造になっています。これはNet/httpサーバの特性上自然な実装であり、Kyverno独自に直列化するようなコードは見当たりません。
一方、ソースコード上で特筆すべきは内部データ構造の並行アクセス制御です。Kyvernoはポリシーリストや一時データをメモリ上にキャッシュしていますが、これらはKubernetesのリソース変更イベントにより随時更新されます。Webhookリクエスト処理中にも更新が入りうるため、データ不整合を防ぐ仕組みが必要です。Kyvernoのコードを見ると、ポリシーキャッシュや一部のコントローラー状態管理においてスレッドセーフな実装(例えばsync.RWMutex
によるロック)が使われています。過去のIssueでは、1.3系で--gen-workers
(バックグラウンド処理ワーカー)を増やした際に**「concurrent map read and map write」エラーでKyvernoがクラッシュする**バグ報告があり (kyverno/kyverno v1.6.2 on GitHub)、Pull Requestで該当箇所にRWMutexロックを導入する修正が行われています (
File kyverno.changes of Package kyverno -
openSUSE Build Service
)。これによりマップを並行読み書きする際の競合が解消され、以降のバージョンでは安定性が向上しました。
また、Kyverno開発チームは高負荷時のパフォーマンス改善にも注力しています。CNCFのブログ記事では、Kyverno 1.10で大規模クラスタにおけるパフォーマンス検証を行ったところ、特定の処理でゴルーチン数が一時的に4000近くまで増加しメモリスパイクを引き起こす問題が報告されています (The need for speed: optimizing Kyverno’s performance | CNCF)。調査の結果、KubernetesクライアントライブラリのEventBroadcaster(イベント送信機構)が原因でリクエスト毎にゴルーチンを生成していたため、Kyverno側で独自のイベント処理(Watcher)に置き換えて対応しました (The need for speed: optimizing Kyverno’s performance | CNCF)。このように内部で過剰な並行処理が発生しないよう最適化も行われており、大量のAdmissionReviewが発生する状況でも安定して処理できるよう改善が重ねられています。事実、Kyvernoは高可用性(HA)構成をサポートしており、Admission Controllerを複数レプリカで動作させることでスループット向上とフォールトトレランスを両立できます (High Availability | Kyverno)。HA構成時は各Podが独立にAdmissionReviewを処理するため、トラフィックは分散され全体として同時により多くのリクエストをさばけます。公式ドキュメントでも「Admission Controllerを複数レプリカにすることで可用性とスケールの両面で効果がある」と述べられており (High Availability | Kyverno)、Kyverno自体が並行処理前提のスケーラブルな設計であることが読み取れます。
最後に、Kyvernoのソースコード上で特徴的な実装として、Validating WebhookをFailurePolicy別に二種類登録している点が挙げられます。Kyvernoはvalidate.kyverno.svc-fail
(Failモード用)とvalidate.kyverno.svc-ignore
(Ignoreモード用)という2つのValidatingWebhookConfigurationエントリを動的に管理し、それぞれ適用すべきリソースを設定します ([BUG] Validation webhook fails and stops resource interactions every time kyverno Helmrelease is down · Issue #2962 · kyverno/kyverno · GitHub)。Failモードのポリシー(違反時にリクエストをブロック)は前者のWebhook経由で厳密に適用され、Ignoreモードのポリシー(違反しても許可)は後者のWebhookで処理されます。この実装により、Kyvernoは違反時の挙動が異なるポリシーを分離し、たとえIgnoreポリシーが失敗してもリソース作成自体はブロックしないようになっています ([BUG] Validation webhook fails and stops resource interactions every time kyverno Helmrelease is down · Issue #2962 · kyverno/kyverno · GitHub) ([BUG] Validation webhook fails and stops resource interactions every time kyverno Helmrelease is down · Issue #2962 · kyverno/kyverno · GitHub)。興味深いのは、一つのリソースに対してFail用とIgnore用のWebhook両方がマッチする場合には、Kubernetes APIサーバが両方のWebhookを並行に呼び出す可能性があることです。例えばあるリソースが“Fail”ポリシーと“Audit(=Ignore相当)”ポリシーの両方の対象となっている場合、Kyvernoは2つのWebhookリクエストを受け、それぞれ別ゴルーチンで処理します。そして、Fail側でどれかのポリシーに違反すれば即座に拒否応答が返されリソース作成は失敗しますが、Ignore側のWebhookも同時に実行はされています(結果として違反があれば後でPolicyReportに記録されます)。このようにFailurePolicyの違いによる複数Webhook処理も並行で進行するため、KyvernoのValidation Webhook処理全体としては非常に非同期・並列度の高い構成になっていると言えます。
4. 過去のIssueやバグレポートの確認
KyvernoのGitHub Issueを調査すると、並行性に関連する問題もいくつか報告・修正されていることがわかります。前述したように、バージョン1.3~1.5頃にはconcurrent map read and write
エラーでKyverno Podがクラッシュする不具合が報告されました (kyverno/kyverno v1.6.2 on GitHub)。これはGo言語のマップがスレッドセーフでないにも関わらず、複数のゴルーチンから同時アクセスしてしまったことが原因で、Issue #1749や#3459として報告されています。開発チームはこれらを重く受け止め、Pull Request #1755や#3462で該当箇所にロックを導入する修正を行いました (
File kyverno.changes of Package kyverno -
openSUSE Build Service
)。この修正以降、その種のクラッシュは解消されており、並行処理下での安定性が改善されています。
また、大規模環境下でのパフォーマンス問題も過去に議論されています。Issue #8829では、Ingressオブジェクトが数千件以上存在するクラスタでKyvernoのバリデーションポリシー(IngressとIstio Gatewayのホスト名衝突を検知するような複雑なポリシー)を適用したところ、Webhookのタイムアウト(デフォルト10秒)に達する事例が報告されました ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub) ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub)。このケースでは、ポリシー内で全Ingressをリストしてチェックする処理が入っており、大量のIngressを抱える環境で並行的にリクエストが来ると処理が間に合わない可能性が示唆されました。調査ではIngress一覧取得自体は数ミリ秒で完了しており別の要因が疑われましたが、結果的にKyverno側でパフォーマンス改善のための調整が行われ、Issueはクローズされています(Kyverno 1.10.xでの修正) ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub) ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub)。具体的な解決内容はログからは不明ですが、考えられる対策としてWebhookのタイムアウト値を延長したり(最大30秒まで設定可能)、不要なコンテキストデータ収集を削減するといった対応が検討されたものと思われます。
さらに、Kyvernoのバックグラウンド処理やイベント生成周りでも並行性に関連するIssueが存在します。Issue #5457では、大量のAdmissionReportが蓄積してetcd容量を逼迫する問題が報告されました(これはWebhook処理結果をレポートリソースに記録する機能に関連) (
File kyverno.changes of Package kyverno -
openSUSE Build Service
)。この問題自体は並行処理というより報告頻度の調整の話ですが、根底には高並行環境で付随する処理(レポート作成)が追いつかなくなる懸念があり、Kyvernoでは不要なレポートを出力しない改善などが行われています。加えて、#8314ではk6による50~500並行リクエスト負荷試験を自動化し、許容レイテンシや失敗率を継続的に検証する取り組みが提案されており ([Feature] Add scale automation tests using k6.io #8314 - GitHub)、プロジェクトとしても並行処理下での品質維持に注力している様子が伺えます。
総じて、Kyvernoは高並行なAdmissionリクエスト処理による不具合報告に対して素早く対応しており、現在の安定版では既知の深刻な並行性バグは解消されていると考えられます。ただし、後述するようにKubernetesの仕組みに起因する根本的な制約(リソース間チェックの信頼性など)はIssueという形で現れにくいため、注意が必要です。これらはバグではなく設計上のトレードオフであるため、ユーザー側で認識して対策を講じる必要があります。
5. Kyvernoが並行性の問題を引き起こす可能性の有無
以上を踏まえ、**KyvernoのValidation Webhook自体が並行性による問題を引き起こす可能性は基本的に「ある」と言えます。ただし、その内容は2通りに分けて考える必要があります。一つはKubernetesプラットフォーム由来の問題(設計上避けられない制約)であり、もう一つはKyverno実装上の問題(バグや性能限界)**です。
まず、前者の設計上の制約については、Qiita記事で指摘された通り**「Validating Webhookだけではリソース間の完全な一貫性を保証できない」**という点に尽きます (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。Kyvernoを使って例えば「同じ名前のリソースが二重に作られないようにする」「あるNamespaceでは他のNamespaceに依存するリソースを禁止する」等、複数リソース間のポリシーを定義した場合でも、同時実行シナリオでは意図しないすり抜けが発生する可能性があります。実例として、Kyverno提供のサンプルポリシー「Unique Ingress Host and Path」を考えてみましょう。このポリシーでは新規Ingressのspec.rules
内のホストとパスを取り出し、Kyvernoのcontext
機能でクラスタ内の全Ingress(のホストとパス一覧)を取得して比較することで一意性を検証しています (Unique Ingress Host and Path | Kyverno) (Unique Ingress Host and Path | Kyverno)。ポリシー定義の抜粋を示します:
context:
- name: rules
apiCall:
urlPath: "/apis/networking.k8s.io/v1/ingresses"
jmesPath: "items[].spec.rules[]"
validate:
message: "The Ingress host and path combination must be unique across the cluster."
foreach:
- list: "request.object.spec.rules[]"
deny:
conditions:
all:
- key: "{{ element.http.paths[].path }}"
operator: AnyIn
value: "{{ rules[?host=='{{element.host}}'][].http.paths[].path }}"
上記のように、Ingress作成時にKyvernoは/apis/networking.k8s.io/v1/ingresses
へAPI呼び出しを行い既存Ingressを取得、その中からホスト名が新規Ingressと同じものを抽出し(JMESPathでrules[?host=='...']
)、パスが重複していればdeny
でブロックするよう実装されています (Unique Ingress Host and Path | Kyverno) (Unique Ingress Host and Path | Kyverno)。このロジック自体は一見妥当ですが、同時に2つのIngressが作成された場合にはお互いがまだAPIサーバに存在しない状態でチェックが行われるため、双方とも「同じホスト・パスを持つIngressは既存に無い」と判断してしまい許可されてしまう可能性があります (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。結果としてクラスタ内に重複したIngressができてしまいます。このように、Kyvernoで高度なバリデーションを書くことである程度のリソース間チェックは可能になりますが、その完全性は保証されない点に注意が必要です。特にクラスター全体で一意性を保障するようなポリシーは、最終的な穴埋め(最終一致性の保証)を他の仕組みに任せる前提で運用するか、リスクを許容した上で使用する必要があります。
Kyvernoの挙動として興味深いのは、このIngressユニークネスポリシーのサンプルでvalidationFailureAction: Audit
(違反時も拒否せず監査)とされ、またbackground: false
(バックグラウンドスキャン無効)と設定されている点です (Unique Ingress Host and Path | Kyverno)。つまり、このポリシーは違反検出時にリソース作成自体はブロックせず、違反事実をPolicyReportに記録する運用が想定されています。おそらく、重複Ingressを完全に防ぐことは困難であるため、仮に発生してもシステムを止めないようにしつつ管理者に通知するというスタンスなのでしょう。実際、仮にEnforce(違反時ブロック)にした場合でも上述の並行シナリオではブロックが機能せず重複を許してしまうため、Auditモードとの差は結果的に「事後対応できるかどうか」だけになります。このようにクリティカルな一貫性チェックほど、KyvernoポリシーではAuditモード+バックグラウンド無効とし、必要に応じて別途手動またはコントローラーで整合性を見る、といった設定になっている例があります。Kyverno利用者も、同様のポリシーを作成する際にはEnforceに過信せず慎重に検討すべきでしょう。
次に、後者のKyverno実装上の問題や性能限界についてです。こちらは前節で述べた通り、大部分は既に対処済みですが、ユーザの運用次第では注意が必要な点があります。一つはWebhookのタイムアウトです。KyvernoのWebhookはデフォルトでtimeoutSeconds: 10
に設定されています ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub)。通常のポリシーであれば十分短い時間で応答可能ですが、ポリシーが複雑だったり参照データ量が多かったりすると10秒以内に処理が終わらないケースもあり得ます ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub) ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub)。特に複数のAdmissionリクエストが同時に殺到した場合や、外部サービス問い合わせ(イメージ署名検証などverifyImagesルールでOCIレジストリに問い合わせるケース等)を行う場合、レスポンス遅延が発生しタイムアウトによる失敗が起こる可能性があります。タイムアウトになるとそのWebhookはエラーと見なされ、FailurePolicyがFailならリクエストは拒否、Ignoreなら無視されます。KyvernoではFailモードのWebhookがタイムアウトするとクラスター上の該当リソース操作全般がブロックされてしまうため、万一頻発するようなら対策が必要です ([BUG] Validation webhook fails and stops resource interactions every time kyverno Helmrelease is down · Issue #2962 · kyverno/kyverno · GitHub)。対策としては、ポリシーの最適化(不要なAPI呼び出しを減らす、複雑なループを避ける等)やWebhookタイムアウト設定の見直し(Max 30秒まで拡大可能)、Kyvernoの水平スケーリング(Pod数増加で負荷分散)などが考えられます。実際、前述のIngress数千件環境のケースではKyverno開発者とユーザとの間でデバッグが行われ、内部でトレースログを分析することでボトルネックを特定し改善が行われました ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub) ([Bug] Webhook Timeouts: Unidentified Issues in Hostname Comparison with a Huge Number of Ingress Objects · Issue #8829 · kyverno/kyverno · GitHub)。このように、極端な負荷条件下ではKyvernoでもパフォーマンスチューニングが必要となる場合があります。公式ドキュメントの「Configuring Kyverno」には、同時処理数やリソース消費を調整するためのパラメータがいくつか紹介されているので (High Availability | Kyverno)、大規模環境で運用する際は参考にすると良いでしょう。
まとめると、KyvernoのValidation Webhookは並行処理を前提として動作しており、Kubernetesの仕組み上避けられない一貫性の制約があります。これはKyvernoに限らずGatekeeperなど他のポリシーエンジンも同様です。重要なのは、クロスリソースな制約の実装にはAdmissionWebhook単体では限界があることを認識し、必要に応じて別途コントローラーや定期バッチによる検査・修復を組み合わせることです (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。Kyverno自体もバックグラウンドスキャン機能(background: true
のポリシーは既存リソースにも定期的に適用される)やPolicyReportによる継続的監視機能を提供しています。これらを活用すれば、仮にAdmission時に検出漏れがあっても後から違反リソースを検知してアラートを上げることが可能です。最終的には、AdmissionWebhookは「即時のガードレール」として働かせ、「完全な整合性の担保」はコントローラーなどの継続的な仕組みに任せるのが安全策と言えるでしょう (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita) (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)。Kyvernoはその設計上、この考え方に沿った使い方をすることで最大の効果を発揮します。以上の点に留意しつつKyvernoを利用すれば、並行性由来の問題を最小限に抑えつつポリシーエンジンとしての利便性を享受できるでしょう。
参考資料: Kubernetes公式ドキュメント「Dynamic Admission Control」 (Admission Controllers 101 | Kyverno), Kyverno公式ドキュメントおよびブログ (How Kyverno Works | Kyverno) (The need for speed: optimizing Kyverno’s performance | CNCF), Qiita記事 (Kubernetes: Validating Webhookの「並行性」の落とし穴 #kubernetes - Qiita)(@suin氏)など。