既定値・クォータ・受け入れ拒否の帳簿
一言でいうと
LimitRangeは個々のオブジェクトのデフォルト値と範囲を、ResourceQuotaはネームスペースの合計を扱います。クォータに引っかかった要求は、PodがPendingで待つのではなく、作成の段階で拒否されることがあります。
なぜ必要なのか
小さなチーム1つが誤って数千個のオブジェクトを作ると、クラスターのほかのチームも影響を受けます。リソースのポリシーは、悪意のある攻撃だけでなく、ループ1つのタイプミスが生む被害も制限します。しかし、制限を入れたという事実だけでは、望む動作が保証されるわけではありません。どの要求に適用されるのか、既存のオブジェクトに触れるのか、使用量が何を数えるのかを、実際に確認する必要があります。
新しいPodを作ろうとしたらForbiddenが出たとしましょう。これを無条件にアカウントの権限の問題と見ると、RBACの権限をもっと与えるという誤った対応につながります。同じHTTPステータスでも、レスポンス本文の原因と関連するオブジェクトが重要です。クォータの超過は、権限を追加しても解決しません。制限を削除して成功させることも、ポリシーを理解した解決ではありません。
どう動くのか
今回の実験では、1つのコンテナのデフォルトの要求をCPU 100m・メモリ32Mi、デフォルトの制限をCPU 200m・メモリ64Miとします。入力でresourcesを省略した新しいPodと、APIに保存されたPodを、並べて読みます。保存されたオブジェクトに値が入っていれば、ユーザーのファイルの値ではなく、admissionの過程で適用されたデフォルト値かもしれません。すでにCPU 50mを明示して作った既存のPodを、100mに変えたとは仮定しません。
ResourceQuotaのrequests.cpuは、この空間に属する対象Podの、CPU要求の合計を制限します。今CPUを実際に何パーセント使っているかを測定した数字ではありません。今回の例の帳簿は、次のように動きます。
| 状態 | 保存された要求の合計 | 許容される上限 | 新しい要求の結果 |
|---|---|---|---|
| fit 50m + defaulted 100m | 150m | 200m | extra 100mを加えると250mになるので拒否 |
| 上限の調整後 | 150m | 300m | 同じextraの要求が入って合計250m |
| 上限を再び下げる | 250m | 100m | 既存のPodは維持され、新しいsmallの要求は拒否 |
| extraの回収・上限の再調整 | 150m | 200m | small 50mが入って合計200m |
この表の上限の変更は、使い捨てのネームスペースで、ポリシーの意味を学ぶ実験です。本番では、使用量・業務上の要求・チームへの配分を検討した、権限のある担当者がポリシーを変更します。単にエラーをなくすためにクォータを増やせという案内ではありません。
クォータを現在の使用量より下に下げたからといって、既存のPodが自動的に終了されるわけではありません。したがって、status.usedがspec.hardより大きい状態を観測できます。これは、ポリシーが無効であることの証拠ではありません。既存のオブジェクトを維持しながら、追加の作成を拒否するかを確認する必要があります。強制的に使用量を減らさなければならないなら、どの作業を終了させるかについて、別の運用上の判断が必要です。
ResourceQuotaは、1つの軸だけを制限するとは限りません。requests.cpu、requests.memory、limits.cpu、limits.memory、Podの個数などをあわせて検査できるので、CPUだけを計算して成功と断定しません。今回の実験は、残りの上限に余裕を持たせて、CPU要求量の効果を分離します。LimitRangeの個別の上限に違反したのか、ResourceQuotaの合計の上限に違反したのかも、区別します。
現場での姿
Podを作る前の入力、APIのレスポンス、作成後の参照結果を、1つの証拠の束として見ます。クォータの拒否では、実際のHTTP 403と、KubernetesのStatusのForbiddenの理由、team-budgetおよびrequests.cpuが出てくる説明を確認します。続けて、同じ名前のPodが存在しないことを確認します。アカウントの権限エラーやTLSエラーを受け取っておきながら、望むポリシーが動作したと記録してはいけません。
逆に、HTTP 201は、オブジェクトの作成が受け付けられたという意味であって、アプリの起動が完了したという意味ではありません。新しいPod UIDがレスポンスと一致するか、ノードに配置されてRunning・Readyになっているかまで、続けて見ます。スケジューリングとadmissionは別の段階なので、「この要求はquotaの範囲内にある」と「このPodが実行されるノードがある」は別々のことです。
kubectl -n kcna-capacity get limitrange defaults -o json
kubectl -n kcna-capacity get resourcequota team-budget -o json
kubectl -n kcna-capacity get pods -o json
状態の帳簿は、変更の直後には、まだ以前の値を示していることがあります。所有するextra Podを削除したあとは、実際のオブジェクトの不在とquota使用量の減少を確認してから、次のリクエストを送ります。固定の短いsleepだけで完了したと仮定しません。待機には制限時間を設け、時間を超えたら成功として上書きせず、観測を保存します。
次のラボですること
リソースが余っているように見える状況でも起こる2つの失敗を、直接比較します。配置の失敗は実際のPod UIDとスケジューラーのイベントで、受け入れ拒否はAPIのステータスとオブジェクトの不在で立証します。クォータの縮小後、既存のfit・defaulted・extraのUIDとコンテナがそのままかを確認したうえで、調査したextraだけを回収し、小さな要求を新たに受け入れさせます。ほかのネームスペースの正常な比較群は変更しません。