Resource Quota

Kubernetes の Namespace ごとに CPU / メモリ / オブジェクト数の上限を定め、1 つのチームがクラスターを占有するのを防ぐ仕組み

Kubernetesインフラ

Resource Quota とは

Resource Quota は、Kubernetes の Namespace ごとに CPU、メモリ、Pod 数などのリソース使用量の上限を設定するリソースである。マルチテナント環境で、1 つのチームやアプリケーションがクラスター全体のリソースを占有するのを防ぐ。

物理的なサーバーリソースは有限だ。Resource Quota がなければ、あるチームのバッチ処理が CPU を 100% 消費し、他チームのサービスが応答不能になる事態が起こりうる。

効くのは作成・更新のリクエストが API サーバーを通る瞬間だけである。ResourceQuota アドミッションプラグインが有効で、かつその Namespace に ResourceQuota オブジェクトが置かれているときに集計が始まり、枠を超える要求は 403 Forbidden として、どの制約に触れたかを添えて拒否される。裏を返せば、既に存在するリソースには遡及しない。Pod を作った後で Quota を作っても、枠を後からきつくしても、動いている Pod が追い出されることはなく、使用量が上限を超えたまま表示され続ける。既存クラスターへ入れるときは、まず現在の使用量を測ってから、それを包む値で枠を切るのが安全な順序だ。

Resource Quota の設定例

チーム単位で枠を切るときは、CPU とメモリの requests・limits、それに Pod や Service のようなオブジェクト数を 1 つの ResourceQuota にまとめて書く。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "10"        # CPU リクエストの合計上限: 10 コア
    requests.memory: 20Gi     # メモリリクエストの合計上限: 20GiB
    limits.cpu: "20"          # CPU リミットの合計上限: 20 コア
    limits.memory: 40Gi       # メモリリミットの合計上限: 40GiB
    pods: "50"                # Pod 数の上限
    services: "10"            # Service 数の上限
    persistentvolumeclaims: "5"  # PVC 数の上限

この設定では、team-a Namespace 内の全 Pod の CPU リクエスト合計が 10 コアを超える Pod の作成は拒否される。集計に入るのは非終了状態の Pod だけで、status.phase が Succeeded または Failed になった Pod は枠を返す。requests.cpucpurequests.memorymemory と書いても同じ意味になる。

servicespersistentvolumeclaims のように専用の名前が用意されていない種類は、count/deployments.apps のように count/<リソース>.<API グループ> の形で数を絞れる (コア API グループのものは count/pods のようにグループ名を省く)。この形は容量というより制御プレーンの保護に効く。Secret を無制限に作らせるとその大きさで API サーバーやコントローラーの起動を妨げうるし、設定を誤った CronJob が Job を作り続ける事故も、count/jobs.batch で頭を押さえられる。

対象を絞りたい場合は scopes / scopeSelector を併用する。Kubernetes 1.36 のドキュメント時点で使えるのは BestEffort、NotBestEffort、spec.activeDeadlineSeconds が設定されている Pod に当たる Terminating とその補集合の NotTerminating、指定した PriorityClass を参照する Pod に当たる PriorityClass、名前空間をまたぐ Pod アフィニティを持つ Pod に当たる CrossNamespacePodAffinity、それに PersistentVolumeClaim 向けの VolumeAttributesClass である。「本番の優先度クラスにだけ厚い枠を与える」といった書き分けができる。

Resource Quota と LimitRange の違い

この 2 つは混同されやすいが、制御の粒度が異なる。

観点Resource QuotaLimitRange
制御対象Namespace 全体の合計個々の Pod / Container
役割チーム全体のリソース上限1 つの Pod が使えるリソースの範囲
設定例「Namespace 全体で CPU 10 コアまで」「1 Pod あたり CPU 最大 2 コア」
デフォルト値設定不可Pod にデフォルトの requests/limits を付与

実務では両方を組み合わせて使う。Resource Quota で Namespace 全体の枠を決め、LimitRange で個々の Pod のデフォルト値と上限を設定する。

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: team-a
spec:
  limits:
    - default:          # limits のデフォルト値
        cpu: "500m"
        memory: 512Mi
      defaultRequest:   # requests のデフォルト値
        cpu: "100m"
        memory: 128Mi
      max:              # 1 Container の上限
        cpu: "2"
        memory: 2Gi
      type: Container

Resource Quota が必要になる場面

  • マルチテナントクラスター: 複数チームが 1 つのクラスターを共有する場合、チームごとに Namespace を分け、Quota でリソースを公平に配分する
  • 開発環境のコスト制御: 開発環境で無制限にリソースを使われると、クラスターのコストが膨らむ。Quota で上限を設けて抑制する
  • 暴走 Pod の防止: バグで無限にレプリカが増える Pod がクラスター全体を圧迫するのを防ぐ

実務での設計指針

リソース配分の考え方

クラスター全体のリソースを 100% として、各 Namespace に配分する。

クラスター全体: CPU 100 コア, メモリ 400GiB
├── team-a (本番サービス):  CPU 40, メモリ 160GiB (40%)
├── team-b (本番サービス):  CPU 30, メモリ 120GiB (30%)
├── team-c (バッチ処理):    CPU 20, メモリ  80GiB (20%)
└── 予備 (バースト用):      CPU 10, メモリ  40GiB (10%)

ただし Quota はクラスター容量と連動しない絶対値の枠にすぎない。ノードを足しても各 Namespace の枠は自動では広がらず、逆に枠の合計が容量を超えていても Quota 自体は何も文句を言わない。足りなくなるのは実際にスケジュールされる段階で、そこから先は先着順の取り合いになり、後から要求した Pod は Pending のまま残る。ノードを増やしたら枠も引き上げる、という手当てが別途必要になる。

だから合計を 100% にせず、10〜20% の予備を残すのがポイントだ。予備がないと、バースト時に一時的に枠を広げる余地すら無くなる。

環境ごとの Quota 設計

環境CPU requestsメモリ requestsPod 数
prod40 コア160GiB200
stg10 コア40GiB50
dev5 コア20GiB30

開発環境は本番の 1/8 程度に抑え、コストを最適化する。

よくある落とし穴

  • requests/limits の指定漏れ: cpumemory については、Quota を置いた Namespace で requests か limits のどちらも書いていない Pod は admission で弾かれる。一方で ephemeral-storage やオブジェクト数の Quota は、指定の無い Pod をそのまま通す。「Quota さえ置けば全リソースで指定を強制できる」わけではないので、指定漏れそのものを無くしたいなら LimitRange で既定値を与える
  • 拒否が Pod の作成エラーとして見えない: 実務で人が作るのは Deployment であって Pod ではない。枠を超える Deployment の作成自体は成功し、その後 ReplicaSet が Pod を作れずに 403 を受け取る。kubectl apply は通ったのにレプリカが揃わない、という形で現れるため、kubectl describe deploymentkubectl describe replicaset のイベントまで降りないと原因に届かない
  • Quota の過小設定: 通常時は問題なくても、デプロイ時に新旧 Pod が同時に存在する (Rolling Update) ため、一時的にリソース使用量が増える。デプロイ時のオーバーヘッドを考慮した余裕が必要
  • 監視の欠如: Quota の使用率を見ていないと、上限に近づいていることに気づかず、ある日突然 Pod が作れなくなる。kube-state-metrics が出す kube_resourcequota は、同じ resource ラベルに対して type="hard"type="used" の 2 系列を返すので、used を hard で割った比率でアラートを組める。kubectl describe quota でも Used と Hard が並ぶが、これは詰まってから見るものなので、常時監視は前者に寄せる

Kubernetes 公式ドキュメントの「Resource Quotas」セクションが一次情報源であり、「Kubernetes in Action」(Marko Lukša 著) でも詳しく解説されている。対象にできるリソース名の一覧や scope の種類は版ごとに増えるため、枠を設計する前に使用中のバージョンのドキュメントで確かめるのが確実だ。

この記事は役に立ちましたか?

関連用語

関連する記事