kai.kzLab

Pending: почему под не запускается и что значит «0/3 nodes are available»

Команда KAI Lab · · 8 мин чтения

Статус Pending значит, что Kubernetes принял под, но ещё не запустил его контейнеры: чаще всего планировщик не нашёл подходящую ноду, реже под уже назначен на ноду и ждёт образ или том. Главные причины — на нодах не хватает CPU или памяти под requests, ноды закрыты taints, не совпадают nodeSelector или affinity, PersistentVolumeClaim не привязан к тому.

Pending сам не проходит. В документации Kubernetes на примере пода с неподъёмным запросом памяти так и сказано: он будет висеть в Pending бесконечно. Хорошая новость: планировщик всегда объясняет, почему не может разместить под, и это объяснение надо только прочитать.

Что значит Pending

Pending — одна из пяти фаз пода. По документации, это время, пока под ждёт планирования, и время, пока скачиваются образы. Поэтому первый вопрос — назначен ли под на ноду:

$ kubectl get pods -n shop -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
search-7b6d5f8c9-p4n2x   0/1     Pending   0          14m   <none>   <none>   <none>           <none>

NODE <none> — планировщик под не разместил, дальше читайте эту статью. Если нода есть, а в STATUS стоит ContainerCreating, ErrImagePull или CreateContainerConfigError, фаза пода по-прежнему Pending, но проблема уже на ноде: образ, тома или конфигурация. Для них есть отдельные разборы: ImagePullBackOff и CreateContainerConfigError.

Все такие поды в кластере показывает одна команда:

$ kubectl get pods -A --field-selector=status.phase=Pending

Главный инструмент: событие FailedScheduling

$ kubectl describe pod search-7b6d5f8c9-p4n2x -n shop
...
Events:
  Type     Reason            Age                 From               Message
  ----     ------            ----                ----               -------
  Warning  FailedScheduling  2m15s (x4 over 14m)  default-scheduler  0/4 nodes are available: 1 node(s) had untolerated taint(s), 3 Insufficient memory. preemption: 0/4 nodes are available: 1 Preemption is not helpful for scheduling, 3 No preemption victims found for incoming pod.

Сообщение читается так: всего в кластере 4 ноды, ни одна не подошла. Одну отсекла метка-запрет (taint), на трёх не хватает памяти. Часть после preemption: объясняет, почему не помогло вытеснение подов с меньшим приоритетом: на ноде с taint это ничего не изменит, а на остальных нет подходящих жертв.

Типичные фрагменты этого сообщения и их смысл — в таблице ниже. Одна нода может попасть сразу под несколько причин. До Kubernetes 1.34 сообщение про taint включало его ключ, например node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, в новых версиях ключа нет, и его надо смотреть на самих нодах.

Причины Pending: таблица

ПричинаЧто в FailedSchedulingКак исправить
Не хватает CPU или памяти под requestsInsufficient cpu, Insufficient memoryснизить requests до реальных, освободить место, добавить ноды
Taint без tolerationnode(s) had untolerated taint(s)toleration в поде или снять taint с ноды
Не совпадают меткиnode(s) didn't match Pod's node affinity/selectorпоставить метку на ноду или поправить селектор
PVC не привязанpod has unbound immediate PersistentVolumeClaimsисправить StorageClass, проверить provisioner, создать PV
Том в другой зоне или на другой нодеnode(s) didn't match PersistentVolume's node affinityноды в зоне тома или том в нужной зоне
Занят hostPortnode(s) didn't have free ports for the requested pod portsубрать hostPort, открыть сервис через Service
Ноды выведены из планированияnode(s) were unschedulablekubectl uncordon, если обслуживание закончено

Не хватает ресурсов

Планировщик смотрит на requests, а не на реальное потребление. Документация Kubernetes подчёркивает: даже если нода почти простаивает, под не попадёт на неё, когда сумма requests не помещается в доступные ресурсы. Сколько уже занято, показывает kubectl describe node:

$ kubectl describe node worker-2
...
Allocatable:
  cpu:                3920m
  memory:             7862592Ki
  pods:               110
...
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests      Limits
  --------           --------      ------
  cpu                3450m (88%)   6 (153%)
  memory             7012Mi (91%)  9Gi (120%)

Allocatable меньше физической памяти ноды: часть резервируется под систему и kubelet. Если поду нужен 1Gi, а свободно около 660Mi по requests, он сюда не влезет, сколько бы ни показывал kubectl top.

Что делать:

  • сравнить requests пода с реальным потреблением и снизить их, если они завышены;
  • проверить, что под вообще помещается хоть на одну ноду: запрос cpu: 1.1 на нодах с 1 CPU не разместится никогда;
  • убрать ненужные поды или добавить ноды. Если в кластере работает Cluster Autoscaler, в событиях пода появятся TriggeredScaleUp или NotTriggerScaleUp с объяснением, почему новая нода не поможет;
  • для важных сервисов задать PriorityClass: под с высоким приоритетом может вытеснить менее важные.

Если в пространстве имён исчерпана ResourceQuota, под не появится вовсе. Тогда ищите событие FailedCreate с текстом exceeded quota у ReplicaSet, а не у пода.

Taints без tolerations

Taint — метка-запрет на ноде: поды без подходящего toleration туда не планируются. Посмотреть их можно так:

$ kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints[*].key
NAME       TAINTS
cp-1       node-role.kubernetes.io/control-plane
worker-1   dedicated
worker-2   <none>
worker-3   node.kubernetes.io/disk-pressure

Три типичных источника:

  • Управляющие ноды. kubeadm ставит на них node-role.kubernetes.io/control-plane:NoSchedule, и обычные поды туда не попадают. Так и задумано.
  • Выделенные ноды. Например, dedicated=gpu:NoSchedule для нод с видеокартами. Нужен toleration в поде.
  • Состояние ноды. Kubernetes сам ставит taints node.kubernetes.io/disk-pressure, memory-pressure, not-ready, unreachable. Если ноды под давлением диска, лечить нужно диск: см. статьи под Evicted и no space left on device в Docker.

Toleration в манифесте пода по образцу из документации:

tolerations:
- key: "dedicated"
  operator: "Equal"
  value: "gpu"
  effect: "NoSchedule"

Снять лишний taint с ноды — та же команда с минусом в конце: kubectl taint nodes worker-1 dedicated=gpu:NoSchedule-. Учтите, что toleration только разрешает попасть на ноду, но не притягивает к ней. Чтобы под шёл именно на выделенные ноды, к toleration добавляют nodeSelector или affinity.

nodeSelector и affinity

Если под требует ноды с меткой, которой нет ни у одной ноды, планировщик ответит node(s) didn't match Pod's node affinity/selector. Проверка в две команды:

$ kubectl get pod search-7b6d5f8c9-p4n2x -n shop -o jsonpath='{.spec.nodeSelector}'
{"disktype":"ssd"}
$ kubectl get nodes -l disktype=ssd
No resources found

Решение — поставить метку нужной ноде (kubectl label nodes worker-3 disktype=ssd) или поправить селектор. Для requiredDuringSchedulingIgnoredDuringExecution правило жёсткое, как у nodeSelector. Если допустимо запускаться и на других нодах, используйте preferredDuringSchedulingIgnoredDuringExecution.

Метки меняются и при обновлениях. 14 марта 2023 года Reddit был недоступен 314 минут: при обновлении кластера до Kubernetes 1.24 kubeadm снял с управляющих узлов метку node-role.kubernetes.io/master, а по ней Calico выбирал узлы для маршрутизации между подами. Подробности — в разборе сбоя Reddit. Вывод для Pending тот же: перед обновлением ищите все селекторы по меткам, которые могут исчезнуть.

PersistentVolumeClaim не привязан

Под с томом не запланируют, пока его PVC не связан с PersistentVolume, если у StorageClass режим привязки Immediate. Смотрите на сам PVC:

$ kubectl get pvc -n shop
NAME            STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
data-search-0   Pending                                      premium-rwo    <unset>                 14m
$ kubectl describe pvc data-search-0 -n shop
...
  Warning  ProvisioningFailed  12s (x58 over 14m)  persistentvolume-controller  storageclass.storage.k8s.io "premium-rwo" not found

Что бывает в событиях PVC:

  • storageclass.storage.k8s.io "…" not found — опечатка в storageClassName или класс не создан. Список классов — kubectl get storageclass, класс по умолчанию помечен (default).
  • no persistent volumes available for this claim and no storage class is set — динамического создания томов нет, а подходящего PV никто не завёл.
  • Waiting for a volume to be created either by the external provisioner … — ждём CSI-драйвер. Если это надолго, проверьте, что его поды работают.
  • waiting for first consumer to be created before binding — это нормально для режима WaitForFirstConsumer: том создадут, когда планировщик выберет ноду.

Если PVC привязан, но в FailedScheduling node(s) didn't match PersistentVolume's node affinity, том создан в другой зоне доступности или это локальный том другой ноды. Под может жить только там, где доступен его диск.

Частые вопросы

Сколько под может висеть в Pending? Сколько угодно. Под не перейдёт в ошибку сам: планировщик будет повторять попытки, когда в кластере что-то меняется, например добавилась нода или освободились ресурсы. Pending дольше пары минут — повод читать события.

Почему Insufficient memory, если kubectl top показывает свободную память? Планировщик считает requests, а не фактическое потребление. Если соседние поды запросили память с запасом и не используют её, для планировщика она всё равно занята. Сравните Allocated resources в kubectl describe node с реальным потреблением и снизьте завышенные requests.

Что значит «Preemption is not helpful for scheduling»? Планировщик проверил, поможет ли вытеснить поды с меньшим приоритетом, и понял, что нет. Обычно причина не в ресурсах: taint, несовпадающие метки или том в другой зоне. Вытеснение освобождает CPU и память, но не снимает taint.

Под в ContainerCreating — это тоже Pending? Да, фаза та же, но под уже на ноде. Планирование прошло, и проблема в запуске: образ скачивается или не скачивается, том не подключился, нет Secret. Смотрите события FailedMount, Failed и статус контейнера.

Как быстро проверить весь кластер на такие проблемы? kubectl get pods -A --field-selector=status.phase=Pending покажет все поды в фазе Pending, а kubectl get events -A --field-selector reason=FailedScheduling — объяснения планировщика. Разобрать такое сообщение вслух часто просят на собеседовании по Kubernetes.

Потренироваться на практике

В симуляторе DevOps KAI Lab в месяце про Kubernetes есть тикет «Под висит в Pending»: разработчик уверен, что места в кластере полно, а под не запускается. Консоль в браузере отдаёт реальные выводы kubectl, и причину нужно найти самому. Проверить себя можно в бесплатном тесте навыков DevOps.

Источники

Читать дальше