Урок по треку Docker и Kubernetes 8 мин

Отладка в Kubernetes за 8 минут

CrashLoopBackOff, OOMKilled, ImagePullBackOff, Pending, kubectl describe, logs --previous и events.

«Под в CrashLoopBackOff, что будешь делать?» это самый частый практический вопрос по Kubernetes. Хороший ответ показывает порядок действий, а не перечисление команд. Порядок почти всегда одинаковый: статус → события → логи → внутрь контейнера → вокруг (узел, сеть, зависимости).

Базовый набор команд

kubectl get pods -n prod -o wide                  # статус, рестарты, узел
kubectl describe pod api-7c9f -n prod             # состояние контейнеров, причины, события
kubectl get events -n prod --sort-by=.lastTimestamp
kubectl logs api-7c9f -n prod                     # логи текущего контейнера
kubectl logs api-7c9f -n prod --previous          # логи упавшего экземпляра
kubectl logs api-7c9f -n prod -c sidecar          # конкретный контейнер
kubectl logs deploy/api -n prod --since=10m
kubectl exec -it api-7c9f -n prod -- sh
kubectl debug -it api-7c9f -n prod --image=nicolaka/netshoot --target=api
kubectl debug node/worker-2 -it --image=ubuntu
kubectl top pods -n prod

kubectl debug добавляет в под эфемерный контейнер с нужными утилитами. Это спасает, когда образ distroless и внутри нет даже shell. --target даёт доступ к процессам целевого контейнера.

Статусы и что за ними стоит

Pending

Под не назначен на узел (или ждёт томов). В describe в событиях причина от планировщика:

  • Insufficient cpu / Insufficient memory: requests не помещаются ни на один узел. Уменьшить requests, добавить узлы, проверить автоскейлер.
  • didn't match Pod's node affinity/selector: nodeSelector или affinity указывают на несуществующие метки.
  • had untolerated taint: узлы с taint, а у пода нет toleration.
  • pod has unbound immediate PersistentVolumeClaims: PVC не привязался (нет StorageClass, нет подходящего PV, конфликт зон).
  • Превышена ResourceQuota namespace: тогда под даже не создаётся, ошибка видна в событиях ReplicaSet.

ContainerCreating надолго

Проблемы с томами (диск не подключается к узлу, Multi-Attach), с сетью (CNI не выдал IP), с секретом или ConfigMap, на который ссылается под (не существует).

ImagePullBackOff / ErrImagePull

Kubelet не может скачать образ:

  • опечатка в имени или теге, тега не существует;
  • приватный реестр без imagePullSecrets или с неверными учётными данными;
  • лимиты Docker Hub на анонимные скачивания;
  • образ под другую архитектуру (тогда скорее exec format error при старте);
  • узел не видит реестр по сети или DNS.

Точный текст ошибки в событиях describe.

CrashLoopBackOff

Контейнер стартует и падает, kubelet перезапускает его с растущей паузой (10 с, 20 с, 40 с... до 5 минут). Это не причина, а симптом. Где искать:

  1. kubectl describe pod: Last State: Terminated, Reason и Exit Code.
  2. kubectl logs --previous: логи именно упавшего экземпляра (текущий может ещё не успеть ничего написать).
  3. Расшифровка кода выхода:
    • 1 или другой код приложения: исключение при старте, нет переменной окружения, не подключилась база, ошибка конфига.
    • 137 (128 + 9, SIGKILL): OOMKilled (видно в Reason) или убит после проваленной liveness probe.
    • 143 (128 + 15, SIGTERM): остановлен штатно, например при провале liveness, и успел выйти сам.
    • 126, 127: не найдена или не исполняется команда из command/ENTRYPOINT.
    • 0 при restartPolicy: Always: процесс просто завершился, а должен работать вечно (например, демон ушёл в фон).
  4. Если процесс падает мгновенно и логов нет: временно заменить команду на sleep infinity (или kubectl debug --copy-to с другой командой) и запустить приложение руками внутри.

OOMKilled

Reason: OOMKilled, Exit Code: 137. Контейнер превысил limits.memory. Разобраться, утечка это или лимит просто мал: графики памяти до падения, kubectl top. Для JVM и Node проверить, что куча меньше лимита. Если OOM приходит от узла (нехватка памяти на узле), поды могут быть вытеснены (статус Evicted), это другой механизм.

Running, но не работает

  • READY 0/1: не проходит readiness. describe покажет Readiness probe failed с ответом.
  • Всё Ready, но запросы не доходят: путь Service → EndpointSlice → под (модуль про сервисы), NetworkPolicy, DNS.
  • Работает, но медленно: троттлинг CPU, соседи по узлу, зависимости.

Terminating навсегда

Под завис в удалении: финализаторы на объекте, недоступный узел, том не отключается. Смотреть metadata.finalizers и состояние узла. --force --grace-period=0 удаляет объект из API, но процесс на недоступном узле может остаться жив: для StatefulSet это риск двух экземпляров.

Проблемы узла

kubectl get nodes, describe node: условия MemoryPressure, DiskPressure, PIDPressure, NetworkUnavailable. DiskPressure часто от образов и логов: kubelet начинает сборку мусора образов и вытеснение подов. На узле: journalctl -u kubelet, crictl ps, crictl logs (аналог docker-команд для CRI).

Методика в одну строку

Что видит Kubernetes (get, describe, events) → что говорит приложение (logs, --previous) → что внутри (exec, debug) → что вокруг (узел, сеть, зависимости, последние изменения: что деплоили?). И всегда спрашивать: что менялось перед началом проблемы.

Частые ошибки

  • Смотреть kubectl logs без --previous у падающего контейнера.
  • Удалять под, не прочитав describe: улика пропадает.
  • Путать OOMKilled (лимит контейнера) и Evicted (нехватка на узле).
  • Лечить CrashLoopBackOff увеличением initialDelaySeconds, не разобравшись в причине.
  • Забывать про events: они хранятся ограниченное время (по умолчанию около часа).

Что спросят на собесе

  • Под в CrashLoopBackOff: порядок действий.
  • Exit code 137 и 143: что значат.
  • Под в Pending: какие причины и где их увидеть.
  • ImagePullBackOff: что проверить.
  • Как отлаживать distroless-контейнер без shell.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.