Устранение неполадок с 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'а. Он означает, что контейнер успешно запускается, но приложение внутри контейнера немедленно завершается (с ненулевым кодом выхода).
Диагностика:
- Используйте
kubectl logs --previousдля просмотра трассировки или причины выхода. - Используйте
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'а.
Диагностика:
- Проверьте раздел
Eventsв выводеkubectl describeна наличие конкретного сообщения об ошибке (например,404 Not Foundилиauthentication required).
Распространенные причины и исправления:
- Опечатка или неверный тег: Имя образа или тег неверны.
- Исправление: Исправьте имя образа в спецификации Deployment или Pod'а.
- Доступ к частному реестру: У кластера нет учетных данных для загрузки из частного реестра (например, Docker Hub, GCR, ECR).
- Исправление: Убедитесь, что в спецификации Pod'а указан соответствующий
imagePullSecretи что Secret существует в пространстве имен.
- Исправление: Убедитесь, что в спецификации Pod'а указан соответствующий
# Пример фрагмента спецификации 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).
Диагностика:
- Выполните
kubectl describeи посмотрите разделEvents.
Распространенные причины и исправления:
- Нехватка ресурсов: В кластере нет узлов с достаточным количеством доступных CPU или памяти для удовлетворения
requestsPod'а.- Исправление: Увеличьте размер кластера или уменьшите запросы ресурсов Pod'а (если это возможно).
- Пример сообщения о событии:
0/4 nodes are available: 4 Insufficient cpu.
- Проблемы с привязкой томов: Pod'у требуется
PersistentVolumeClaim(PVC), который не может быть привязан к базовомуPersistentVolume(PV).- Исправление: Проверьте статус PVC (
kubectl get pvc) и убедитесь, что StorageClass работает.
- Исправление: Проверьте статус PVC (
- Несоответствие селектора или правила сходства: Pod запрашивает метку узла, которая не существует, или обязательное правило сходства исключает все узлы.
- Исправление: Сравните
nodeSelector,nodeAffinityи метки узлов с помощьюkubectl get nodes --show-labels.
- Исправление: Сравните
- Отсутствие допусков для пятен: Узлы доступны, но они отталкивают этот Pod, потому что у него нет соответствующего допуска.
- Исправление: Добавьте предполагаемый допуск к Pod'у или удалите пятно, если оно больше не представляет реального правила размещения.
Сценарий 4: OOMKilled (Завершен из-за нехватки памяти)
Хотя это обычно приводит к статусу CrashLoopBackOff, основная причина специфична: контейнер использовал больше памяти, чем установленный лимит в его спецификации, что привело к принудительному завершению его работы операционной системой хоста (через Kubelet).
Диагностика:
- Проверьте
kubectl describe->Last State->Reason: OOMKilled.
Исправления:
- Увеличьте лимиты: Увеличьте
limitпамяти в спецификации Pod'а, предоставив больше пространства. - Оптимизируйте приложение: Профилируйте приложение, чтобы уменьшить его потребление памяти.
- Установите 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 будет перезапускать его повторно, чисто изолируя ошибки настройки от ошибок времени выполнения приложения.
Практический поток триажа
Когда вы на дежурстве, используйте повторяемый путь:
- Проверьте
kubectl get pods -n <namespace> -o wide, чтобы увидеть статус, количество перезапусков, возраст и размещение на узле. - Выполните
kubectl describe podи прочитайте Events, State, Last State, точки монтирования и настройки ресурсов. - Получите журналы с помощью
kubectl logs, добавив--previousдля перезапущенных контейнеров и-cдля многоконтейнерных Pod'ов. - Если Pod находится в статусе
Pending, проверьте планирование, пятна, метки узлов и PVC, прежде чем читать журналы приложения. - Если Pod находится в статусе
Running, но не получает трафик, проверьте пробы готовности, селекторы Service и конечные точки. - Если Pod был завершен из-за OOM, сравните лимиты с реальными графиками памяти, прежде чем просто увеличивать число.
Этот порядок не позволяет вам сразу переходить к приложению, когда Kubernetes еще даже не запустил его.
Финальная проверка
Самая полезная привычка — отделять симптомы от причин. CrashLoopBackOff — это симптом. Причиной может быть отсутствующий секрет, неудачная миграция, проба жизнеспособности или лимит памяти. Pending — это симптом. Причиной могут быть запросы CPU, PVC, пятно или селектор узла. Позвольте статусу Pod'а указать вам, где искать, а затем позвольте событиям и журналам рассказать вам, что изменилось.