Ошибки 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 и так далее.

BackOff-цикл пода
BackOff-цикл пода

Политика перезапуска в 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.

Читайте также в нашем блоге:

Комментарии (2)


  1. Chupaka
    11.07.2024 23:47

    Контейнер, развёртывание — почему ж "под" не перевели? :)


    1. zradeg
      11.07.2024 23:47
      +3

      Хмм... Стручок? Отделяемый грузовой отсек? Небольшое стадо?