このページでは、Kubernetesにおける Admission Webhook の設計に関するグッドプラクティスと考慮事項について説明します。 この情報は、Admission Webhookサーバーを運用するクラスター管理者や、APIリクエストを変更または検証するサードパーティアプリケーションを対象としています。
このページを読む前に、以下の概念について理解していることを確認してください。
Admission制御は、Kubernetes APIに対して作成、更新、または削除リクエストが送信されたときに発生します。 Admissionコントローラーは、定義した特定の基準に一致するリクエストをインターセプトします。 これらのリクエストは、Mutating Admission WebhookまたはValidating Admission Webhookに送信されます。 これらのWebhookは、オブジェクト仕様において特定のフィールドが存在することや、特定の許可された値を持つことを保証するために記述されることが多いです。
WebhookはKubernetes APIを拡張するための強力なメカニズムです。 設計が不適切なWebhookは、クラスター内のオブジェクトに対して大きな制御権を持つため、ワークロードの中断を引き起こすことがよくあります。 他のAPI拡張メカニズムと同様に、Webhookはすべてのワークロード、他のWebhook、アドオン、プラグインとの互換性を大規模にテストすることが困難です。
さらに、Kubernetesはリリースごとに、新機能の追加、ベータまたは安定版ステータスへの昇格、非推奨化によってAPIを変更します。
安定版のKubernetes APIでさえ変更される可能性が高いです。
例えば、Pod APIはv1.29でサイドカーコンテナ機能を追加するために変更されました。
Kubernetes APIの変更によってKubernetesオブジェクトが破損状態になることはまれですが、以前のバージョンのAPIで正常に動作していたWebhookが、そのAPIのより新しい変更と整合性がとれなくなる場合があります。
これにより、クラスターを新しいバージョンにアップグレードした後に予期しない動作が発生する可能性があります。
このページでは、一般的なWebhookの障害シナリオと、Webhookを慎重かつ丁寧に設計・実装することでそれらを回避する方法について説明します。
自分でAdmission Webhookを運用していない場合でも、クラスターで実行しているサードパーティアプリケーションがMutatingまたはValidating Admission Webhookを使用している可能性があります。
クラスターにMutating Admission Webhookが存在するかどうかを確認するには、以下のコマンドを実行してください:
kubectl get mutatingwebhookconfigurations
出力には、クラスター内のMutating Admissionコントローラーが一覧表示されます。
クラスターにValidating Admission Webhookが存在するかどうかを確認するには、以下のコマンドを実行してください:
kubectl get validatingwebhookconfigurations
出力には、クラスター内のValidating Admissionコントローラーが一覧表示されます。
Kubernetesには、複数のAdmission制御およびポリシー適用オプションが含まれています。 特定のオプションを使用するタイミングを理解することで、レイテンシーやパフォーマンスの改善、管理オーバーヘッドの削減、バージョンアップグレード時の問題の回避に役立ちます。 以下の表は、Admission時にリソースをミューテーションまたはバリデーションするためのメカニズムを示しています。
| メカニズム | 説明 | ユースケース |
|---|---|---|
| Mutating Admission Webhook | Admission前にAPIリクエストをインターセプトし、カスタムロジックを使用して必要に応じて変更します。 |
|
| Mutating Admission Policy | Admission前にAPIリクエストをインターセプトし、Common Expression Language(CEL)式を使用して必要に応じて変更します。 |
|
| Validating Admission Webhook | Admission前にAPIリクエストをインターセプトし、複雑なポリシー宣言に対してバリデーションします。 |
|
| Validating Admission Policy | Admission前にAPIリクエストをインターセプトし、CEL式に対してバリデーションします。 |
|
一般的に、拡張可能な方法でロジックを宣言または設定したい場合は、Webhook Admission制御を使用してください。 Webhookサーバーの運用オーバーヘッドなしで、より単純なロジックを宣言したい場合は、組み込みのCELベースのAdmission制御を使用してください。 Kubernetesプロジェクトでは、可能な場合はCELベースのAdmission制御を使用することを推奨しています。
CustomResourceDefinitionを使用している場合、CustomResourceの仕様の値をバリデーションしたり、フィールドのデフォルト値を設定したりするためにAdmission Webhookを使用しないでください。 KubernetesではCustomResourceDefinitionを作成する際に、バリデーションルールとデフォルトフィールド値を定義できます。
詳細については、以下のリソースを参照してください。
このセクションでは、パフォーマンスの改善とレイテンシーの削減に関する推奨事項を説明します。 要約すると、以下の通りです:
Mutating Admission Webhookは順番に呼び出されます。 Mutating Webhookの設定によっては、一部のWebhookが複数回呼び出される場合があります。 Mutating Webhookの呼び出しのたびに、Admissionプロセスにレイテンシーが加わります。 これは、並列で呼び出されるValidating Webhookとは異なります。
Mutating Webhookを設計する際は、レイテンシーの要件と許容範囲を考慮してください。 クラスター内のMutating Webhookが多いほど、レイテンシーが増加する可能性が高まります。
レイテンシーを削減するために、以下を検討してください:
Webhookが行うミューテーションと競合する可能性のある、クラスター内で実行されている他のコンポーネントを考慮してください。 例えば、Webhookがラベルを追加し、別のコントローラーがそのラベルを削除する場合、Webhookが再度呼び出されます。 これによりループの発生につながります。
これらのループを検出するには、以下を試してください:
クラスターの監査ポリシーを更新して、監査イベントをログに記録します。 以下のパラメーターを使用してください:
level: RequestResponseverbs: ["patch"]omitStages: RequestReceivedWebhookがミューテーションする特定のリソースに対してイベントを作成するように監査ルールを設定します。
同じパッチが同じオブジェクトに複数回適用されたり、オブジェクトのフィールドが複数回更新と復元を繰り返したりしていないか、監査イベントを確認します。
Admission Webhookは、APIリクエストのレイテンシーに影響を与えるため、可能な限り迅速に(通常ミリ秒単位で)評価する必要があります。 Webhookには小さなタイムアウトを使用してください。
詳細については、タイムアウトを参照してください。
Admission Webhookは、高可用性とパフォーマンス上の利点を提供するために、何らかの形式のロードバランシングを活用する必要があります。
Webhookがクラスター内で実行されている場合、ClusterIPタイプのServiceの背後で複数のWebhookバックエンドを実行できます。
Webhookを設計する際は、クラスターの可用性要件を考慮してください。
例えば、ノードのダウンタイムやゾーン障害が発生した場合、Kubernetesはロードバランサーが利用可能なゾーンやノードにトラフィックを再ルーティングできるように、PodをNotReadyとしてマークします。
これらのPodへの更新がMutating Webhookをトリガーする可能性があります。
影響を受けるPodの数によっては、Mutating Webhookサーバーがタイムアウトしたり、Podの処理に遅延が発生したりするリスクがあります。
その結果、必要なタイミングでトラフィックを素早く切り替えられなくなる可能性があります。
前述の例のような状況を念頭に置いて、Webhookを実装してください。 Kubernetesが避けられない障害に対応する際に発生する操作は、Webhookの対象から外してください。
このセクションでは、特定のWebhookをトリガーするリクエストのフィルタリングに関する推奨事項について説明します。 要約すると、以下の通りです:
Admission Webhookは、APIリクエストが対応するWebhook設定に一致した場合にのみ呼び出されます。 各Webhookのスコープを制限して、Webhookサーバーへの不要な呼び出しを減らしてください。 以下のスコープ制限を検討してください:
kube-system名前空間のオブジェクトにマッチさせることを避けてください。
kube-system名前空間で独自のPodを実行している場合は、objectSelectorを使用して、重要なワークロードのミューテーションを避けてください。kube-node-leaseシステム名前空間にLeaseオブジェクトとして存在するノードリースをミューテーションしないでください。
ノードリースをミューテーションすると、ノードのアップグレードの失敗につながる可能性があります。
この名前空間のLeaseオブジェクトにバリデーション制御を適用するのは、その制御がクラスターをリスクにさらさないと確信できる場合のみにしてください。namespaceSelectorを使用して、各Webhookを特定の名前空間に制限してください。Admissionコントローラーは、特定の基準を満たすリクエストにマッチさせるために使用可能な複数のフィールドをサポートしています。
例えば、namespaceSelectorを使用して、特定の名前空間を対象とするリクエストをフィルタリングできます。
よりきめ細かいリクエストフィルタリングには、Webhook設定のmatchConditionsフィールドを使用してください。
このフィールドでは、リクエストがAdmission Webhookをトリガーするためにtrueと評価される必要がある複数のCEL式を記述できます。
matchConditionsを使用すると、Webhookサーバーへの呼び出し数を大幅に削減できる場合があります。
詳細については、リクエストのマッチング: matchConditionsを参照してください。
デフォルトでは、Admission Webhookは指定されたリソースに影響するすべてのAPIバージョンで実行されます。
Webhook設定のmatchPolicyフィールドがこの動作を制御します。
matchPolicyフィールドにEquivalentの値を指定するか、フィールドを省略して、Webhookが任意のAPIバージョンで実行されるようにしてください。
詳細については、リクエストのマッチング: matchPolicyを参照してください。
このセクションでは、ミューテーションのスコープと、オブジェクトフィールドを扱う際の注意点についての推奨事項について説明します。 要約すると、以下の通りです:
Admission Webhookサーバーは、特定のKubernetes APIリクエストに対する処理内容をHTTPレスポンスで返します。
このレスポンスはAdmissionReviewオブジェクトです。
Mutating Webhookは、レスポンス内のpatchTypeフィールドとpatchフィールドを使用して、Admissionを許可する前にミューテーションする特定のフィールドを追加できます。
変更が必要なフィールドのみを変更するようにしてください。
例えば、web-server Deploymentが少なくとも3つのレプリカを持つことを保証するように設定されたMutating Webhookを考えてみてください。
Deploymentオブジェクトの作成リクエストがWebhook設定に一致した場合、Webhookはspec.replicasフィールドの値のみを更新する必要があります。
Kubernetesオブジェクト仕様のフィールドには、配列が含まれる場合があります。
一部の配列はkey:valueペアを含み(コンテナ仕様のenvVarフィールドなど)、他の配列はキーなしです(Pod仕様のreadinessGatesフィールドなど)。
配列フィールドの値の順序が重要な場合があります。
例えば、コンテナ仕様のargsフィールドの引数の順序は、コンテナに影響を与える可能性があります。
配列を変更する際は、以下を考慮してください:
replaceの代わりにadd JSONPatch操作を使用してください。Webhookは、送信されたAdmissionReviewの内容に対してのみ動作し、それ以外の箇所への変更を行わないようにしてください。
これらの追加の変更は 副作用 と呼ばれ、適切に調整されない場合、Admission中に競合を引き起こす可能性があります。
Webhookに副作用がない場合、.webhooks[].sideEffectsフィールドをNoneに設定する必要があります。
Admissionの評価中に副作用が必要な場合、dryRunがtrueに設定されたAdmissionReviewオブジェクトの処理時には副作用を抑制する必要があり、.webhooks[].sideEffectsフィールドをNoneOnDryRunに設定する必要があります。
詳細については、副作用を参照してください。
クラスター内で実行されているWebhookは、自身のPodの起動に必要なリソースをインターセプトするように設定されている場合、自身のDeploymentに対してデッドロックを引き起こす可能性があります。
例えば、Mutating Admission Webhookが、特定のラベル(env: prodなど)がPodに設定されている場合にのみPodのcreateリクエストを許可するように設定されているとします。
Webhookサーバーは、envラベルを設定していないDeploymentで実行されています。
Webhookサーバーのpodを実行しているノードが異常になった場合、Webhook Deploymentは別のノードにPodを再スケジュールしようとします。
しかし、envラベルが設定されていないため、既存のWebhookサーバーがリクエストを拒否します。
その結果、移行が行えなくなります。
Webhookが実行されている名前空間をnamespaceSelectorで除外してください。
以下のようなシナリオで、依存関係のループが発生する可能性があります:
これらの依存関係のループを避けるには、以下を試してください:
objectSelectorを使用して、Webhookが依存するアドオンに作用することを防止してください。Mutating Admission WebhookはfailurePolicy設定フィールドをサポートしています。
このフィールドは、Webhookが失敗した場合に、APIサーバーがリクエストを許可するか拒否するかを示します。
Webhookの失敗は、タイムアウトやサーバーロジックのエラーによって発生する可能性があります。
デフォルトでは、Admission WebhookはfailurePolicyフィールドをFailに設定します。
この場合、Webhookが失敗した場合、APIサーバーはリクエストを拒否します。
しかし、デフォルトでリクエストを拒否すると、Webhookのダウンタイム中、ポリシーに準拠したリクエストが拒否される可能性があります。
Mutating WebhookのfailurePolicyフィールドをIgnoreに設定して、エラー時でもリクエストを通すようにしてください。
バリデーションコントローラーを使用して、リクエストの状態がポリシーに準拠していることを確認してください。
このアプローチには以下の利点があります:
一般的に、Kubernetes APIが将来のバージョンで変更される可能性があるという前提でWebhookを設計してください。
APIの安定性を当然のものとして扱うサーバーを記述しないでください。
例えば、Kubernetesにおけるサイドカーコンテナのリリースにより、Pod APIにrestartPolicyフィールドが追加されました。
広範なAPIリクエストに応答するMutating Webhookは、意図せず自身をトリガーする可能性があります。 例えば、クラスター内のすべてのリクエストに応答するWebhookを考えてみてください。 Webhookをすべてのミューテーションに対してEventオブジェクトを作成するように設定した場合、自身のEventオブジェクト作成リクエストにも応答してしまいます。
これを避けるには、Webhookが作成するリソースに一意のラベルを設定することを検討してください。 Webhookのマッチ条件からこのラベルを除外してください。
APIサーバー内の一部のKubernetesオブジェクトは変更できません。 例えば、静的Podをデプロイすると、ノード上のkubeletは静的Podを追跡するため、APIサーバーにミラーPodを作成します。 しかし、ミラーPodへの変更は静的Podには反映されません。
Admission中にこれらのオブジェクトをミューテーションしようとしないでください。
すべてのミラーPodにはkubernetes.io/config.mirrorアノテーションが付与されています。
アノテーションを無視するセキュリティリスクを軽減しつつ、ミラーPodを除外するには、静的Podが特定の名前空間でのみ実行されるようにしてください。
このセクションでは、Webhookの順序と冪等なWebhookの設計に関する推奨事項を説明します。 要約すると、以下の通りです:
Mutating Admission Webhookは一貫した順序で実行されません。 さまざまな要因により、特定のWebhookが呼び出されるタイミングが変わる可能性があります。 Webhookがadmissionプロセスの特定のポイントで実行されることに依存しないでください。 他のWebhookが変更後のオブジェクトをさらにミューテーションする可能性があります。
以下の推奨事項は、意図しない変更のリスクを最小限に抑えるのに役立つ可能性があります:
すべてのMutating Admission Webhookは 冪等 である必要があります。 Webhookは、すでに変更したオブジェクトに対して、元の変更を超える追加の変更を行わずに実行できる必要があります。
さらに、クラスター内のすべてのMutating Webhookは、全体として捉えたときに冪等である必要があります。 Admission制御のミューテーションフェーズが終了した後、すべての個々のMutating Webhookがオブジェクトに対して追加の変更を行わずに実行できる必要があります。
環境によっては、大規模に冪等性を確保することが困難な場合があります。 以下の推奨事項が役立つかもしれません:
以下の例は、冪等なミューテーションロジックを示しています:
Podのcreateリクエストに対して、Podの.spec.securityContext.runAsNonRootフィールドをtrueに設定する。
Podのcreateリクエストに対して、コンテナの.spec.containers[].resources.limitsフィールドが設定されていない場合、デフォルトのリソース制限を設定する。
Podのcreateリクエストに対して、foo-sidecarという名前のコンテナがまだ存在しない場合、foo-sidecarという名前のサイドカーコンテナを注入する。
これらのケースでは、Webhookは安全に再呼び出しできるか、すでにフィールドが設定されているオブジェクトを許可できます。
以下の例は、冪等でないミューテーションロジックを示しています:
Podのcreateリクエストに対して、現在のタイムスタンプをサフィックスとしたfoo-sidecarという名前のサイドカーコンテナを注入する(foo-sidecar-19700101-000000など)。
Webhookを再呼び出しすると、同じサイドカーが毎回異なるコンテナ名でPodに複数回注入される可能性があります。 同様に、ユーザーが提供したPodにすでにサイドカーが存在する場合、Webhookは重複するコンテナを注入する可能性があります。
Podのcreate/updateリクエストに対して、Podにenvラベルが設定されている場合は拒否し、そうでなければenv: prodラベルをPodに追加する。
Webhookを再呼び出しすると、Webhookは自身の出力に対して失敗します。
Podのcreateリクエストに対して、foo-sidecarコンテナが存在するかどうかを確認せずに、foo-sidecarという名前のサイドカーコンテナを追加する。
Webhookを再呼び出しすると、Pod内にコンテナが重複し、リクエストが無効になってAPIサーバーに拒否されます。
このセクションでは、Mutating Webhookのテストとミューテーションされたオブジェクトのバリデーションに関する推奨事項について説明します。 要約すると、以下の通りです:
堅牢なテストは、新規または更新されたWebhookのリリースサイクルの中核となる部分です。 可能であれば、クラスターのWebhookへの変更を、本番クラスターに近いステージング環境でテストしてください。 少なくとも、minikubeやkindなどのツールを使用して、Webhookの変更用の小さなテストクラスターを作成することを検討してください。
Mutating Webhookは、Admission前にオブジェクトに適用されるバリデーションを壊してはなりません。 例えば、PodのデフォルトCPUリクエストを特定の値に設定するMutating Webhookを考えてみてください。 そのPodのCPU制限がミューテーションされたリクエストよりも低い値に設定されている場合、PodはAdmissionに失敗します。
すべてのMutating Webhookを、クラスターで実行されるバリデーションに対してテストしてください。
本番クラスターを新しいマイナーバージョンにアップグレードする前に、ステージング環境でWebhookとワークロードをテストしてください。 結果を比較して、アップグレード後もWebhookが期待通りに機能し続けることを確認してください。
さらに、API変更に関する情報を得るために、以下のリソースを活用してください:
Mutating Webhookは、Validating Webhookが実行される前にすべて完了します。 ミューテーションがオブジェクトに適用される安定した順序はありません。 その結果、後から実行されるMutating Webhookによってミューテーションが上書きされる可能性があります。
ValidatingAdmissionWebhookやValidatingAdmissionPolicyなどのValidating Admissionコントローラーをクラスターに追加して、ミューテーションが引き続き存在することを確認してください。
例えば、特定のInitコンテナをサイドカーコンテナとして実行するためにrestartPolicy: Alwaysフィールドを挿入するMutating Webhookを考えてみてください。
すべてのミューテーションが完了した後、それらのInitコンテナがrestartPolicy: Alwaysの設定を保持していることを確認するValidating Webhookを実行できます。
詳細については、以下のリソースを参照してください:
このセクションでは、Mutating Admission Webhookのデプロイに関する推奨事項について説明します。 要約すると、以下の通りです:
Mutating Webhookをクラスターにデプロイする準備ができたら、以下の手順で作業を進めてください。
failurePolicyフィールドをIgnoreに設定する。
これにより、設定が誤ったWebhookによる中断を回避できます。namespaceSelectorフィールドをテスト用の名前空間に設定する。テスト用の名前空間でWebhookを監視して問題がないか確認し、その後他の名前空間にロールアウトしてください。 Webhookがインターセプトする意図のないAPIリクエストをインターセプトしてしまった場合は、ロールアウトを一時停止してWebhook設定のスコープを調整してください。
Mutating Webhookは強力なKubernetesコントローラーです。 RBACまたは他の認可メカニズムを使用して、Webhook設定とサーバーへのアクセスを制限してください。 RBACの場合、以下のアクセスが信頼できるエンティティにのみ利用可能であることを確認してください。
admissionregistration.k8s.io/v1Mutating Webhookサーバーがクラスター内で実行されている場合は、その名前空間内でのリソースの作成または変更へのアクセスを制限してください。
以下のプロジェクトは、「適切な」カスタムWebhookサーバー実装の例です。 Webhookを設計する際の出発点として使用できます。 ただし、これらの例をそのまま使用しないでください。 出発点として使用し、特定の環境で適切に動作するようにWebhookを設計してください。
このページの項目は、Kubernetesが必要とする機能を提供するサードパーティ製品またはプロジェクトです。Kubernetesプロジェクトの作者は、それらのサードパーティ製品またはプロジェクトに責任を負いません。詳しくは、CNCFウェブサイトのガイドラインをご覧ください。コンテンツガイドを読んでから、サードパーティリンクを追加する変更を提案してください。