エラーの内容
Error from server (Forbidden): pods "my-pod" is forbidden: exceeded quota: my-quota, requested: cpu=500m, used: cpu=900m, limited: cpu=1
名前空間に ResourceQuota の上限が設定されており、このPodがその上限を超えてしまっています。エラーメッセージを見ると状況が明確です。クォータの上限は 1000m CPU で、すでに 900m が使用済み、さらに 500m を要求しているため、合計 1400m になります。Kubernetesはこのリクエストを即座に拒否します。
Podはペンディング状態にもなりません。スケジューラーが認識する前に、APIサーバーの段階でブロックされます。
ステップ1:現在のクォータ使用量を確認する
まず、何がどれだけ使用されており、何が許可されているかを正確に確認します。
# 'my-namespace' を実際の名前空間名に置き換えてください
kubectl describe resourcequota -n my-namespace
出力は以下のようになります。
Name: my-quota
Namespace: my-namespace
Resource Used Hard
-------- ---- ----
cpu 900m 1
memory 512Mi 2Gi
pods 8 10
CPUが問題の原因です。1000m のハード上限に対して 900m が使用されており、残りは 100m しかなく、500m を要求するPodには到底足りません。
すべての名前空間のクォータを一度に確認するには、以下を実行します。
kubectl get resourcequota --all-namespaces
ステップ2:クォータを消費しているリソースを特定する
何かを変更する前に、どのPodが 900m を消費しているかを特定します。
kubectl get pods -n my-namespace -o custom-columns=NAME:.metadata.name,CPU:.spec.containers[*].resources.requests.cpu
metrics-serverがインストールされている場合、kubectl top を使うと宣言されたリクエストではなく実際の使用量を確認できます。
kubectl top pods -n my-namespace
不一致に注意してください。CPU を 500m リクエストしているのに実際の平均使用量が 30m 程度のPodは、リソースサイズ見直しの格好のターゲットです。
ステップ3:修正方法
方法A — PodのリソースリクエストをReduceする
過大なリクエストが最もよくある原因です。Podが本当に 500m を必要としていない場合は、マニフェストで値を下げましょう。
apiVersion: v1
kind: Pod
metadata:
name: my-pod
namespace: my-namespace
spec:
containers:
- name: my-container
image: my-image:latest
resources:
requests:
cpu: "100m" # 500m から 100m に削減
memory: "128Mi"
limits:
cpu: "200m"
memory: "256Mi"
現実的な値を設定するために、まず kubectl top pods を実行してください。平均 80m のPodに 100m を設定すれば、クォータを無駄にせず安全なバッファを確保できます。
方法B — ResourceQuotaの上限を増やす
名前空間にもう少し余裕が必要な場合もあります。まず現在のクォータ定義を確認します。
kubectl get resourcequota my-quota -n my-namespace -o yaml
直接パッチを適用する場合:
kubectl patch resourcequota my-quota -n my-namespace \
--type='json' \
-p='[{"op": "replace", "path": "/spec/hard/cpu", "value": "2"}]'
クォータをYAMLファイルで管理している場合(推奨方法)は、更新して適用します。
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-quota
namespace: my-namespace
spec:
hard:
cpu: "2" # 1 から 2 に増加
memory: 4Gi
pods: "20"
kubectl apply -f resourcequota.yaml
方法C — 未使用のPodを削除するか、Deploymentをスケールダウンする
完了済みや失敗したPodも、削除されるまでクォータを消費し続けます。不要なPodを整理しましょう。
# 完了済み・失敗したPodを削除
kubectl delete pods -n my-namespace --field-selector=status.phase==Succeeded
kubectl delete pods -n my-namespace --field-selector=status.phase==Failed
# 不要になったDeploymentをスケールダウン
kubectl scale deployment old-deployment -n my-namespace --replicas=0
他の方法を試す前にまずこれを確認する価値があります。古い Completed ジョブが大量に残っている名前空間は、数百ミリコアを静かに占有していることがあります。上限を変更せずにクォータの余裕を確保できるかもしれません。
ステップ4:修正を確認する
変更を加えたら、再試行する前にクォータに余裕があることを確認します。
# 更新されたクォータ使用量を確認
kubectl describe resourcequota -n my-namespace
# Podを再作成する
kubectl apply -f my-pod.yaml
# Podが起動していることを確認
kubectl get pod my-pod -n my-namespace
期待される出力:
NAME READY STATUS RESTARTS AGE
my-pod 1/1 Running 0 10s
今後のためのヒント
- リソースのリクエストと制限は必ず設定する — ResourceQuotaが設定されている名前空間では、リクエストや制限が未定義のPodは拒否されます。すべてのPodに個別に指定したくない場合は、
LimitRangeを使って名前空間全体のデフォルト値を設定しましょう。 - LimitRangeとResourceQuotaをセットで使う —
LimitRangeはコンテナごとのデフォルト値を設定し、ResourceQuotaは名前空間全体の合計に上限を設けます。これらは連携して機能するように設計されています。 - CI/CDパイプラインにクォータチェックを組み込む — デプロイ前のステップとして
kubectl describe resourcequota -n $NAMESPACEを実行し、利用可能なCPUがデプロイするPodのリクエストサイズを下回った場合にパイプラインを失敗させるようにしましょう。 - リクエストの値は推測ではなく実際のメトリクスに基づいて設定する —
kubectl top podsを使うか、Prometheusから1週間分のp95 CPUデータを取得してください。「500m くらいで大丈夫だろう」という憶測よりはるかに信頼できる根拠になります。 - クォータはポリシーであり、物理的な制約ではない — このエラーは物理的なリソース不足ではなく、管理上の上限に達したことを意味します。ノードには十分な空きCPUがあるかもしれません。ResourceQuotaはクラスター管理者が設定したガバナンス上の制限に過ぎません。

