Что такое CrashLoopBackOff и почему это важно
CrashLoopBackOff — состояние Pod’а, при котором kubelet пытается запустить контейнер, контейнер быстро завершается, затем kubelet запускает его снова, но с увеличивающейся задержкой между попытками. Эта задержка и называется backoff.
Ключевая мысль: CrashLoopBackOff почти никогда не «лечится» ожиданием. Пока причина завершения процесса внутри контейнера не устранена, Pod будет крутиться в цикле «старт → падение → пауза → старт».
Чаще всего причина в одном из сценариев:
- приложение падает из‑за ошибки конфигурации/переменных окружения/секретов;
- процесс убивается по памяти:
OOMKilledи частоexit code 137; - контейнер «живой», но его убивает
livenessProbe; - долгий старт без корректной
startupProbe(миграции, прогрев, загрузка зависимостей); - ошибки в entrypoint/команде запуска, отсутствие бинарника, права, файловые пути.
С чего начать: быстрый чек-лист команд
Ниже набор команд, который в большинстве случаев быстро выводит на первопричину. Порядок важен: он соответствует тому, как kubelet «видит» проблему.
1) Посмотреть статус Pod и контейнеров
kubectl get pod -n <namespace> <pod> -o wide
Смотрите колонку STATUS (обычно будет CrashLoopBackOff) и RESTARTS (растёт). Если Pod управляется Deployment/StatefulSet, полезно сразу понять, какой ReplicaSet/Revision активен.
2) Посмотреть причины рестартов через kubectl describe
kubectl describe pod -n <namespace> <pod>
kubectl describe почти всегда показывает «скелет» проблемы: почему контейнер в последний раз завершился, кто его убил, и какие события происходили вокруг (pull image, probes, OOM, ошибки монтирования томов).
Что искать в выводе:
- в секции Containers:
Last State,State,Reason,Exit Code,Started/Finished; - внизу в Events:
Back-off restarting failed container,OOMKilled,Liveness probe failed,Readiness probe failed, ошибки volume mount.
3) Забрать логи «предыдущего» падения: kubectl logs --previous
kubectl logs -n <namespace> <pod> --previous
Команда kubectl logs --previous критична при CrashLoopBackOff: текущий контейнер может ещё не успеть вывести лог, а предыдущий — уже упал и оставил след. Если контейнеров несколько, добавьте -c <container>.
kubectl logs -n <namespace> <pod> -c <container> --previous
Если логов нет вообще, это тоже сигнал: процесс падает до инициализации логгера, entrypoint не запускается, либо приложение пишет в файл, а не в stdout/stderr.
4) События в namespace (иногда полезнее, чем describe одного Pod)
kubectl get events -n <namespace> --sort-by=.lastTimestamp
Иногда проблема «рядом»: не создался Secret, не смонтировался PVC, не вытянулся образ, нет прав ServiceAccount — а Pod проявляет это как CrashLoopBackOff.

Как читать exit codes и понять, кто «убил» контейнер
Exit code — это «финальная точка», которая часто экономит часы. Kubernetes показывает его в kubectl describe в контейнерной секции.
Exit code 137: что означает и почему это часто OOM
exit code 137 обычно означает, что процесс завершён сигналом SIGKILL (9), то есть его «жёстко» убили. Типичный сценарий в Kubernetes — OOMKilled, когда контейнер превысил лимит памяти.
Как подтвердить OOM:
- в
kubectl describe podищитеLast State: TerminatedиReason: OOMKilled; - в Events встречаются сообщения про OOM или давление памяти;
- по метрикам видно, что потребление упиралось в
limits.memory.
Важно: exit code 137 бывает не только от OOM. Контейнер могут убить и по другим причинам (eviction при давлении ресурсов на ноде, остановка ноды, ручное удаление Pod’а). Всегда сверяйтесь с Reason и Events.
Частые exit codes при CrashLoopBackOff
1— приложение само завершилось с ошибкой (конфиг, миграции, недоступна БД и т. п.).2— часто ошибка аргументов/CLI: неверные флаги, неверная команда.126— команда найдена, но не исполняется (права, noexec, несовместимость).127— команда не найдена (неверный entrypoint/command, отсутствует бинарник).137— SIGKILL, частоOOMKilled.139— segfault (ошибка приложения, нативные зависимости, баги).
Backoff: почему рестарты замедляются и как это мешает диагностике
backoff — защитный механизм: чтобы не «забить» ноду постоянными рестартами, kubelet увеличивает задержку между попытками запуска контейнера. В Events это выглядит как Back-off restarting failed container.
Backoff не исправляет проблему — он делает её менее разрушительной для кластера. Ваша задача — понять, почему контейнер завершился, а не «разогнать» рестарты.
Практические приёмы, чтобы не потерять «улики»:
- используйте
kubectl logs --previousсразу после рестарта; - если падение мгновенное, временно увеличьте уровень логирования приложения через env, чтобы успеть увидеть причину;
- держите централизованный сбор логов: при частых рестартах это сильно экономит время (подходы к пайплайнам и меткам можно сверить в материале про лейблы и пайплайны Loki).
Probes и CrashLoopBackOff: когда контейнер убивает не приложение, а проверка
Частый сценарий: приложение в целом работает, но kubelet регулярно его убивает из‑за неверно настроенной livenessProbe. Или Pod никогда не становится Ready из‑за readinessProbe, а дальше начинается «пила» рестартов из‑за деплоя/автоскейлинга, которую легко принять за падение приложения.
Liveness vs Readiness vs Startup: что и когда использовать
livenessProbe— «жив ли процесс». Если проваливается, kubelet перезапускает контейнер.readinessProbe— «готов ли принимать трафик». Если проваливается, контейнер не убивают, но Pod исключают из endpoints.startupProbe— защита медленного старта: пока она не успешна,livenessProbeне должна «рубить» контейнер за то, что он ещё запускается.
Как понять, что виновата probe
В kubectl describe в секции Events вы увидите строки вида:
Liveness probe failed: ...
Readiness probe failed: ...
Если есть Liveness probe failed, а затем рестарт — это почти прямое попадание. Дальше важно понять, что именно не так: порт, path, таймауты или логика эндпоинта.
Типовые ошибки конфигурации probes:
- слишком маленькие
timeoutSecondsиfailureThresholdдля реального времени ответа; - health endpoint зависит от внешних ресурсов (БД/очереди), и кратковременная деградация приводит к рестарту вместо ожидания;
- нет
startupProbeпри долгой инициализации:livenessProbeначинает проверять слишком рано; - проверка идёт на неправильный порт/путь или не учитывает префикс/host header.
Практический паттерн: добавить startupProbe и ослабить liveness
Если приложение стартует долго (миграции, прогрев, загрузка моделей), добавление startupProbe обычно резко снижает ложные рестарты. livenessProbe при этом имеет смысл сделать более «терпимой», чтобы рестарт происходил только при реальном зависании, а не при кратком всплеске задержек.
OOMKilled и память: почему контейнер падает «без логов»
OOMKilled — классика для CrashLoopBackOff. Контейнер может отработать несколько секунд, съесть память на старте и быть убитым раньше, чем успеет записать полезный лог. Вы увидите exit code 137, рост рестартов и backoff.
Что делать по шагам:
- Подтвердить
Reason: OOMKilledвkubectl describe. - Проверить
resources.limits.memoryиresources.requests.memoryв манифесте или Helm values. - Сверить лимиты приложения с лимитами контейнера (например, Java heap, число воркеров, размеры буферов).
- Если память «ползёт» со временем — подозревать утечку и включать профилирование/метрики, а не просто поднимать лимит бесконечно.
Отдельный нюанс QoS: если у контейнера нет requests/limits, он может быть менее предсказуемо выселен при давлении памяти на ноде. А строгий limits.memory, наоборот, даёт воспроизводимый OOM внутри cgroup — и это часто проще диагностировать.

Ошибки конфигурации и окружения: когда приложение честно выходит с code 1
Второй по частоте сценарий: приложение валится с Exit Code: 1 из‑за отсутствующих переменных окружения, неправильных строк подключения, неверных путей к файлам, отсутствующих сертификатов, миграций, которые не проходят.
Практика диагностики:
- сначала
kubectl logs --previous— обычно там стек/сообщение об ошибке; - потом внимательно проверить env/volumeMounts в
kubectl describe: смонтировались ли Secret/ConfigMap, те ли ключи и имена; - если используются initContainers — проверить их логи отдельно: основная app может не стартовать, потому что init не завершился.
Как не «стрелять себе в ногу»: приёмы при расследовании
Уточняйте, какой контейнер падает
В Pod’е может быть несколько контейнеров (sidecar, прокси, агент логов). CrashLoopBackOff относится к конкретному контейнеру. Всегда указывайте -c в логах и смотрите секцию каждого контейнера в kubectl describe.
Не путайте рестарты контейнера и пересоздание Pod
RESTARTS — это рестарты контейнеров внутри одного Pod. Если контроллер пересоздаёт Pod заново (rollout, переселение, выселение), вы увидите новые Pod’ы с нулевыми рестартами, но с той же ошибкой. Поэтому полезно смотреть events по namespace.
Временная пауза для анализа (когда нужно «остановить мясорубку»)
Если рестарты слишком частые и мешают анализу, иногда проще временно масштабировать Deployment в 0, поправить конфиг/секрет, затем вернуть реплики. Это особенно уместно, если падение вызвано явной ошибкой конфигурации и вы хотите избежать нагрузки на внешние сервисы (БД, брокер).
Мини-алгоритм: от CrashLoopBackOff к первопричине
- Соберите контекст:
kubectl get pod, имя контейнера, число рестартов, время последнего рестарта. - Прочитайте факты kubelet:
kubectl describe pod, секцииLast Stateи Events. - Достаньте логи падения:
kubectl logs --previous(при необходимости с-c). - Классифицируйте:
OOMKilled/exit code 137→ память/лимиты/утечки.Liveness probe failed→ настройка probes/таймаутов, часто нужнаstartupProbe.Exit Code: 127/126→ entrypoint/command/права/образ.Exit Code: 1→ конфиг/секреты/доступы/миграции/зависимости.
Проверьте внешние зависимости: DNS, сервисы, БД/очереди, PVC, права ServiceAccount — через events и логи. Если нужна системная «страховка» для воркеров и очередей (чтобы перезапуски были предсказуемыми), может пригодиться разбор про Supervisor и systemd для воркеров.
Типовой пример: «падает на старте, потом backoff, логов мало»
Если вы видите CrashLoopBackOff, в events — backoff, а в логах почти пусто, чаще всего это один из трёх случаев:
- OOMKilled на старте: подтверждается
Reason: OOMKilledи нередкоexit code 137. - Неверная команда запуска:
exit code 127или126. - Слишком агрессивная
livenessProbeбезstartupProbe: в events будетLiveness probe failed.
Это хорошая «развилка» для быстрой диагностики: сначала describe, потом logs --previous, и только потом — глубокие гипотезы.
Что настроить заранее, чтобы CrashLoopBackOff расследовался быстрее
- Пишите важные ошибки старта в stdout/stderr (не только в файл).
- Делайте health endpoints простыми и быстрыми; не завязывайте
livenessProbeна внешние зависимости. - Используйте
startupProbeдля долгого старта, аreadinessProbe— для готовности принимать трафик. - Задавайте requests/limits осмысленно и согласуйте лимиты приложения (heap/worker’ы) с лимитами контейнера.
- Собирайте события и логи централизованно — тогда рестарты и backoff не «съедят» улики.
Итог
CrashLoopBackOff — это сигнал от Kubernetes: «контейнер не удерживается в рабочем состоянии, и я уже начал увеличивать backoff». Самые полезные инструменты — kubectl describe (факты и events) и kubectl logs --previous (логи последнего падения). Дальше всё упирается в классификацию причины: OOMKilled/exit code 137, probes (livenessProbe/readinessProbe/startupProbe) либо ошибки запуска/конфига.
Если вы разворачиваете приложения на своих серверах, удобнее всего держать рабочие окружения на VDS: можно быстро подключить сбор метрик/логов, подобрать ресурсы под реальные пики и без боли пересобрать образ/рантайм при расследованиях.


