← назад к разделу

Инциденты в Kubernetes снаружи выглядят однообразно — «сервис не работает», — но различаются по месту поломки: образ, ресурсы, probes, сеть, само приложение. Эта статья — рабочий набор команд и карта типичных состояний: по симптому → к причине → к действию.

kubectl-набор разработчика

kubectl -n payments get pods                                  # общее состояние: STATUS, RESTARTS, AGE
kubectl -n payments describe pod order-service-xxx           # события пода: почему он такой
kubectl -n payments logs order-service-xxx                   # логи текущего контейнера
kubectl -n payments logs order-service-xxx -p                # логи ПРЕДЫДУЩЕГО (перед рестартом!)
kubectl -n payments logs deploy/order-service --tail=200 -f  # логи всех реплик деплоймента
kubectl -n payments get events --sort-by=.lastTimestamp      # лента событий namespace
kubectl -n payments exec -it order-service-xxx -- sh         # шелл внутри контейнера
kubectl -n payments port-forward svc/order-service 8080:80   # сервис к себе на localhost
kubectl -n payments top pods                                  # фактический CPU/память
kubectl -n payments get endpoints order-service              # кто реально за Service
kubectl -n payments rollout status deploy/order-service      # как идёт выкатка

Две команды недооценены драматически: logs -p — единственный способ увидеть, что было перед рестартом (текущие логи начинаются после), и get events --sort-by — там Kubernetes прямым текстом пишет причины: не хватило памяти на ноде, не смог скачать образ, probe провалилась.

CrashLoopBackOff

Под стартует, падает, Kubernetes перезапускает с нарастающей паузой — и так по кругу. Это не болезнь, а симптом: приложение завершается, причина внутри.

Порядок разбора: logs -p (что сказало приложение перед смертью: стектрейс? «port already in use»? кривой конфиг, заваленный @Validated-валидацией?) → describe pod (exit code: 1 — само упало, см. логи; 137 — убито, см. OOMKilled ниже) → если логов нет вовсе — приложение не дожило до логгера: проверяется entrypoint образа и конфигурация логирования.

Отдельный частый случай — убийство probe-ами: приложение стартует 90 секунд, а liveness начинает стрелять через 30 — под убивается вечно «недозапустившимся». Лечится startup probe с честным failureThreshold (разбор probes).

OOMKilled (exit 137)

Контейнер превысил memory limit и был убит ядром — мгновенно, без стектрейса и прощальных логов. В describe pod: Last State: Terminated, Reason: OOMKilled.

Для JVM-сервиса диагноз почти всегда один из двух: доля heap не согласована с лимитом (MaxRAMPercentage не задан — у старых JVM дефолт 25%, у задранных — не остаётся места не-heap памяти) либо лимит честно мал для рабочей нагрузки. Разбор и правильные настройки — в статье про ресурсы и JVM. Важно не путать: OutOfMemoryError в логах — это не OOMKilled, это heap кончился внутри живого процесса; лечится по-другому.

ImagePullBackOff / ErrImagePull

Нода не может скачать образ. Три причины закрывают почти всё: опечатка в теге (или тег не был запушен — CI собрал, но push упал), нет прав к registry (протух imagePullSecret), registry недоступен. describe pod показывает точную ошибку registry в events. Это единственный инцидент из списка, где код приложения гарантированно ни при чём.

Pending: под никуда не назначен

Под создан, но не запланирован ни на одну ноду. describe pod → events → причина словами: Insufficient memory / Insufficient cpu (ни на одной ноде нет столько свободных requests — кластер «полон», даже если фактическое потребление низкое: планирование идёт по requests, не по факту), либо несовпадение nodeSelector/affinity/taints. Действия разработчика: проверить, не завышены ли requests сервиса; дальше — разговор с платформенной командой про ёмкость.

Мигающая readiness: 503 у соседей

Симптом: сервис «работает», но потребители ловят перемежающиеся 503/таймауты. Причина: readiness probe нестабильна — под выпадает из endpoints и возвращается. Типичные сценарии: в readiness включена нестабильная внешняя зависимость (мигает она — мигает весь сервис), GC-паузы или перегрузка заставляют probe не укладываться в timeout, слишком агрессивные periodSeconds/failureThreshold.

Проверка: kubectl get endpoints несколько раз подряд — список прыгает; describe podReadiness probe failed в events. Решение — пересмотр состава readiness (зависимость, без которой сервис может отвечать деградированно, в probe не входит) и порогов.

«Запрос не доходит»: проверка цепочки

Когда снаружи 502/504, а поды зелёные — проверяется цепочка пути запроса, сверху вниз, каждое звено отдельно:

kubectl -n payments get ingress api                 # 1. Ingress существует, host/path верные?
kubectl -n payments get endpoints order-service    # 2. за Service есть поды?
kubectl -n payments port-forward svc/order-service 8080:80   # 3. Service отвечает?
kubectl -n payments port-forward pod/order-service-xxx 8080:8080  # 4. сам под отвечает?

Где цепочка рвётся — там и слой проблемы: 4 — приложение, 3–2 — selector/readiness, 1 — Ingress/DNS/TLS. Пустые endpoints при живых подах — почти всегда readiness или опечатка в selector (labels пода ≠ selector сервиса — после рефакторинга манифестов случается чаще, чем хочется верить). Не забыть NetworkPolicy: «не доходит» между namespace — возможно, политика, а не поломка.

Под перезапустился «сам»

RESTARTS > 0 без деплоя — у рестарта всегда есть причина, и она записана: describe pod → Last State. Варианты: OOMKilled (выше), liveness probe failed (см. probes — не проверяет ли она БД?), нода ушла в drain/upgrade (это норма: поды обязаны переживать переезд — на то PodDisruptionBudget и minReplicas >= 2), Evicted при нехватке ресурсов ноды.

Наблюдаемость: что должно быть до инцидента

Всё перечисленное разбирается за минуты, когда у сервиса есть базовая гигиена: структурированные логи в stdout (их собирает платформа; файлы внутри пода умирают вместе с ним), метрики Micrometer + scrape, алерты на рестарты и недоступность endpoints, дашборд с памятью/CPU по подам против limits. Это территория Observability Style Guide — Kubernetes лишь делает его обязательным.

Сводная карта

СимптомГде смотретьТипичная причина
CrashLoopBackOfflogs -p, exit codeПадение на старте: конфиг, порт, миграция; агрессивный liveness
OOMKilled / 137describe pod Last StateHeap-доля не согласована с limit; limit мал
ImagePullBackOffdescribe pod eventsТег не существует; права к registry
Pendingdescribe pod eventsRequests не помещаются; selector/taints
Перемежающиеся 503get endpoints в динамикеМигающая readiness
502/504 снаружиЦепочка Ingress→Service→endpoints→podРвётся одно звено — см. порядок проверки
RESTARTS растутdescribe pod Last StateLiveness, OOM, eviction, drain ноды

Что почитать дальше

  • Spring Boot в Kubernetes — probes и ресурсы: профилактика половины этой статьи.
  • Сеть и трафик — endpoints, Ingress и NetworkPolicy.
  • Деплой и конфигурация — rollout status и откаты.
  • Observability Style Guide — логи, метрики и алерты, без которых отладка слепа.