Устранение неполадок с Pod'ами Kubernetes: Полное руководство

Разберитесь в сложностях сбоев Pod'ов Kubernetes с помощью этого полного руководства. Изучите структурированный процесс диагностики распространенных проблем, таких как CrashLoopBackOff, ImagePullBackOff и истощение ресурсов. Мы подробно рассказываем, как использовать такие важные инструменты, как `kubectl describe` и `kubectl logs --previous`, чтобы определить первопричину, интерпретировать состояния выхода контейнера и реализовать практические исправления для поддержания надежного времени безотказной работы и стабильности приложения.

Устранение неполадок с Pod'ами Kubernetes: Полное руководство

Устранение неполадок с Pod'ами Kubernetes — это не столько запоминание каждого статуса, сколько умение находить подсказки, которые оставляет Kubernetes. Pod почти никогда не выходит из строя "молча". Планировщик, kubelet, среда выполнения контейнера, реестр образов, плагин томов и ваше приложение — все они оставляют следы в разных местах. Хитрость заключается в том, чтобы проверять их в правильном порядке, чтобы не тратить двадцать минут на чтение журналов приложения для Pod'а, который так и не загрузил свой образ.

Обычно я начинаю с одного вопроса: произошел ли сбой Pod'а до запуска контейнера, во время запуска контейнера или после того, как приложение начало работать? Это единственное разделение позволяет сохранять фокус расследования. Статус Pending обычно указывает на проблемы с планированием, хранилищем или настройкой образа. ImagePullBackOff указывает на путь к реестру, тег, учетные данные или исходящий трафик узла. CrashLoopBackOff обычно означает, что процесс запускается и завершается, хотя причина может быть в конфигурации, отсутствующем файле, неверной команде, невыполненной зависимости или нехватке памяти.


Три столпа диагностики Pod'ов

Устранение неполадок начинается с использования трех основных команд kubectl для сбора всей доступной информации о неисправном Pod'е.

1. Первоначальная проверка статуса (kubectl get pods)

Первым шагом всегда является определение текущего состояния Pod'а и его контейнеров. Обратите пристальное внимание на столбцы STATUS и READY.

kubectl get pods -n my-namespace

Интерпретация статуса Pod'а

Статус Значение Первоначальное действие
Running По крайней мере один контейнер работает; это не всегда означает, что приложение обслуживает трафик. Проверьте READY, количество перезапусков и события готовности.
Pending Pod принят Kubernetes, но контейнеры еще не созданы. Проверьте события планирования или статус загрузки образа.
CrashLoopBackOff Контейнер запускается, выходит из строя, и Kubelet неоднократно пытается перезапустить его. Проверьте журналы приложения (kubectl logs --previous).
ImagePullBackOff Kubelet не может загрузить требуемый образ контейнера. Проверьте имя образа, тег и учетные данные реестра.
Error Pod завершился из-за ошибки времени выполнения или неудачной команды запуска. Проверьте журналы и события describe.
Terminating/Unknown Pod завершает работу или хост Kubelet не отвечает. Проверьте работоспособность узла.

2. Глубокий анализ (kubectl describe pod)

Если статус отличается от Running, команда describe предоставляет важный контекст, детализируя решения планировщика, распределение ресурсов и состояния контейнеров.

kubectl describe pod [POD_NAME] -n my-namespace

Сосредоточьтесь на этих разделах в выводе:

  • Containers/Init Containers: Проверьте State (особенно Waiting или Terminated) и Last State (где часто записывается причина сбоя, например, Reason: OOMKilled).
  • Resource Limits: Убедитесь, что Limits и Requests установлены правильно.
  • Events: Это самый важный раздел. События показывают историю жизненного цикла, включая сбои планирования, проблемы с монтированием томов и попытки загрузки образа.

Совет: Если в разделе Events отображается сообщение типа "0/N nodes available", Pod, скорее всего, не может быть запланирован из-за нехватки ресурсов (CPU, память) или невыполнения правил сходства.

Читайте события снизу вверх, когда вам нужна самая свежая подсказка, но не игнорируйте более старые события. У Pod'а может быть более одной проблемы. Например, развертывание может начаться с FailedScheduling, потому что запрошенный объем памяти слишком велик, а затем перейти к ImagePullBackOff после добавления узла. Если вы смотрите только на конечный статус, вы можете пропустить изменение, которое продвинуло проблему вперед.

3. Просмотр журналов (kubectl logs)

Для проблем с приложением во время выполнения журналы предоставляют трассировку стека или сообщения об ошибках, объясняющие, почему процесс завершился.

# Проверка текущих журналов контейнера
kubectl logs [POD_NAME] -n my-namespace

# Проверка журналов для конкретного контейнера внутри Pod'а
kubectl logs [POD_NAME] -c [CONTAINER_NAME] -n my-namespace

# Критически важно для CrashLoopBackOff: Проверка журналов *предыдущего* неудачного запуска
kubectl logs [POD_NAME] --previous -n my-namespace

Если у Pod'а есть сайдкары, всегда указывайте -c. Многие разочаровывающие расследования возникают из-за чтения журналов работоспособного сайдкара вместо неисправного контейнера приложения. Для сбоев init-контейнеров также используйте имя init-контейнера с -c:

kubectl logs [POD_NAME] -c [INIT_CONTAINER_NAME] -n my-namespace

Распространенные сценарии сбоев Pod'ов и их решения

Большинство сбоев Pod'ов соответствуют нескольким узнаваемым шаблонам, каждый из которых требует целенаправленного диагностического подхода.

Сценарий 1: CrashLoopBackOff

Это самый частый сбой Pod'а. Он означает, что контейнер успешно запускается, но приложение внутри контейнера немедленно завершается (с ненулевым кодом выхода).

Диагностика:

  1. Используйте kubectl logs --previous для просмотра трассировки или причины выхода.
  2. Используйте kubectl describe для проверки Exit Code в разделе Last State.

Распространенные причины и исправления:

  • Код выхода 1/2: Общая ошибка приложения, отсутствующий файл конфигурации, сбой подключения к базе данных или сбой приложения из-за неверных входных данных.
    • Исправление: Отладка кода приложения или проверка монтируемых ConfigMap/Secret.
  • Отсутствующие зависимости: Сценарий точки входа требует файлы или окружения, которые отсутствуют.
    • Исправление: Проверьте Dockerfile и процесс сборки образа.
  • Неверная команда или аргументы: Образ контейнера действителен, но команда в спецификации Pod'а неправильно переопределяет точку входа образа.
    • Исправление: Сравните command и args в Deployment с ожидаемой командой запуска образа. По возможности протестируйте тот же образ локально.
  • Перезапуски, вызванные пробами: Проба жизнеспособности может завершить медленно запускающееся приложение до того, как оно закончит прогрев.
    • Исправление: Увеличьте initialDelaySeconds, используйте startupProbe или направьте пробу на более легкую конечную точку работоспособности.

Практичный шаблон — развернуть одну временную копию с тем же образом, но безвредной командой, затем проверить файловую систему и окружение:

kubectl run debug-image \
  --image=registry.example.com/app:tag \
  --restart=Never \
  --command -- sleep 3600

kubectl exec -it debug-image -- /bin/sh

Это не заменяет исправление Deployment, но помогает быстро ответить на простые вопросы: действительно ли файл конфигурации находится в образе, существует ли бинарный файл, есть ли в контейнере ожидаемая оболочка и присутствуют ли переменные окружения?

Сценарий 2: ImagePullBackOff / ErrImagePull

Это происходит, когда Kubelet не может загрузить образ контейнера, указанный в определении Pod'а.

Диагностика:

  1. Проверьте раздел Events в выводе kubectl describe на наличие конкретного сообщения об ошибке (например, 404 Not Found или authentication required).

Распространенные причины и исправления:

  • Опечатка или неверный тег: Имя образа или тег неверны.
    • Исправление: Исправьте имя образа в спецификации Deployment или Pod'а.
  • Доступ к частному реестру: У кластера нет учетных данных для загрузки из частного реестра (например, Docker Hub, GCR, ECR).
    • Исправление: Убедитесь, что в спецификации Pod'а указан соответствующий imagePullSecret и что Secret существует в пространстве имен.
# Пример фрагмента спецификации Pod'а для использования pull secret
spec:
  containers:
  ...
  imagePullSecrets:
  - name: my-registry-secret

Также проверьте, где находится pull secret. Секреты Kubernetes имеют пространства имен. Секрет с именем regcred в default не поможет Pod'у в payments. Если один и тот же образ работает в одном пространстве имен, но не работает в другом, сравните сервисные аккаунты и imagePullSecrets, прежде чем предполагать, что реестр неисправен:

kubectl get serviceaccount default -n payments -o yaml
kubectl get secret regcred -n payments

Сценарий 3: Статус Pending (Зависание)

Pod остается в статусе Pending, что обычно указывает на то, что он не может быть запланирован на узел или ожидает ресурсы (например, PersistentVolume).

Диагностика:

  1. Выполните kubectl describe и посмотрите раздел Events.

Распространенные причины и исправления:

  • Нехватка ресурсов: В кластере нет узлов с достаточным количеством доступных CPU или памяти для удовлетворения requests Pod'а.
    • Исправление: Увеличьте размер кластера или уменьшите запросы ресурсов Pod'а (если это возможно).
    • Пример сообщения о событии: 0/4 nodes are available: 4 Insufficient cpu.
  • Проблемы с привязкой томов: Pod'у требуется PersistentVolumeClaim (PVC), который не может быть привязан к базовому PersistentVolume (PV).
    • Исправление: Проверьте статус PVC (kubectl get pvc) и убедитесь, что StorageClass работает.
  • Несоответствие селектора или правила сходства: Pod запрашивает метку узла, которая не существует, или обязательное правило сходства исключает все узлы.
    • Исправление: Сравните nodeSelector, nodeAffinity и метки узлов с помощью kubectl get nodes --show-labels.
  • Отсутствие допусков для пятен: Узлы доступны, но они отталкивают этот Pod, потому что у него нет соответствующего допуска.
    • Исправление: Добавьте предполагаемый допуск к Pod'у или удалите пятно, если оно больше не представляет реального правила размещения.

Сценарий 4: OOMKilled (Завершен из-за нехватки памяти)

Хотя это обычно приводит к статусу CrashLoopBackOff, основная причина специфична: контейнер использовал больше памяти, чем установленный лимит в его спецификации, что привело к принудительному завершению его работы операционной системой хоста (через Kubelet).

Диагностика:

  1. Проверьте kubectl describe -> Last State -> Reason: OOMKilled.

Исправления:

  1. Увеличьте лимиты: Увеличьте limit памяти в спецификации Pod'а, предоставив больше пространства.
  2. Оптимизируйте приложение: Профилируйте приложение, чтобы уменьшить его потребление памяти.
  3. Установите requests: Убедитесь, что requests установлены близко к фактическому стабильному использованию, чтобы повысить надежность планирования.
resources:
  limits:
    memory: "512Mi" # Увеличьте это значение
  requests:
    memory: "256Mi"

Будьте осторожны с "исправлениями" памяти, которые только повышают лимит. Если в приложении есть утечка, более высокий лимит может только отсрочить следующий сбой и увеличить риск для узла. Смотрите на использование памяти с течением времени в вашей системе метрик. Пилообразный паттерн, возвращающийся к базовому уровню после сборки мусора, отличается от постоянного роста до OOM.

Сценарий 5: CreateContainerConfigError и CreateContainerError

Эти статусы легко пропустить, потому что они не звучат как сбои приложения. Обычно они означают, что kubelet не смог собрать конфигурацию контейнера.

Распространенные причины включают:

  • На ConfigMap или Secret, на которые есть ссылки, не существует в пространстве имен.
  • Ключ внутри ConfigMap или Secret написан с ошибкой.
  • Путь монтирования тома конфликтует с другим монтированием.
  • Контейнер ссылается на недопустимый контекст безопасности.

Самая быстрая проверка — все еще describe:

kubectl describe pod [POD_NAME] -n my-namespace

Ищите сообщения о событиях, такие как secret "app-config" not found или configmap "settings" not found. Затем проверьте объект:

kubectl get secret app-config -n my-namespace
kubectl get configmap settings -n my-namespace -o yaml

Это распространенная ошибка в конвейере развертывания. Манифест приложения применяется, но этап создания секрета не удался или был выполнен в неправильном пространстве имен.

Сценарий 6: Running, но не Ready

Pod может отображать статус Running, оставаясь при этом непригодным для использования. Столбец READY сообщает вам, сколько контейнеров готово в соответствии с их пробами готовности. Pod с 1/2 или 0/1 может быть жив, но удален из конечных точек Service.

Проверяйте конечные точки, когда трафик не проходит, но Pod'ы выглядят живыми:

kubectl get endpoints [SERVICE_NAME] -n my-namespace
kubectl describe pod [POD_NAME] -n my-namespace

Если список конечных точек пуст, проблема может быть в пробе готовности, несоответствии селектора Service или в том, что приложение прослушивает другой порт, чем ожидает Service. В реальных инцидентах именно здесь люди теряют время: они продолжают перезапускать Pod'ы, даже если Pod'ы не являются причиной отсутствия трафика.


Предотвращение будущих сбоев: Лучшие практики

Надежные приложения требуют тщательной настройки для предотвращения распространенных ошибок развертывания.

Используйте пробы жизнеспособности и готовности

Правильная реализация проб позволяет Kubernetes интеллектуально управлять работоспособностью контейнеров:

  • Пробы жизнеспособности (Liveness Probes): Определяют, достаточно ли контейнер работоспособен, чтобы продолжать работу. Если проба жизнеспособности не удалась, Kubelet перезапустит контейнер (решая проблемы мягких блокировок).
  • Пробы готовности (Readiness Probes): Определяют, готов ли контейнер обслуживать трафик. Если проба готовности не удалась, Pod удаляется из конечных точек Service, предотвращая неудачные запросы, пока контейнер восстанавливается.

Не направляйте пробы жизнеспособности на глубокие проверки зависимостей, если вы действительно не хотите, чтобы Kubernetes перезапускал контейнер всякий раз, когда у этой зависимости происходит кратковременный сбой. Недоступность базы данных в течение тридцати секунд обычно не является доказательством того, что процесс мертв. Готовность — лучшее место, чтобы сказать: "не отправляйте этому Pod'у трафик прямо сейчас".

Устанавливайте лимиты и запросы ресурсов

Всегда определяйте требования к ресурсам для контейнеров. Это предотвращает потребление Pod'ами чрезмерных ресурсов (что приводит к нестабильности узла) и гарантирует, что планировщик сможет разместить Pod на узле с достаточной емкостью.

Используйте Init-контейнеры для настройки

Если Pod'у требуется проверка зависимостей или настройка данных перед запуском основного приложения (например, ожидание завершения миграции базы данных), используйте Init-контейнер. Если Init-контейнер выйдет из строя, Pod будет перезапускать его повторно, чисто изолируя ошибки настройки от ошибок времени выполнения приложения.

Практический поток триажа

Когда вы на дежурстве, используйте повторяемый путь:

  1. Проверьте kubectl get pods -n <namespace> -o wide, чтобы увидеть статус, количество перезапусков, возраст и размещение на узле.
  2. Выполните kubectl describe pod и прочитайте Events, State, Last State, точки монтирования и настройки ресурсов.
  3. Получите журналы с помощью kubectl logs, добавив --previous для перезапущенных контейнеров и -c для многоконтейнерных Pod'ов.
  4. Если Pod находится в статусе Pending, проверьте планирование, пятна, метки узлов и PVC, прежде чем читать журналы приложения.
  5. Если Pod находится в статусе Running, но не получает трафик, проверьте пробы готовности, селекторы Service и конечные точки.
  6. Если Pod был завершен из-за OOM, сравните лимиты с реальными графиками памяти, прежде чем просто увеличивать число.

Этот порядок не позволяет вам сразу переходить к приложению, когда Kubernetes еще даже не запустил его.

Финальная проверка

Самая полезная привычка — отделять симптомы от причин. CrashLoopBackOff — это симптом. Причиной может быть отсутствующий секрет, неудачная миграция, проба жизнеспособности или лимит памяти. Pending — это симптом. Причиной могут быть запросы CPU, PVC, пятно или селектор узла. Позвольте статусу Pod'а указать вам, где искать, а затем позвольте событиям и журналам рассказать вам, что изменилось.