Что означает ошибка CrashLoopBackOff
CrashLoopBackOff — это состояние Kubernetes пода, когда контейнер внутри него постоянно завершается с ошибкой (exit code ≠ 0), а kubelet безуспешно пытается его перезапустить. После каждой неудачной попытки задержка перед следующим запуском экспоненциально увеличивается (10s, 20s, 40s и т.д.), что и отражено в названии состояния.
Ошибка появляется в выводе kubectl get pods:
NAME READY STATUS RESTARTS AGE
my-app-5d89d7c8b9-xyz 0/1 CrashLoopBackOff 5 2m
Это не ошибка самого Kubernetes, а индикатор того, что контейнер не может запуститься устойчиво. Проблема всегда в конфигурации пода или в самом приложении.
Причины возникновения
- Ошибка в приложении — приложение падает при старте (например, из-за неверных аргументов, отсутствия переменных окружения, ошибок в коде).
- Нехватка ресурсов — контейнеру не хватает памяти (OOMKilled) или CPU, что приводит к аварийному завершению.
- Неправильная команда запуска — в манифесте пода указаны некорректные
commandилиargs, или отсутствует точка входа (ENTRYPOINT) в образе. - Проблемы с образом — несуществующий тег образа, ошибка аутентификации в приватном реестре, повреждённый образ.
- Недоступные тома — том (PersistentVolumeClaim) не смounted, недоступен или имеет неправильные права.
- Недостаток прав — контейнеру не хватает прав (SecurityContext, ServiceAccount) для доступа к ресурсам (например, к порту <1024).
- Конфликт портов — приложение пытается слушать порт, который уже занят другим процессом в контейнере или хосте.
- Проблемы с инициализацией — init-контейнеры завершились с ошибкой, не предоставив необходимые ресурсы.
Способ 1: Диагностика через логи и описание пода
Первый и самый важный шаг — собрать информацию об ошибке.
- Узнайте имя пода:
kubectl get pods
Найдите под с статусомCrashLoopBackOff. Запишите его полное имя (например,my-app-5d89d7c8b9-xyz). - Просмотрите логи предыдущего экземпляра контейнера:
kubectl logs <pod-name> --previous
Ключ--previousпоказывает логи контейнера, который уже завершился. Если под многоконтейнерный, укажите имя контейнера:-c <container-name>. - Изучите события (Events) пода:
kubectl describe pod <pod-name>
В выводе найдите разделEvents. Частые ошибки:OOMKilled— нехватка памяти.FailedилиErrImagePull— проблемы с образом.FailedMount— ошибка монтирования тома.Exitedс кодом — ошибка приложения.
- Если приложение выводит логи в stdout/stderr, они появятся в
kubectl logs. Если логи пишутся в файл внутри контейнера, подключитесь к упавшему контейнеру (если возможно):kubectl exec -it <pod-name> -- /bin/sh
Но приCrashLoopBackOffконтейнер может быть недоступен для exec. В этом случае используйте--previousдля логов или временно измените манифест, чтобы контейнер не завершался (например, запуститеsleep infinity).
Способ 2: Проверка и увеличение ресурсов (память/CPU)
Частая причина — контейнеру не хватает памяти, и ядро завершает процесс (OOMKilled).
- В
kubectl describe pod <pod-name>найдите:Limits: memory: 256Mi Requests: memory: 128Mi
Еслиmemoryв Limits слишком мало (например, 128Mi) для Java-приложения или базы данных, увеличьте. - Измените манифест пода (deployment/statefulset):
spec: containers: - name: my-app resources: limits: memory: "512Mi" cpu: "500m" requests: memory: "256Mi" cpu: "250m"
Примените изменения:kubectl apply -f deployment.yaml - Если проблема в CPU, увеличьте
limits.cpuиrequests.cpu. Но учтите, что CPU-лимиты могут вызывать throttling, но не OOMKilled. - Для диагностики можно временно убрать лимиты (не для продакшена!), чтобы проверить, исчезнет ли ошибка:
resources: limits: {} requests: {}
Способ 3: Проверка команды запуска и аргументов
Если приложение требует конкретных аргументов или переменных окружения, а они не переданы, оно может упасть.
- Проверьте, какая команда выполняется в контейнере:
kubectl describe pod <pod-name> | grep -A5 "Command:" kubectl describe pod <pod-name> | grep -A5 "Args:"
Если команда или аргументы неверны, контейнер завершится сразу. - Пример проблемы:
В Dockerfile указаноENTRYPOINT ["java", "-jar", "app.jar"], а в манифесте пода переопределёнcommand: ["python"]— это вызовет ошибку. - Исправьте манифест:
spec: containers: - name: my-app image: my-image:latest command: ["java"] # опционально, если хотите переопределить ENTRYPOINT args: ["-jar", "app.jar"] # опционально, если хотите переопределить CMD env: - name: DB_HOST value: "postgres"
Убедитесь, что команда существует в образе (проверьте Dockerfile). - Если приложение зависит от переменных окружения, добавьте все необходимые в секции
env. Проверьте, что нет опечаток.
Способ 4: Проверка образа и его доступности
Проблемы с образом — одна из самых частых причин CrashLoopBackOff.
- Проверьте, что образ существует и доступен:
kubectl describe pod <pod-name> | grep -i "image"
Ищите событияErrImagePullилиImagePullBackOff. - Убедитесь, что тег образа корректен:
- Не используйте
latestв продакшене (он может меняться). - Проверьте, что образ есть в реестре:
docker pull <image:tag>(если у вас есть доступ).
- Не используйте
- Если образ в приватном реестре, убедитесь, что создан
imagePullSecret:kubectl get secret
Если секрета нет, создайте:kubectl create secret docker-registry regcred \ --docker-server=<registry-server> \ --docker-username=<username> \ --docker-password=<password>
И добавьте в под/деплоймент:spec: imagePullSecrets: - name: regcred - Проверьте, что образ совместим с архитектурой нод. Например, образ для
amd64не запустится на нодеarm64.
Способ 5: Проверка томов и SecurityContext
Если контейнеру не удаётся смонтировать том или получить доступ к ресурсу из-за прав, он упадёт.
- Проверьте события FailedMount:
kubectl describe pod <pod-name> | grep -A10 "Events"
Ошибки типаMountVolume.SetUpилиfailed to mount volumesуказывают на проблемы с PVC или конфигурацией тома. - Убедитесь, что PVC существует и в статусе
Bound:kubectl get pvc - Проверьте SecurityContext:
- Если контейнер запускается от
root(UID 0), но приложение требует не-привилегированного пользователя, настройтеrunAsUser. - Если нужно слушать порт <1024, требуется
CAP_NET_BIND_SERVICE:securityContext: capabilities: add: ["NET_BIND_SERVICE"] - Проверьте, что
fsGroupустановлен правильно для доступа к томам.
- Если контейнер запускается от
- Пример исправления для тома:
spec: containers: - name: my-app volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: my-pvc
Убедитесь, чтоmy-pvcсуществует и имеет достаточный размер.
Профилактика
- Тестируйте образ локально перед деплоем:
docker run --rm -it <image:tag> <command>
Убедитесь, что контейнер запускается и не падает. - Указывайте явные команды и аргументы в манифесте, даже если они совпадают с Dockerfile. Это исключит неявные зависимости.
- Настройвайте разумные лимиты ресурсов на основе мониторинга (например, через
kubectl top pod). Начинайте с запасом 20-30%. - Используйте readiness/liveness probes — они не предотвратят CrashLoopBackOff, но помогут отличить проблемы запуска от проблем работоспособности.
- Включайте логирование в stdout/stderr и собирайте их через DaemonSet (Fluentd, Filebeat) — это упростит диагностику.
- Проверяйте манифесты через
kubectl apply --dry-run=clientперед применением. - Для критичных приложений рассмотрите использование
restartPolicy: OnFailure(для Job) или настройкуbackoffLimitв Job, чтобы ограничить число перезапусков.
💡 Совет: Если приложение падает только в Kubernetes, но работает локально, сравнивайте окружение: переменные, тома, сеть, архитектуру ноды. Часто проблема в различиях между Docker run и Kubernetes pod.