Отладка в Kubernetes: CrashLoopBackOff, Pending и OOMKilled
Продвинутое3 минРедакция Innovialabs
Коротко
Статус пода подсказывает, где искать: Pending — проблема планирования, CrashLoopBackOff — контейнер стартует и падает, OOMKilled — нехватка памяти. Почти всё диагностируется четырьмя командами: get, describe, logs --previous и get events.
Сначала статус, потом причина
Статус пода — это не диагноз, а направление поиска:
| Статус | Что происходит | Где смотреть |
|---|---|---|
| Pending | Под не может быть размещён на узле | describe, events |
| CrashLoopBackOff | Контейнер запускается и падает, Kubernetes увеличивает паузу между рестартами | logs --previous, exit code |
| OOMKilled | Процесс убит ядром за превышение memory limit | describe, метрики памяти |
| ImagePullBackOff | Образ не скачивается | describe, events |
Базовый набор команд
Почти любая диагностика начинается с одних и тех же шагов:
kubectl get pods -n <ns> -o wide
kubectl describe pod <pod> -n <ns>
kubectl logs <pod> -n <ns> --previous
kubectl get events -n <ns> --sort-by=.lastTimestamp
- describe показывает состояние контейнеров,
Last State, exit code, причину и события пода. - logs --previous выводит логи предыдущего, упавшего экземпляра контейнера. Без флага вы часто видите пустой лог только что перезапущенного процесса.
- events — хронология: планирование, скачивание образа, проверки probes, выселения.
CrashLoopBackOff: контейнер стартует и падает
Сначала смотрите exit code в describe:
- 1 или другой код приложения — ошибка в самом приложении. Ищите stack trace в
logs --previous. - 137 — процесс получил SIGKILL. Часто это OOMKilled или убийство после проваленной liveness probe.
- 0 — процесс завершился «успешно», но Kubernetes ожидает долгоживущий сервис. Например, команда запуска отработала и вышла.
Типичные причины:
- Нет конфигурации. Отсутствует переменная окружения, Secret или ConfigMap, приложение падает при старте.
- Недоступна зависимость. База данных или другой сервис не отвечает, а приложение не умеет ждать.
- Неверная команда или entrypoint. Опечатка в
command/args, не тот рабочий каталог. - Слишком агрессивная liveness probe. Приложение стартует дольше, чем
initialDelaySeconds, и его убивают до готовности. Решение — startupProbe для медленного старта. - Права доступа. Контейнер запускается не от того пользователя и не может писать в нужный каталог.
Если логов нет совсем, временно замените команду на sleep и зайдите внутрь через kubectl exec, чтобы проверить окружение вручную. Не оставляйте это в продакшене.
Pending: под не может найти узел
В describe в разделе Events будет сообщение планировщика. Частые варианты:
- Insufficient cpu/memory — ни на одном узле не хватает ресурсов под requests. Уменьшите requests, добавьте узлы или проверьте autoscaler.
- didn't match node selector / affinity — нет узла с нужными метками.
- had taint that the pod didn't tolerate — узлы помечены taint, а у пода нет toleration.
- unbound PersistentVolumeClaim — том не создан или нет подходящего StorageClass.
OOMKilled: процесс убит за память
В describe видно Reason: OOMKilled и exit code 137. Действуйте так:
- Посмотрите реальное потребление памяти по метрикам за период до падения.
- Если пик стабилен и предсказуем — поднимите memory limit с запасом.
- Если потребление растёт постоянно — ищите утечку памяти, повышение лимита лишь отсрочит падение.
- Проверьте, знает ли runtime о лимите контейнера: настройки heap в JVM, Node.js, Go.
Отличайте OOMKilled контейнера от выселения (Evicted): во втором случае памяти не хватило всему узлу, и kubelet вытесняет поды по QoS-классам.
Частые ошибки при отладке
- Смотреть
kubectl logsбез--previousи делать вывод, что логов нет. - Лечить CrashLoopBackOff увеличением ресурсов, не прочитав exit code.
- Одинаковые liveness и readiness probes: проблема с зависимостью вызывает рестарты вместо временного исключения из трафика.
- Игнорировать events: они хранятся ограниченное время, поэтому их стоит смотреть сразу или собирать в систему логирования.