Ошибки CrashLoopBackOff тормозят ваши развёртывания? В этой статье мы поговорим о том, почему поды зависают в состоянии CrashLoopBackOff и как узнать, что под попал в это состояние и по какой причине, — всё это с примерами. Статья является переводом материала QA-инженера Омкара Кулкарни из Encora Inc.
Фазы жизненного цикла подов в Kubernetes
Когда YAML-файл конфигурации пода (ресурса) поступает в Kubernetes, сервер Kube API проверяет конфигурацию и делает её доступной. Одновременно с этим планировщик следит за появлением новых подов и распределяет их на узлы в соответствии с их ресурсными предпочтениями. Всего у пода есть четыре фазы жизненного цикла:
Значение |
Описание |
Pending |
Первый шаг после создания пода. Планировщик Kubernetes размещает под на узел |
Running |
Под переходит в это состояние после успешного создания контейнера |
Succeeded |
Под переходит в это состояние после успешного завершения работы главного контейнера |
Failed |
Под переходит в это состояние, если какой-то из его контейнеров «упал» или его exit-код отличен от нуля |
Узнать, в какой фазе находится под, можно с помощью следующей команды: $ kubectl get pod
.
Состояния контейнеров в подах
Как уже говорилось выше, у подов есть различные фазы. Аналогичным образом Kubernetes отслеживает состояние контейнеров внутри подов. Всего таких статуса три: Waiting (ожидаю), Running (работаю) и Terminated (выключен). После того как под запланирован на узел, kubelet начинает запускать в соответствующем рантайме его контейнеры.
Проверить статус контейнера можно с помощью следующей команды: $ kubectl describe pod <имя пода>
.
Состояние |
Описание |
Waiting |
Состояние указывает, что контейнер по-прежнему находится в процессе создания и не пока не готов к работе |
Running |
Всё нормально: ресурсы (CPU, память) выделены успешно, процесс контейнера запущен |
Terminated |
Процесс контейнера прекратил свою работу |
Что такое CrashLoopBackOff
В Kubernetes состояние CrashLoopBackOff
указывает на то, что под завис в цикле перезапусков. Это означает, что один или несколько контейнеров в поде не смогли стартовать.
В общем случае говорят, что под застрял в состоянии CrashLoop, когда один или несколько его контейнеров постоянно запускаются, падают и снова запускаются.
Что такое BackOff-время и почему оно важно
Алгоритм BackOff, или алгоритм выдержки, — простая техника, которая используется в сетевых технологиях и информатике для повторного запуска задач в случае сбоев. Представим, что мы пытаемся отправить другу сообщение, но оно по какой-то причине не доходит. В этом случае вместо того, чтобы сразу пытаться отправить его снова, алгоритм предписывает немного подождать.
То есть после первой неудачной попытки мы некоторое время ждём, прежде чем совершить вторую. Если и она неудачна — мы ждём чуть дольше, а затем повторяем попытку. Термин BackOff означает, что с каждой попыткой период ожидания постепенно возрастает. Идея в том, чтобы у системы или сети было время восстановиться после сбоя. Кроме того, такой подход предотвращает эффект лавины.
То есть BackOff — это время, которое должно пройти после того, как под завершил работу. Только после того, как оно истечёт, он попытается перезапуститься. Задержка даёт системе время на восстановление и устранение ошибки. Набор таких интервалов «притормаживает» перезапуски.
Предположим, что под не запустился сразу. Тогда время ожидания до перезапуска по умолчанию (в конфигурации kubelet’а) составит 10 секунд. Обычно после каждой попытки оно увеличивается вдвое. То есть после первой попытки под будет ждать 10 секунд, после второй — 20 секунд, затем 40, 80 и так далее.
Политика перезапуска в Kubernetes
Как говорилось выше, Kubernetes пытается перезапустить под, когда тот даёт сбой. Поды в Kubernetes задуманы как сущности, которые восстанавливаются самостоятельно. Это означает, что контейнеры, в которых возникают ошибки или сбои, автоматически перезапускаются.
Конкретное поведение контролируется конфигурацией под названием restartPolicy
в спецификации пода. Задавая политику перезапуска, мы определяем, как Kubernetes будет обрабатывать сбои контейнеров. Возможные значения: Always, OnFailure и Never. Значение по умолчанию — Always.
Пример спецификации пода с заданной политикой перезапуска K8s:
apiVersion: v1
kind: Pod
metadata:
name: my-nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
restartPolicy: Always #политика перезапуска
Как узнать, что под попал в состояние CrashLoop
Статус подов можно проверить с помощью простой команды kubectl. Пример её вывода:
Видно, что под my-nginx не готов: его статус отличен от Ready и указан как CrashLoopBackOff. Также видно количество перезапусков.
Здесь происходит именно то, о чём говорилось выше: под падает и пытается перезапуститься. Пока под «отдыхает», можно попробовать установить причину его перезапусков.
Например, на платформе PerfectScale для этого надо перейти на вкладку Alerts — здесь будут отображены критические алерты о ресурсах Kubernetes и необычной активности в системе. Также можно посмотреть подробную сводку алертов для конкретного тенанта:
Распространённые причины CrashLoopBackOff
1. Нехватка ресурсов
Распределение памяти играет решающую роль в обеспечении бесперебойной работы развёртываний в Kubernetes. Если требования пода к памяти не учтены, может возникнуть состояние CrashLoopBackOff.
Например, если приложение «съедает» больше памяти, чем ему выделено, система убивает его по OOM (Out Of Memory). Результат — состояние CrashLoopBackOff.
2. Проблемы, связанные с образами
Нехватка разрешений — если у образа контейнера нет необходимых разрешений для доступа к ресурсам, тот может упасть.
Неправильный образ контейнера — если под использует неправильный образ для запуска контейнера, это может приводить к постоянным перезагрузкам и состоянию CrashLoopBackOff.
3. Ошибки конфигурации
Синтаксические ошибки или опечатки — при конфигурировании подов возможны ошибки, такие как опечатки в названиях контейнеров, именах образов и переменных окружения. Они могут помешать корректному запуску контейнеров.
Неверно заданные запросы и лимиты ресурсов — ошибки в настройке запрашиваемых ресурсов (минимально необходимого количества) и лимитов (максимально допустимого количества) могут приводить к сбоям в работе контейнера.
Отсутствующие зависимости — зависимости, которые пользователь забыл внести в spec пода.
4. Проблемы с внешними сервисами
Сетевые проблемы — если контейнер зависит от какого-либо внешнего сервиса, например базы данных, а этот внешний сервис в момент запуска недоступен или не работает, это может привести к CrashLoopBackOff.
Если один из внешних сервисов не работает, а контейнер в поде полагается на него, это может привести к отказу контейнера из-за невозможности подключения.
5. Исключения в приложениях
Ошибка или исключение в контейнеризованном приложении могут привести к аварийному завершению его работы. Причины могут быть разными: некорректный ввод, нехватка ресурсов, сетевые проблемы, проблемы с правами доступа к файлам, неправильная конфигурация секретов и переменных окружения или ошибки в коде. Отсутствие в коде приложения надлежащих механизмов обработки ошибок, позволяющих изящно перехватывать и обрабатывать исключения, может привести к возникновению состояния CrashLoopBackOff в Kubernetes.
6. Неправильно настроены liveness-пробы
Liveness-пробы помогают убедиться, что процесс в контейнере работает (не завис). Если всё же это произошло, контейнер будет убит и перезапущен (если это предусмотрено политикой перезапуска restartPolicy пода). Распространённой ошибкой является неправильная настройка liveness-пробы, в результате которой контейнер перезапускается при временных задержках с ответами, например из-за высокой нагрузки. Такая проба усугубляет проблему, а не решает её.
Выясняем причины CrashLoopBackOff
Предыдущий раздел показал, что под может оказаться в состоянии CrashLoopBackOff по множеству причин. Теперь поговорим о том, как установить конкретную причину. Устранение неисправностей обычно начинают с формулирования возможных сценариев, после чего выявляют первопричину, постепенно исключая все неактуальные сценарии.
Предположим, что команда kubectl get pods
показала статус CrashLoopBackOff у одного или нескольких подов:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
app 1/1 Running 1 (3d12h ago) 8d
busybox 0/1 CrashLoopBackOff 18 (2m12s ago) 70m
hello-8n746 0/1 Completed 0 8d
my-nginx-5c9649898b-ccknd 0/1 CrashLoopBackOff 17 (4m3s ago) 71m
my-nginx-7548fdb77b-v47wc 1/1 Running 0 71m
Попытаемся по шагам установить причину:
1. Проверим описание пода
Команда kubectl describe pod pod-name
выводит подробную информацию о конкретных подах и контейнерах:
$ kubectl describe pod pod-name
Name: pod-name
Namespace: default
Priority: 0
……………………
State: Waiting
Reason: CrashLoopBackOff
Last State: Terminated
Reason: StartError
……………………
Warning Failed 41m (x13 over 81m) kubelet Error: container init was OOM-killed (memory limit too low?): unknown
В её выводе содержится различная информация, например:
State (Состояние): Waiting (Ожидание)
Reason (Причина): CrashLoopbackOff
Last state (Предыдущее состояние): Terminated (Работа прервана)
Reason (Причина): StartError (Ошибка при старте)
Этого достаточно, чтобы сделать пару предположений о причинах. Из последних строк вывода «kubelet Error: container init was OOM-killed (memory limit too low?)» следует, что контейнер не запускается из-за недостатка памяти.
2. Проверим логи пода
Логи содержат подробную информацию о ресурсах Kubernetes, о запуске контейнера, о любых проблемах, возникших на пути, о прекращении работы ресурса или даже о её успешном завершении.
Проверить логи подов можно с помощью следующих команд:
$ kubectl logs pod-name # извлечь логи пода с единственным контейнером
Посмотреть логи пода с несколькими контейнерами:
$ kubectl logs pod-name --all-conainers=true
Посмотреть логи за определённый промежуток времени. Например, чтобы вывести логи за последний час, выполните:
$ kubectl logs pod-name --since=1h
3. Посмотрим события
События содержат самую свежую информацию о ресурсах Kubernetes. Можно запросить события для определённого пространства имён или отфильтровать их по конкретной рабочей нагрузке:
$ kubectl events
LAST SEEN TYPE REASON OBJECT MESSAGE
4h43m (x9 over 10h) Normal BackOff Pod/my-nginx-5c9649898b-ccknd Back-off pulling image "nginx:latest"
3h15m (x11 over 11h) Normal BackOff Pod/busybox Back-off pulling image "busybox"
40m (x26 over 13h) Warning Failed Pod/my-nginx-5c9649898b-ccknd Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: container init was OOM-killed (memory limit too low?): unknown
Пример работы команды показан выше.
Вывести список последних событий для всех пространств имён:
$ kubectl get events --all-namespaces
Вывести список событий для конкретного пода:
$ kubectl events --for pod/pod-name
4. Проверим логи развёртывания:
$ kubectl logs deployment deployment-name
Found 2 pods, using pod/my-nginx-7548fdb77b-v47wc
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Sourcing
Логи развёртывания помогают выяснить причину сбоя контейнеров и то, почему под оказывается в состоянии CrashLoopBackOff.
Заключение
В этой статье мы подробно поговорили о CrashLoopBackOff. Важно отметить, что само по себе это не ошибка, а состояние, у которого могут быть различные причины. Установить их поможет анализ логов и событий для конкретного пода.
P. S.
Читайте также в нашем блоге:
Chupaka
Контейнер, развёртывание — почему ж "под" не перевели? :)
zradeg
Хмм... Стручок? Отделяемый грузовой отсек? Небольшое стадо?