Pod завис в Pending: как читать события и найти нехватку ресурсов
После увеличения числа реплик часть приложения осталась в Pending. На графике CPU свободен наполовину, поэтому хочется просто перезапустить Pod. Перезапуск не увеличит ёмкость узлов и не изменит правила размещения. Нужен ответ на более узкий вопрос: Kubernetes ещё выбирает узел или уже выбрал его и готовит контейнеры? От этого зависит весь дальнейший поиск.
Отделяем ожидание узла от запуска контейнера
В примерах приложение находится в namespace demo, проблемный Pod называется api-7d9b8f-demo. Подставьте имя из своего вывода. Укажите namespace в каждой команде: одинаковые имена приложений в разных окружениях легко приводят к расследованию чужого объекта.
kubectl -n demo get pod api-7d9b8f-demo -o wide
kubectl -n demo describe pod api-7d9b8f-demo
kubectl -n demo get events --field-selector involvedObject.name=api-7d9b8f-demo --sort-by=.metadata.creationTimestampПустое поле NODE и условие PodScheduled=False указывают на задачу размещения. Событие FailedScheduling обычно содержит причину отказа и число рассмотренных узлов. Если NODE уже заполнено, ищите ниже события загрузки образа, подключения тома и запуска init-контейнеров. Фаза Pending охватывает и эти ожидания; сама по себе она не доказывает нехватку ресурсов. События имеют ограниченный срок хранения, поэтому сохраняйте описание во время проблемы.
Почему свободный CPU на графике не помогает
Планировщик ориентируется на запрошенные ресурсы, requests. Это заявка приложения на место. Текущее использование CPU показывает другую величину: сколько процесс потребляет сейчас. Два почти бездействующих Pod могут зарезервировать весь доступный CPU узла. Показания kubectl top полезны для подбора реалистичной заявки, но не заменяют проверку requests.
kubectl get nodes
kubectl describe node worker-1
kubectl -n demo get pod api-7d9b8f-demo -o yamlВ описании узла найдите Allocatable и таблицу Allocated resources. Capacity включает ресурс узла целиком, тогда как Allocatable показывает объём, который выделяется Pod. В YAML проблемного Pod посмотрите resources.requests каждого контейнера. Для обычного Pod без init-контейнеров и дополнительных особенностей заявки контейнеров суммируются; init-контейнеры, sidecar и overhead требуют отдельного учёта. Проверять только первый контейнер недостаточно.
Допустим, на каждом из двух подходящих узлов осталось по 600m незанятых заявками CPU, а новый Pod просит 1000m. Суммарно свободно 1200m, однако одного подходящего узла нет. Pod не раскладывается по двум узлам. Сообщение Insufficient cpu при этом согласуется с общей свободной ёмкостью кластера. Аналогично проверяется память: важно место на конкретном допустимом узле.
Исправляем заявку с понятным основанием
В Deployment фрагмент ресурсов задают внутри spec.template.spec.containers. Например, для контейнера api исходная конфигурация может выглядеть так:
resources:
requests:
cpu: "1000m"
memory: "512Mi"
limits:
cpu: "2000m"
memory: "1Gi"Уменьшать request только ради исчезновения Pending опасно для качества размещения. Сначала сравните потребление приложения при рабочей нагрузке, время ответа и запас при старте. Если заявка завышена, исправьте шаблон Deployment в его источнике конфигурации. Если она обоснована, нужны дополнительные подходящие узлы либо уменьшение другой нагрузки. Изменение одного limits.cpu при сохранённом request проблему размещения не решает.
Когда ресурсов хватает, читаем оставшуюся часть события
FailedScheduling может одновременно перечислить Insufficient memory, несовпадение nodeSelector и untolerated taint. Это разные причины на разных узлах. Новый большой узел с неподходящими метками не поможет. Сопоставьте spec.nodeSelector, affinity и tolerations Pod с labels и taints узлов. Для Pod с PersistentVolumeClaim дополнительно проверьте состояние PVC и ограничения зоны тома. Сообщение о несвязанном PVC направляет расследование к хранилищу, даже если процесс ещё не запущен.
kubectl -n demo get pvc
kubectl get nodes --show-labels
kubectl -n demo get deployment api -o yamlПосле изменения повторите get pod и describe для нового Pod. Успешное назначение видно по NODE и PodScheduled=True. Затем отдельно убедитесь, что контейнеры запустились и приложение стало Ready. Это две разные границы: решение планировщика устраняет нехватку места, но не проверяет загрузку образа или готовность HTTP-сервера.
Есть ещё одна полезная проверка: сравните Pod с шаблоном контроллера и фактическими значениями после admission. LimitRange может подставить значения по умолчанию, поэтому отсутствие resources в исходном Helm-шаблоне не доказывает отсутствие request у созданного Pod. В расследовании решающей является спецификация, которую получил API. Если же создание Pod вообще запрещено ResourceQuota, новый объект может не появиться: тогда ищите FailedCreate в событиях ReplicaSet, а не ожидайте FailedScheduling у несуществующего Pod.
При оценке добавления узлов проверьте максимальное число Pod на узле и резерв для системных компонентов. Свободной памяти может хватать, а допустимое число размещённых Pod уже исчерпано. Сообщение планировщика позволяет отличить этот случай от нехватки CPU. Изменение request наугад здесь только запутает проверку: сначала должна измениться именно причина, записанная в событии.
Полезный результат расследования — конкретная запись: какой request не помещался, на каких допустимых узлах и какое изменение добавило место. Сохранённое событие до исправления и описание нового Pod после него позволяют проверить решение без догадок по одному зелёному индикатору.
Ещё по теме
- DNS в Kubernetes отвечает медленно: проверяем CoreDNS, search и ndots
- Drain узла остановился на PodDisruptionBudget: как разобраться в ограничении
Продолжить изучение
Хотите увереннее разворачивать приложения и разбирать сбои в кластере? Сети, конфигурация, хранилища и обновления входят в программу «Kubernetes с нуля до PRO».
Продолжить на практике
- Kubernetes с нуля до PRO ↗
Разворачивайте и сопровождайте приложения в Kubernetes: от первого Pod до релизов, сетей, хранилищ, масштабирования и диагностики. Итоговый проект — сервис с базой данных, мониторингом и проверенным откатом.
