Як виправити помилки CrashLoopBackOff в Kubernetes
Чи гальмують ваші розгортання помилки CrashLoopBackOff? У цій статті ми поговоримо про те, чому поди застряють у стані CrashLoopBackOff, як дізнатися, що под потрапив у цей стан і з якої причини, — усе це з прикладами.
Фази життєвого циклу подів у 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 # політика перезапуску
Як дізнатися, що под потрапив у стан CrashLoopBackOff
Статус подів можна перевірити за допомогою простої команди 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
Спробуємо по кроках встановити причину:
- Перевіримо опис пода
Команда 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?)» випливає, що контейнер не запускається через нестачу пам’яті.
- Перевіримо логи пода
Логи містять докладну інформацію про ресурси Kubernetes, про запуск контейнера, про будь-які проблеми, що виникли на шляху, про припинення роботи ресурсу або навіть про її успішне завершення.
Перевірити логи подів можна за допомогою наступних команд:
$ kubectl logs pod-name # витягнути логи пода з єдиним контейнером
Подивитися логи пода з кількома контейнерами:
$ kubectl logs pod-name --all-containers=true
Подивитися логи за певний проміжок часу. Наприклад, щоб вивести логи за останню годину, виконайте:
$ kubectl logs pod-name --since=1h
- Подивимось події
Події містять найсвіжішу інформацію про ресурси 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
- Перевіримо логи розгортання:
$ 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. Важливо зазначити, що саме по собі це не помилка, а стан, у якого можуть бути різні причини. Встановити їх допоможе аналіз логів і подій для конкретного пода.