Разбор аварий: CrashLoopBackOff и Pending
Запустил под, который падает сразу после старта, и следил за ним восемь раз по десять секунд. Перезапусков стало 1, потом 2, потом 3, потом 4 - но интервалы между ними росли: 10 секунд, 20, 40, 80. Между попытками статус меняется с Error на CrashLoopBackOff. Рядом запустил под с несуществующим тегом образа: ImagePullBackOff и прямым текстом «Failed to pull image: код NotFound».
Стержень: статус пода и его события уже содержат ответ; гадать не надо, надо прочитать.
// Формулировки: «под в CrashLoopBackOff, что делать?», «как читать статусы?», «под Pending, куда смотреть?»
Статусы читаются однозначно
Pending - под создан, но не запущен. Почти всегда планировщик не нашёл узла: не хватает ресурсов, не подходят правила размещения, нет нужного тома. Ответ лежит в событиях пода прямым текстом - я получал там «0 из 1 узлов доступны: недостаточно процессора и памяти».
ImagePullBackOff - узел не смог скачать образ. Причины: опечатка в имени или теге, нет доступа к реестру, не заведён ключ для приватного реестра. У меня в событиях было буквально «Failed to pull image, код NotFound». CrashLoopBackOff - контейнер стартует и падает, кластер перезапускает его с растущей паузой.
// Ещё три частых. OOMKilled - превышен лимит памяти, код завершения 137. Error - контейнер завершился с ненулевым кодом. Completed у пода развёртывания - приложение завершилось само, хотя должно было работать; обычно это уход процесса в фон или неверная команда запуска.
- Pending
- узел не найден; смотреть события и запросы ресурсов
- CrashLoopBackOff
- контейнер падает при старте; пауза между попытками растёт
- ImagePullBackOff
- образ не скачался: имя, тег, доступ к реестру
Растущая пауза и почему это важно
Кластер не долбит перезапуском без остановки: после каждой неудачи он ждёт вдвое дольше. Мой замер это показывает: первый перезапуск через десять секунд, потом двадцать, сорок, восемьдесят - и так до предела примерно в пять минут.
Практическое следствие: не спешите делать выводы по первому взгляду. Под, который «висит в CrashLoopBackOff и ничего не делает», на самом деле ждёт следующей попытки. И второе: если вы исправили причину, но под всё ещё в цикле, он поднимется сам - просто не мгновенно, а на следующей попытке через несколько минут. Ускорить можно удалением пода: новый начнёт отсчёт заново.
// Логи упавшего контейнера смотрят с ключом «предыдущий»: обычная команда показывает логи ТЕКУЩЕГО контейнера, который только что запустился и ещё ничего не написал. Логи того, который упал, доступны отдельно - и именно там лежит причина.
- растущая пауза
- каждая следующая попытка перезапуска ждёт вдвое дольше, до пяти минут
- логи предыдущего контейнера
- то, что написал упавший экземпляр; отдельный ключ команды
Порядок разбора
Порядок один и тот же, и он экономит часы. Первое - статус и число перезапусков: они уже сужают круг. Второе - подробное описание пода: там события, причина последнего завершения и код выхода. Третье - логи, причём и текущего контейнера, и предыдущего, если были перезапуски. Четвёртое - события всего отсека, отсортированные по времени: там видно, что происходило вокруг.
Дальше по симптому. Если сервис не отвечает - смотрю список адресов за ним: пусто означает несовпадение меток или непройденную пробу готовности. Если под работает, но ведёт себя странно - захожу внутрь и проверяю оттуда: разрешается ли имя соседа, отвечает ли база, лежат ли на месте файлы конфигурации.
// И три вещи, которые стоит проверить до того, как искать сложное. Не изменилось ли описание (история версий это покажет). Хватает ли места на узлах. Не выселяет ли кластер поды по нехватке памяти - это видно в событиях узла и объясняет «поды почему-то перезапускаются» лучше любых логов приложения.
- подробное описание пода
- события, причина завершения и код выхода в одном месте
- события отсека
- хронология происходившего вокруг; сортируются по времени
Как отвечать: «Под в CrashLoopBackOff. Ваши действия?»
Это значит, что контейнер стартует и падает, а кластер перезапускает его с растущей паузой - я мерил, интервалы удваиваются: десять секунд, двадцать, сорок, восемьдесят, и так до пяти минут. Поэтому сначала перестаю ждать мгновенной реакции. Первым делом смотрю подробное описание пода: там причина последнего завершения и код выхода. Код 137 означает превышение лимита памяти, 1 - приложение упало само, 127 - команда не найдена в образе. Потом читаю логи ПРЕДЫДУЩЕГО контейнера, а не текущего: обычная команда покажет только что запущенный экземпляр, который ещё ничего не написал, а причина лежит в логах упавшего. Дальше проверяю очевидное: доступна ли конфигурация и секреты, которые под подключает, отвечают ли зависимости, не убивает ли контейнер собственная проба живости из-за медленного старта. И если причину уже исправил, под поднимется сам, просто не сразу - или удаляю его, чтобы отсчёт паузы начался заново.
Ответ объясняет механику паузы, даёт правильный источник логов и разбирает коды выхода. Логи предыдущего контейнера - деталь, на которой видно, кто это реально делал.
На чём валятся
- −Читают логи текущего контейнера вместо предыдущего и не видят причины падения.
- −Не смотрят события пода, где причина написана прямым текстом.
- −Ждут мгновенного подъёма после починки: пауза между попытками уже выросла до минут.
- −Ищут проблему в приложении, когда поды выселяются по нехватке памяти на узле.
- −Не проверяют список адресов за сервисом и ищут сетевые проблемы там, где не сошлись метки.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.
- Под в CrashLoopBackOff. С чего начать разбор?A)Логи прошлого запуска, затем события describeB)Пересоздать под: состояние часто залипает после обновления нодыC)Зайти внутрь через exec и посмотреть процессыD)Проверить, что образ есть в реестре и тег не сдвинулся
показать ответ и разбор
+A)Логи прошлого запуска, затем события describe// разбор: Статус означает, что контейнер падает и kubelet перезапускает его с растущей паузой. Полезное лежит в логах предыдущего запуска (текущий контейнер может быть уже мёртв) и в событиях пода: код выхода, причина, OOMKilled, отказ проб. Дальше по коду: 1 — упало приложение, 137 — убит сигналом, 127 — не найдена команда. Зайти внутрь обычно нельзя: контейнер не живёт достаточно долго.
- Под встал в ImagePullBackOff, хотя образ точно опубликован. Что смотреть?A)Ресурсы ноды: скачивание не начинается без свободной памятиB)Доступ к реестру: imagePullSecrets, сеть до него, точность тегаC)Состояние пробы готовности: без неё образ не загружаетсяD)Класс хранилища: слои образа кладутся на том PVC
показать ответ и разбор
+B)Доступ к реестру: imagePullSecrets, сеть до него, точность тега// разбор: Статус означает, что kubelet не смог скачать образ. Частые причины: приватный реестр без imagePullSecrets в поде или сервисном аккаунте, опечатка в теге и репозитории, нет сетевого доступа с ноды до реестра (прокси, фаервол, зеркало), исчерпан лимит анонимных скачиваний. Точная причина — в событиях: unauthorized, manifest unknown, i/o timeout читаются однозначно.
- На ноде поды массово получили статус Evicted, в событиях — DiskPressure. Что произошло и что чинить?A)Планировщик переселил поды ради балансировки нагрузкиB)Автоскейлер снимал ноду и вытеснил нагрузкуC)Kubelet вытеснял поды из-за нехватки места на диске нодыD)PodDisruptionBudget разрешил выселение и его применили
показать ответ и разбор
+C)Kubelet вытеснял поды из-за нехватки места на диске ноды// разбор: Kubelet следит за ресурсами ноды и при нехватке места запускает вытеснение: первыми уходят BestEffort, затем те, кто превысил свои запросы. Диск на ноде обычно съедают логи контейнеров без ротации, эфемерное хранилище подов и слои старых образов. Лечится ротацией логов, лимитами ephemeral-storage у подов, уборкой образов и, отдельно, мониторингом свободного места с алертом до порога вытеснения.
- Нода в статусе NotReady, её поды начали переезжать. С чего начать разбор на самой ноде?A)Проверить kubelet и CNI на нодеB)Сразу пересоздать все поды ноды вручнуюC)Удалить и заново завести объект Node в APID)Перезапустить api-server управляющего слоя
показать ответ и разбор
+A)Проверить kubelet и CNI на ноде// разбор: NotReady почти всегда значит, что kubelet ноды перестал репортить готовность в api-server — из-за самого kubelet (упал/не стартует), CNI (сеть пода не поднимается), нехватки ресурсов или потери связи с control plane. Начинают на ноде: systemctl status kubelet и его логи (journalctl -u kubelet), состояние CNI, disk/mem. kubectl describe node покажет условия (MemoryPressure/DiskPressure/NetworkUnavailable).
- Под висит в статусе Init:0/1 и не переходит к основным контейнерам. Куда смотреть?A)В readinessProbe основного контейнераB)В нехватку ClusterIP для сервиса подаC)В initContainer: он не завершилсяD)В отсутствие resources requests у пода
показать ответ и разбор
+C)В initContainer: он не завершился// разбор: Init:0/1 значит: из одного init-контейнера завершился ноль — под застрял на init-фазе. Init-контейнеры выполняются по очереди до основных, и если один падает или висит (ждёт недоступную зависимость, миграция не проходит, нет доступа), основные не стартуют. Смотрят логи именно init-контейнера: kubectl logs pod -c <init-name> и события describe. Частая причина — ожидание внешнего сервиса, которого нет.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.