Pod が Pending で止まるとき — 原因 7 つと確認の順序
デプロイしたのに Pod が Pending から動きません。Pending はエラーではなく「実行するノードがまだ決まっていない」状態なので、ログもなく(コンテナが起動前なので kubectl logs は空振りです)、原因がすぐには見えません。この記事は Pending の原因を実務の頻度順に 7 つ整理します。試験観点のトラブルシューティング全般は CKA #22 で扱ったので、ここでは運用中に遭遇するシナリオに集中します。
確認は一か所から始めます — Events #
Pending の理由は、ほぼ常にスケジューラが Events に書き残しています。
$ kubectl describe pod api-server-7d4b9c-x2k8p
...
Events:
Type Reason Message
---- ------ -------
Warning FailedScheduling 0/6 nodes are available: 3 Insufficient cpu,
2 node(s) had untolerated taint {gpu: "true"},
1 node(s) didn't match Pod's node affinity/selector.このメッセージが診断のほぼすべてです。「ノード 6 台中 0 台が利用可能」とその内訳(CPU 不足 3 台、taint 2 台、affinity 不一致 1 台)が出るので、以下の原因リストはこのメッセージを解釈する辞書だと思ってください。
1. リソース不足 — Insufficient cpu/memory #
最も多い原因です。スケジューラはノードの実使用量ではなく requests の合計で空きを計算します。Insufficient cpu は「すべてのノードで、残りの割り当て可能量(allocatable)よりこの Pod の requests が大きい」という意味です。
- 確認:
kubectl describe nodeのノードごとのAllocated resourcesで、requests の合計がどれだけ埋まっているかを見ます。 - 対処: まずはノードの増設か他のワークロードの整理を検討します。ただしその前に、次の項目(過大な requests)を先に疑う価値があります。
2. 過大な requests — 実使用は低いのに予約が満杯 #
kubectl top node の実使用率は 30% なのに Insufficient cpu が出るなら、誰かの requests が実際より大きく設定されています。requests は予約なので、使わなくても枠を占有します。「とりあえず大きめ」の requests が積もると、クラスタの半分が空の予約でロックされます。
- 確認: ワークロードごとの requests と実使用を並べて見ます(metrics-server、または VPA の推奨モード)。
- 対処: 実測に基づいて requests を再調整します。基準の決め方は K8s 中級 #4 の概念の上で、別の記事として扱います。
3. nodeSelector・affinity の不一致 #
didn't match Pod's node affinity/selector が見えたら、Pod が要求するラベルを持つノードがないか足りないケースです。ノードのラベルが変更・削除されたか、マニフェストのラベルの打ち間違い(disktype: ssd vs diskType: ssd)が典型です。
- 確認:
kubectl get nodes --show-labelsで実際のラベルとマニフェストの要求を突き合わせます。 - 対処: ラベルを合わせるか、必須でない制約なら required を preferred affinity に緩めます。
4. taint を許容する toleration がない #
untolerated taint は、ノード側が「誰でも来るな」と宣言しているケースです。GPU ノード、専用ノードプール、そしてコントロールプレーンノードのデフォルトの taint が該当します。クラスタに GPU ノードしか空きが残っていないと、一般の Pod が全部 Pending になる、という事故が起きます。
- 確認:
kubectl describe node <node> | grep Taintsで見ます。 - 対処: 意図した専用ノードなら一般ワークロード用のノードを確保し、そのノードに行くべき Pod なら toleration を追加します。
5. PVC がバインドされない #
ボリュームを使う Pod は、PVC が準備できて初めてスケジュールされます。waiting for a volume to be created や、PVC が Pending ならこちらです。StorageClass 名の打ち間違い、プロビジョナーの未インストール、そして WaitForFirstConsumer モードで他の制約と絡んでノードが決められないケースがよくあります。AZ のあるクラウドでは「ボリュームは a ゾーンにあるのに、スケジュール可能なノードは b ゾーンだけ」というゾーンの不一致も常連です。
- 確認:
kubectl get pvcとkubectl describe pvcの Events を見ます。 - 対処: StorageClass の確認と、ゾーン分散の見直し(ボリュームを使うワークロードの affinity はゾーンと一緒に設計)です。
6. ResourceQuota・LimitRange 違反 #
ネームスペースにクォータが掛かっていると、ノードに空きがあっても拒否されます。この場合 Pod が作成すらされないか(ReplicaSet の Events に記録)、exceeded quota のメッセージが残ります。新しいチームのオンボーディングでデフォルトのクォータをコピーして使っていて引っかかるパターンが多いのです。
- 確認:
kubectl describe quota -n <ns>と ReplicaSet・Deployment の Events を見ます。 - 対処: クォータの調整か、ワークロードの requests の縮小です。
7. Cluster Autoscaler が増えないケース #
「オートスケーラーがあるからノードは自動で増えるはず」が裏切られるケースです。ノードグループの上限到達、クラウド側のインスタンス上限・容量不足(特定タイプの品切れ)、そして Pod 自身の制約(affinity・taint)のせいで、増やしてもどうせ行けないノードだから増やさないという場合があります。Autoscaler は理由をイベントとログに残します。
- 確認:
kubectl describe podのNotTriggerScaleUpイベントと autoscaler のログを見ます。 - 対処: ノードグループ上限の引き上げ、インスタンスタイプの多様化、Pod の制約の緩和です。
診断手順のまとめ #
kubectl describe pod— まず Events を読みます。FailedScheduling のメッセージが原因の分布を教えてくれます。- Insufficient なら
describe nodeの Allocated resources で予約の飽和を確認し、実使用と突き合わせて過大な requests を探します。 - selector/taint ならノードのラベル・taint とマニフェストを突き合わせます。
- ボリュームのワークロードなら PVC の状態とゾーン配置を見ます。
- クォータのメッセージならネームスペースのクォータを、オートスケーラー環境なら NotTriggerScaleUp イベントを確認します。
まとめ #
- Pending はエラーではなく「行くノードがない」状態で、理由はほぼ常に
describeの FailedScheduling イベントに書いてあります。 - スケジューラは実使用ではなく requests の合計で計算します。実使用が低いのに空きがないなら、まず過大な requests を疑います。
- ラベル・taint の不一致は、マニフェストとノードの状態の突き合わせですぐ捕まります。
- ボリュームの Pod は PVC とゾーン配置まで、オートスケーラー環境は「なぜ増やさなかったか」のイベントまで確認すれば十分です。
- 同じメッセージが繰り返されるなら、個別の対処より requests の算定基準とノードプールの設計を直すほうが根本的な対策です。