Инциденты в 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 pod — Readiness 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 лишь делает его обязательным.
Сводная карта
| Симптом | Где смотреть | Типичная причина |
|---|---|---|
| CrashLoopBackOff | logs -p, exit code | Падение на старте: конфиг, порт, миграция; агрессивный liveness |
| OOMKilled / 137 | describe pod Last State | Heap-доля не согласована с limit; limit мал |
| ImagePullBackOff | describe pod events | Тег не существует; права к registry |
| Pending | describe pod events | Requests не помещаются; selector/taints |
| Перемежающиеся 503 | get endpoints в динамике | Мигающая readiness |
| 502/504 снаружи | Цепочка Ingress→Service→endpoints→pod | Рвётся одно звено — см. порядок проверки |
| RESTARTS растут | describe pod Last State | Liveness, OOM, eviction, drain ноды |
Что почитать дальше
- Spring Boot в Kubernetes — probes и ресурсы: профилактика половины этой статьи.
- Сеть и трафик — endpoints, Ingress и NetworkPolicy.
- Деплой и конфигурация — rollout status и откаты.
- Observability Style Guide — логи, метрики и алерты, без которых отладка слепа.