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

ポリシーをコードで

範囲と障害モード — ポリシーを有効にする日の順番

TT Labで続きを見る

一言でいうと

ポリシーのリスクは、ルールの内容ではなく、範囲と故障モードから生まれます。そのため、ポリシーをオンにする日の順序は、常に、狭い範囲 → 警告 → 広げる → ブロックです。

なぜ必要なのか

ポリシー導入の事故の記録を集めてみると、ルールが間違っていて起きた事故は珍しいです。ほとんどは、2つです。広すぎる範囲にかけたか、故障したときの動作を決めていなかったかです。

広くかけることがなぜ危険かは、コストの性質を見ればわかります。どんなリソースにもマッチするポリシー1つのコストは、ポリシー1回の実行時間ではありません。APIサーバーに入ってくるすべての書き込みリクエストに付く税です。クラスターには、人が送るリクエストよりも、コントローラーが送るリクエストのほうが、ずっと多いです。Lease・イベント・EndpointSlice・Podの状態の更新が、絶え間なく流れています。ワイルドカードのポリシー1つが、そのすべてを、もう一度ずつつかまえます。

2つ目のほうが、もっと恐ろしいです。ポリシーがkube-systemまで見るようにかけておいて、ポリシーエンジンが落ちると、failurePolicy: Failのもとで、クラスターは自分自身を復旧できなくなります。エンジンを蘇生させるためのデプロイも、そのポリシーを通る必要があるからです。循環です。

どう動くのか

範囲は3つの層で絞ります。

層 何を決めるか 例
リソース どのグループ・バージョン・種類・verbを見るか apps/v1 deploymentsのCREATE・UPDATEだけ
ネームスペース どのネームスペースに適用するか namespaceSelectorで、特定のラベルが付いた場所だけ
リクエスト そのうち、どのリクエストを見るか objectSelector、matchConditionsのCEL条件

3つの層は、コストを減らす順序でもあります。リソースの層でふるい落とされたリクエストは、APIサーバーがポリシーを思い出しもしません。ネームスペースの層はその次で、リクエストの層は最後です。そのため、ワイルドカードで受けておいて、CEL条件でふるい分けるポリシーは、3つの層のうち最もコストの高い場所で働いていることになります。同じ結果を出しながら、コストだけが大きいのです。

kube-systemを除外するのは、好みではなく、エスケープハッチを残す作業です。ポリシーエンジンが住むネームスペースも同じです。ランブックに「Webhookの設定を削除して、クラスターを蘇生させる」という手順が入っている組織が多いのですが、そもそも範囲から外しておけば、その手順を使う場面が減ります。

故障モード。failurePolicyが引き換えにするものです。

値 ポリシーサーバーが応答できないとき そのとき失うもの
Fail リクエストを拒否する 可用性。エンジンが落ちると、その範囲の書き込みが止まる
Ignore リクエストを通過させる 保証。エンジンが落ちている間、検査なしに入ってくる

どちらも、正しい答えではありません。同じクラスターの中でも、ルールごとに、合う値が違います。セキュリティ境界を守るルールはFailに、衛生ルールはIgnoreにします(ラベルの標準、推奨設定などが衛生ルールです)。そして、Failを選んだルールほど、範囲を絞ることが重要になります。範囲が狭ければ、エンジンが落ちても、止まるのがその範囲だけだからです。範囲と故障モードは、別々に選ぶ値ではなく、セットで選ぶ値です。

timeoutSecondsは、3つ目のつまみです。Webhookの応答を待つ時間で、長くすると、障害のときにAPIサーバーがその分だけ拘束されます。短くすると、遅い応答が失敗として処理されて、failurePolicyに渡されます。そのため、タイムアウトは「余裕を持たせて」ではなく、「このルールが正常なときにかかる時間より、少し長く」で決めます。

組み込みポリシーとWebhookポリシーは、故障モードが違います。この違いが、設計の選択を変えます。

そのため、実務の配置は、たいてい次のように分かれます。エンジンなしで表現できる検証はVAP・PSAに移して、故障の表面積を減らし、本当に必要なものだけをWebhookに残します。

ポリシーをオンにする日の順序です。

1) 좁은 범위로 시작한다 (한 네임스페이스, 한 종류, 한 동사)
2) 경고·감사로만 켜고 며칠 센다
3) 걸린 것을 고치거나 좁은 예외로 남긴다
4) 범위를 한 단계 넓히고 2..3 을 반복한다
5) 마지막에 차단으로 올린다 (정책 본문이 아니라 켜는 쪽만 고친다)
6) 되돌리는 절차를 미리 적어 둔다

このコードブロックの韓国語は、6つの手順を順に、狭い範囲で始める(1つのネームスペース、1つの種類、1つのverb)、警告・監査だけでオンにして数日数える、引っかかったものを直すか、狭い例外として残す、範囲を1段階広げて手順2と手順3を繰り返す、最後にブロックに上げる(ポリシーの本文ではなく、オンにする側だけを直す)、元に戻す手順をあらかじめ書いておく、と述べています。

手順4と手順5の順序を入れ替えるチームが多いです。狭い範囲からすぐにブロックに上げて、そのあとで範囲を広げるのです。そうすると、範囲を広げる日が、そのまま事故の日になります。新しく入ってきたネームスペースが、観察なしにいきなりブロックに出会うからです。広げる作業とブロックに上げる作業を、同じ日に行わないことが、この順序の核心です。

現場での姿

1つ目は、ワイルドカードポリシーのレイテンシ税です。すべてのリソースにマッチするWebhook1つのせいで、APIサーバーのレイテンシ分布のテールがまるごと上がった事例が、よくあります。原因の把握が難しい理由は、ポリシー自体は速いからです。遅いのはリクエスト1つではなく、リクエストのすべてです。

2つ目は、自分自身をブロックしたポリシーです。エンジンのネームスペースを範囲から外さなかったため、エンジンを再デプロイしようとするリクエストが、死んだエンジンに尋ねてブロックされます。蘇生させる唯一の道が、Webhookの設定を削除することだけなので、その手順を知る人を探している間に、障害が長引きます。

3つ目は、Ignoreでオンにしたまま忘れることです。事故を避けるために、すべてIgnoreでオンにしておくと、エンジンが数時間落ちている間に検査なしに入ってきたものを、誰も知りません。Ignoreを選んだなら、エンジンの可用性アラートがポリシーの一部である必要があります。

4つ目は、1つのノードに集中したエンジンです。レプリカが3つでも、同じノードにあれば、そのノードが抜けるときに一緒に消えます。Failのもとでは、それがそのまま書き込みの停止です。トポロジースプレッドとPodDisruptionBudgetは、ポリシー導入の選択肢ではなく、必須の準備です。

5つ目は、この環境で見られることです。kwokクラスターでは、届かないアドレスを持つWebhookの設定を作って、FailとIgnoreの違いを実際に見られます。同じリクエストが、一方ではブロックされ、もう一方では通過します。

参考ドキュメント

次のラボですること

ポリシーをわざと壊して、その故障を観察します。届かないアドレスを持つWebhookの設定を作って、failurePolicy: Failでリクエストがブロックされることと、Ignoreで通過することを、同じリクエストで確認し、namespaceSelectorでシステムのネームスペースを範囲から外して、エスケープハッチができるのを見ます。範囲を、リソース・ネームスペース・リクエストの3つの層でそれぞれ絞りながら、どのリクエストがポリシーを通り、どのリクエストが通らないかを数えてみて、最後に、狭い範囲 → 警告 → 広げる → ブロックの順序を一周しながら、元に戻す手順まで作ります。