Прямой ответ
Отладка пода строится на четырёх инструментах: describe показывает события и причину сбоя, logs — вывод приложения, exec — доступ внутрь работающего контейнера, debug — временный контейнер там, где exec невозможен.
Первый шаг почти всегда один и тот же:
kubectl describe pod <имя> -n <namespace>
kubectl get events -n <namespace> --sort-by=.lastTimestamp | tail -30
В разделе Events видны причины, которых нет в логах приложения: нехватка ресурсов, сбой монтирования тома, ошибка загрузки образа, провал пробы готовности.
Логи:
kubectl logs <pod> -n <ns> -f # текущий вывод с отслеживанием
kubectl logs <pod> -c <контейнер> # конкретный контейнер пода
kubectl logs <pod> --previous # вывод ПЕРЕД перезапуском
kubectl logs <pod> --since=10m --timestamps # окно и метки времени
kubectl logs -l app=api --all-containers # по метке, все контейнеры
Флаг --previous критичен при CrashLoopBackOff: текущий контейнер ещё не успел ничего вывести, а причина содержится в логе завершённого экземпляра.
Доступ внутрь:
kubectl exec -it <pod> -c <контейнер> -- /bin/sh
kubectl exec <pod> -- cat /etc/resolv.conf
kubectl port-forward <pod> 8080:8080
kubectl cp <ns>/<pod>:/var/log/app.log ./app.log
Если в образе нет оболочки и утилит — а в минимальных образах её обычно нет — используют ephemeral-контейнер:
kubectl debug -it <pod> --image=busybox --target=<контейнер> -- sh
kubectl debug node/<узел> -it --image=ubuntu
Ephemeral-контейнер добавляется в существующий под, разделяет с целевым контейнером пространство процессов (при включённом shareProcessNamespace) и не удаляется до пересоздания пода. Это единственный способ получить сетевые утилиты и отладчик рядом с приложением, не меняя образ.
Практика: разбор типовых состояний
Pending. Под создан, но не запущен. В событиях обычно видно одну из причин:
| Событие | Причина |
|---|---|
FailedScheduling + Insufficient cpu/memory |
Не хватает ресурсов на узлах |
FailedScheduling + node(s) had untolerated taint |
Под не подходит под ограничения узлов |
FailedAttachVolume |
Том не подключается, часто из-за привязки к зоне |
Проверка ёмкости узлов: kubectl describe node <узел> в разделе Allocated resources.
ImagePullBackOff. Образ не загружается. Причины: опечатка в имени или теге, отсутствие права доступа к приватному реестрию, отсутствие imagePullSecrets в namespace.
CrashLoopBackOff. Контейнер завершается и перезапускается. Порядок действий:
kubectl logs --previous— вывод до падения.kubectl describe pod— код завершения и причина. Код 137 означает принудительное завершение, обычно из-за превышения лимита памяти; 143 — штатный SIGTERM.- Проверка пробы
liveness: если приложение долго стартует, а проба срабатывает рано, под попадает в цикл перезапусков без реальной ошибки.
Running, но не обслуживает трафик. Обычно провалена проба readiness, либо селектор сервиса не совпадает с метками пода:
kubectl get endpoints <service> -n <ns> # пустой список — проблема в селекторе
kubectl get pod <pod> --show-labels
Сравнение пода с эталоном. Если один под из набора работает иначе, полезно сравнить его с исправным: метки, версию образа, переменные окружения и фактически применённую конфигурацию. Расхождение часто объясняется тем, что под не был пересоздан после изменения ConfigMap и работает со старой версией.
Проверка сети изнутри:
kubectl debug -it <pod> --image=nicolaka/netshoot --target=app -- bash
# внутри: nslookup <service>, curl -v http://<service>:8080/healthz
Рекомендации:
- Начинайте с
describeи событий, а не с логов. Значительная часть проблем возникает до старта приложения и в его логах не отражается. - Логи должны быть структурированы и содержать идентификатор запроса — иначе разбор в кластере из десятков подов превращается в поиск по времени; см. статью про структурированные логи.
- Держите минимальный образ, но предусмотрите отладку. Отсутствие оболочки в образе правильно с точки зрения безопасности, но требует привычки использовать
kubectl debug. - Проверяйте ресурсы при необъяснимых перезапусках. Код 137 почти всегда означает превышение лимита памяти — детали в статье про requests и limits.
- Используйте
port-forwardдля проверки приложения в обход балансировщика. Если через порт-форвардинг работает, а через сервис — нет, проблема в сети или конфигурации ingress.
Практический сценарий: под в CrashLoopBackOff без ошибок в логах
Контекст: под платёжного сервиса перезапускался каждые 30 секунд. kubectl logs возвращал пустой вывод, в приложении ошибок не было.
- Первый шаг.
kubectl logs <pod> --previousпоказал последнюю строку перед падением: исключение при подключении к базе по адресу, которого не существовало. - Проверка событий.
kubectl describe podпоказал код завершения 1 и отсутствие срабатываний проб: приложение падало до запуска HTTP-сервера. - Проверка конфигурации.
kubectl get cm <имя> -o yamlвыявил адрес базы из предыдущего окружения. Конфигурацию скопировали при создании namespace. - Проверка доступности. Временный контейнер подтвердил, что целевое имя не разрешается:
kubectl debug -it <pod> --image=busybox --target=app -- nslookup db-primaryвернул ошибку, аnslookup db-primary.payments.svc.cluster.local— успех. - Действия. (а) Исправили ConfigMap; (б) добавили проверку разрешения имени на старте с явной ошибкой вместо зависания на таймауте; (в) вывели версию конфигурации в лог при старте, чтобы расхождение было видно сразу; (г) добавили в чек-лист создания окружения шаг проверки ConfigMap; (д) настроили алерт на число перезапусков пода за 10 минут, поскольку до этого проблему заметили по жалобе пользователя.
- Результат. Под запустился; аналогичная ошибка в новом окружении теперь обнаруживается по алерту за минуту, а причина читается из первой строки лога.
Вывод: пустой вывод kubectl logs не означает отсутствие информации — нужен флаг --previous и раздел событий. Временный контейнер закрывает ситуацию, когда в образе нет утилит для проверки сети.
FAQ
Почему kubectl logs ничего не возвращает для упавшего пода?
Текущий контейнер мог не успеть вывести данные. Используйте --previous, чтобы получить лог завершённого экземпляра, и describe pod для кода завершения.
Под перезапускается, но --previous пустой. Что делать?
Если предыдущий контейнер не успел вывести данные, причину ищут в разделе событий и в коде завершения из describe pod. Дополнительно помогает запуск копии пода с командой, которая не завершается, — это даёт время на осмотр состояния без цикла перезапусков.
Можно ли отлаживать под, если в образе нет shell?
Да, через kubectl debug --image=<образ с утилитами> --target=<контейнер>. Временный контейнер добавляется в под и разделяет его сетевое пространство имён.
Что показывает kubectl get events и почему там не всё?
События хранятся ограниченное время, обычно около часа. Для длительного анализа нужен экспорт событий во внешнее хранилище.
Как отладить приложение на конкретном узле?
kubectl debug node/<узел> -it --image=ubuntu запускает контейнер в пространстве имён узла с доступом к его файловой системе. Требуются права на создание подов в соответствующем namespace.
Источники: Kubernetes docs (debug application, debug running pod, ephemeral containers) — ссылки в шапке.