Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Отладка подов: логи, exec и ephemeral-контейнеры

Отладка подов в Kubernetes: логи, describe, exec и debug

Отладка подов: kubectl describe и events, логи с --previous, exec в контейнер, ephemeral-контейнеры и разбор типовых состояний Pending и CrashLoopBackOff.

Прямой ответ

Отладка пода строится на четырёх инструментах: 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. Контейнер завершается и перезапускается. Порядок действий:

  1. kubectl logs --previous — вывод до падения.
  2. kubectl describe pod — код завершения и причина. Код 137 означает принудительное завершение, обычно из-за превышения лимита памяти; 143 — штатный SIGTERM.
  3. Проверка пробы 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

Рекомендации:

  1. Начинайте с describe и событий, а не с логов. Значительная часть проблем возникает до старта приложения и в его логах не отражается.
  2. Логи должны быть структурированы и содержать идентификатор запроса — иначе разбор в кластере из десятков подов превращается в поиск по времени; см. статью про структурированные логи.
  3. Держите минимальный образ, но предусмотрите отладку. Отсутствие оболочки в образе правильно с точки зрения безопасности, но требует привычки использовать kubectl debug.
  4. Проверяйте ресурсы при необъяснимых перезапусках. Код 137 почти всегда означает превышение лимита памяти — детали в статье про requests и limits.
  5. Используйте port-forward для проверки приложения в обход балансировщика. Если через порт-форвардинг работает, а через сервис — нет, проблема в сети или конфигурации ingress.

Практический сценарий: под в CrashLoopBackOff без ошибок в логах

Контекст: под платёжного сервиса перезапускался каждые 30 секунд. kubectl logs возвращал пустой вывод, в приложении ошибок не было.

  1. Первый шаг. kubectl logs <pod> --previous показал последнюю строку перед падением: исключение при подключении к базе по адресу, которого не существовало.
  2. Проверка событий. kubectl describe pod показал код завершения 1 и отсутствие срабатываний проб: приложение падало до запуска HTTP-сервера.
  3. Проверка конфигурации. kubectl get cm <имя> -o yaml выявил адрес базы из предыдущего окружения. Конфигурацию скопировали при создании namespace.
  4. Проверка доступности. Временный контейнер подтвердил, что целевое имя не разрешается: kubectl debug -it <pod> --image=busybox --target=app -- nslookup db-primary вернул ошибку, а nslookup db-primary.payments.svc.cluster.local — успех.
  5. Действия. (а) Исправили ConfigMap; (б) добавили проверку разрешения имени на старте с явной ошибкой вместо зависания на таймауте; (в) вывели версию конфигурации в лог при старте, чтобы расхождение было видно сразу; (г) добавили в чек-лист создания окружения шаг проверки ConfigMap; (д) настроили алерт на число перезапусков пода за 10 минут, поскольку до этого проблему заметили по жалобе пользователя.
  6. Результат. Под запустился; аналогичная ошибка в новом окружении теперь обнаруживается по алерту за минуту, а причина читается из первой строки лога.

Вывод: пустой вывод 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) — ссылки в шапке.