冒頭まとめ

Pending は、コンテナの状態を表す名前ではありません。対象全体がどの段階にあるかを表す名前です。公式の定義には、対象は受け付けられたがコンテナの1つ以上がまだ起動していない状態であり、これには機器に割り当てられる前の時間と、イメージを取得している時間の両方が含まれる、と書かれています。

この一文が示すとおり、Pending は性質の違う2つの状況を1語でまとめています。置き場所がまだ決まっていない状況と、置き場所は決まったが起動の準備が終わっていない状況です。前者は配置を担う仕組みの問題で、後者は機器の上の仕組みの問題です。原因も対処もまったく違います。

分岐は1点で決まります。配置先が決まっているかどうかです。決まっていなければ配置の問題、決まっていれば起動準備の問題です。この確認を最初に行えば、調べる範囲が半分になります。

配置の問題であれば、記録に理由が残ります。文言は「0/N nodes are available」で始まり、そのあとに理由が続きます。この形式はソースに定義されていて、理由ごとに何台の機器が該当したかという数が付きます。

読み方に注意点が1つあります。この数は理由ごとの機器の台数で、1台の機器が複数の理由に数えられることがあります。したがって、並んでいる数字を足しても総数に一致するとは限りません。合計が合わないのは異常ではありません。

エラーの概要

一覧では、準備できているコンテナの数が0のまま止まります。

NAME                    READY   STATUS    RESTARTS   AGE
my-app-5f8c7d9b4-nq3vt  0/1     Pending   0          6m41s

配置が済んでいない場合、記録に理由が残ります。

Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  6m (x5 over 6m)    default-scheduler  0/5 nodes are available: 3 Insufficient memory, 2 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }.

この例では、5台のうち3台がメモリの空き不足、2台が許容していない印によって除外されています。理由が2つ並んでいるので、対処も2通り考えられます。空きを増やすか、印を許容する設定を加えるかです。

配置が済んでいる場合は、この記録が出ません。代わりに、コンテナごとの待機の理由が入ります。この場合、Pending という表示のままイメージの取得や保管領域の準備が進んでいます。

まず最初に:配置先が決まっているかを見る

第一に、配置先が入っているかを確認します。

kubectl get pod <ポッド名> -o jsonpath='{.spec.nodeName}{"\n"}'

何も出なければ、まだ置き場所が決まっていません。配置の問題として調べます。機器の名前が出れば、置き場所は決まっています。起動準備の問題として調べます。

第二に、配置の問題であれば、理由の文言を取り出します。

kubectl describe pod <ポッド名> | grep -A5 FailedScheduling

第三に、起動準備の問題であれば、コンテナごとの待機の理由を見ます。

kubectl get pod <ポッド名> \
  -o jsonpath='{range .status.containerStatuses[*]}{.name}{"  "}{.state.waiting.reason}{"\n"}{end}'

よくある原因と解決手順

原因1:資源の空きが足りない

文言に空き不足を示す理由が入る場合です。要求している量に対して、どの機器にも空きがありません。

まず、機器ごとの割り当て状況を確認します。ここで見るべきは実際の使用量ではなく、要求量の合計です。配置の判断に使われるのは要求量だからです。

kubectl describe node <機器名> | sed -n '/Allocated resources/,/^Events/p'

Before(要求量が機器の容量に対して大きすぎる):

resources:
  requests:
    memory: "32Gi"
    cpu: "8"

After(実測に基づいて要求量を下げる):

resources:
  requests:
    memory: "2Gi"
    cpu: "500m"
  limits:
    memory: "4Gi"

要求量を実測より大きく書いていると、使っていない量のぶんだけ機器を占有します。空きが無いように見えて、実際には余っている、という状況はこの形です。

要求量が適正でも空きが足りない場合は、機器を増やすか、既存の対象を整理するかになります。

原因2:条件に合う配置先が無い

文言に、許容していない印や、条件に一致しないという理由が入る場合です。資源の空きとは無関係で、そもそも候補から外れています。

印による除外であれば、許容する設定を加えます。

spec:
  tolerations:
    - key: "dedicated"
      operator: "Equal"
      value: "batch"
      effect: "NoSchedule"

配置先を名札で限定している場合は、その名札を持つ機器が実在するかを確認します。

kubectl get nodes --show-labels
kubectl get pod <ポッド名> -o jsonpath='{.spec.nodeSelector}{"\n"}'

指定した名札がどの機器にも付いていなければ、永久に配置されません。名札を付けるか、指定を外すかのどちらかです。

同じ種類の対象を同じ機器に置かない条件を付けている場合も、機器の台数が足りないと配置できません。この場合、条件を必須から推奨に変えると配置は通りますが、分散の保証は失われます。

原因3:保管領域が結び付いていない

保管領域を要求しているのに、割り当てが決まっていない場合です。要求の状態を確認します。

kubectl get pvc -n <区画名>

待機中のままであれば、原因は2つ考えられます。条件に合う領域が用意されていない場合と、最初の利用者が決まるまで割り当てを保留する設定になっている場合です。

後者の場合、配置と割り当てが互いを待つ状態にはなりません。配置が先に決まり、それから割り当てが行われます。したがって、この設定で長く待機している場合は、配置のほうが別の理由で止まっています。原因1と原因2に戻ってください。

原因4:配置は済んでいるが起動準備で止まっている

配置先が入っているのに Pending のままの場合です。この段階では、イメージの取得、保管領域の接続、初期化用のコンテナの実行などが行われています。

待機の理由を見れば、どこで止まっているかが分かります。取得に失敗している場合は専用の理由が入ります(Kubernetes の ImagePullBackOff の記事)。

初期化用のコンテナが終わらない場合も、この段階に留まります。順番に実行され、すべて成功するまで本体は起動しません。

kubectl get pod <ポッド名> \
  -o jsonpath='{range .status.initContainerStatuses[*]}{.name}{"  "}{.state}{"\n"}{end}'
kubectl logs <ポッド名> -c <初期化用コンテナ名>

接続待ちの処理を初期化用のコンテナに入れている場合、相手が起動しない限り永久に待ちます。待ち時間の上限を設けておくと、原因の特定が早くなります。

原因5:割り当ての上限に当たっている

区画ごとに使用量の上限を設けている場合、上限を超える要求は受け付けられません。この場合、配置以前の段階で止まります。

kubectl get resourcequota -n <区画名>
kubectl describe resourcequota -n <区画名>

上限に達していれば、既存の対象を減らすか、上限を引き上げるかになります。なお、上限を設けている区画では、要求量を書いていない対象が受け付けられない設定になっていることがあります。その場合、要求量を明記すれば通ります。

補足:似ているが別のもの

コンテナが起動したあとに落ちている場合は、別の表示になります。一度は起動しているので、段階としては先へ進んでいます(Kubernetes の CrashLoopBackOff の記事)。

メモリの都合で止められている場合も、起動後の話です(Kubernetes の OOMKilled の記事)。Pending の段階では、まだメモリを使い始めていません。

配置が済んでいるのに機器の側が応答しない場合は、機器そのものの状態を確認してください。機器が正常でない状態にあると、そこへの新しい配置は行われません。

利用者向けの通信への影響は、状態コードとして現れます。準備できている対象が1つも無い状態が続けば 503 になります(Kubernetes の 503 の記事)。

切り分けの順序

  1. 配置先が入っているかを確認する。ここで調べる範囲が半分になる。
  2. 入っていなければ、配置が失敗した記録の文言を読む。
  3. 文言の数字は理由ごとの機器の台数。合計が総数に一致しなくてもよい。
  4. 空き不足なら、機器ごとの要求量の合計を見る。実際の使用量ではない。
  5. 条件不一致なら、指定した名札や印を持つ機器が実在するかを確認する。実在しなければ永久に配置されない。
  6. 配置先が入っているなら、コンテナごとの待機の理由と、初期化用のコンテナの進み具合を見る。
  7. どちらでもなければ、区画ごとの上限を確認する。

確認コマンド集

# 1. 配置先が決まっているかを確認する(最初に行う)
kubectl get pod <ポッド名> -o jsonpath='{.spec.nodeName}{"\n"}'

# 2. 配置が失敗した記録を読む
kubectl describe pod <ポッド名> | grep -A5 FailedScheduling

# 3. 機器ごとの要求量の合計を見る
kubectl describe node <機器名> | sed -n '/Allocated resources/,/^Events/p'

# 4. 機器の名札と、指定している条件を突き合わせる
kubectl get nodes --show-labels
kubectl get pod <ポッド名> -o jsonpath='{.spec.nodeSelector}{"  "}{.spec.tolerations}{"\n"}'

# 5. 保管領域の要求の状態を確認する
kubectl get pvc -n <区画名>

# 6. 初期化用のコンテナの進み具合を見る
kubectl get pod <ポッド名> \
  -o jsonpath='{range .status.initContainerStatuses[*]}{.name}{"  "}{.state}{"\n"}{end}'

# 7. 区画ごとの上限を確認する
kubectl describe resourcequota -n <区画名>

Editor’s Note

配置が失敗したときの文言は、文章ではなく集計表として読むのが正解です。ソースを見ると、この文言は理由ごとに件数を数え上げ、「件数と理由」の組を並べて作られています。組は文字列として並べ替えられ、区切って連結されます。

つまり「3 Insufficient memory, 2 node(s) had untolerated taint」という並びは、1つの説明文ではありません。3台がこの理由で、2台があの理由で除外された、という2件の集計です。それぞれに対処があり、片方だけを直しても配置されるとは限りません。

もう1つ、件数の数え方に癖があります。1台の機器について複数の理由が記録された場合、それぞれが別々に数えられます。したがって、並んでいる件数の合計が機器の総数を超えることがあります。「0/5」なのに件数の合計が7になっていても、記録が壊れているわけではありません。1台が2つの理由で弾かれた、というだけです。

この読み方を知らないと、合計が合わないことに引っかかって時間を使います。逆に、集計表だと分かっていれば、件数の多い理由から手を付けるという優先順位がその場で立ちます。5台中3台が空き不足なら、まず空きを作る。残る2台の印は、そのあとで考えればよい、という判断ができます。

Pending は、待っているだけで何も起きていないように見える状態です。しかし、記録にはなぜ進めないかが数字付きで残っています。一覧を眺めて待つより、この1行を読むほうが早く進みます。


免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアの仕様は予告なく変更されることがあります。最新の情報は各ツールの公式サポートページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。