Объяснение ошибок планирования Kubernetes: решения и лучшие практики
Освойте планирование Kubernetes! Это руководство объясняет, почему Pod'ы застревают в состоянии 'Pending'. Научитесь диагностировать ошибки с помощью `kubectl describe`, решать проблемы, связанные с нехваткой CPU/памяти, преодолевать ограничения Node Affinity и правильно использовать Taints и Tolerations для надежного размещения рабочих нагрузок.
Объяснение ошибок планирования Kubernetes: решения и лучшие практики
Ошибки планирования Kubernetes обычно проявляются как pod, застрявший в состоянии Pending. Этот статус может показаться неопределенным, но он имеет конкретное значение: Kubernetes принял объект pod, но планировщик не нашел узел, удовлетворяющий требованиям pod. Контейнер не упал. Приложение не запустилось. Во многих случаях образ даже не был загружен.
Самый быстрый способ решить эти проблемы — сравнить то, что запрашивает pod, с тем, что может предложить кластер. Запросы CPU и памяти, метки узлов, правила сродства, taints, tolerations, постоянные тома, правила распределения топологии и квоты пространств имен могут блокировать размещение. Планировщик строг в отношении обязательных ограничений. Если одно обязательное правило исключает все узлы, pod ждет.
Диагностика зависших Pod'ов: первый шаг
Прежде чем пытаться исправить, необходимо точно диагностировать почему планировщик терпит неудачу. Основной инструмент для этого расследования — kubectl describe pod.
Когда Pod застревает в состоянии Pending, раздел Events вывода describe содержит критическую информацию, детализирующую процесс принятия решения о планировании и любые отклонения.
Использование kubectl describe pod
Всегда нацеливайтесь на проблемный Pod:
kubectl describe pod <pod-name> -n <namespace>
Изучите вывод, обращая особое внимание на раздел Events внизу. Сообщения здесь обычно указывают ограничение, которое помешало планированию. Распространенные сообщения связаны с Insufficient cpu, Insufficient memory, несоответствием селектора узлов, недопустимыми taints или связыванием томов.
Распространенные категории ошибок планирования и их решения
Сбои планирования обычно делятся на три основные категории: ограничения ресурсов, ограничения политик (Affinity/Anti-Affinity) и конфигурация узлов (Taints/Tolerations).
1. Ограничения ресурсов (недостаточно ресурсов)
Это самая частая причина. Планировщику требуется узел, который может удовлетворить запросы, определенные в спецификации Pod. Если ни на одном узле нет достаточного количества выделяемых CPU или памяти, Pod останется в состоянии Pending.
Выявление проблемы
В разделе Events будут показаны сообщения типа:
0/3 nodes are available: 3 Insufficient cpu.0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match node selector.
Эти сообщения могут быть объединены. Не останавливайтесь на первой фразе. Если три узла не работают по трем разным причинам, исправление только одной причины может все равно оставить pod в состоянии ожидания.
Решения для нехватки ресурсов
- Уменьшите запросы Pod: Если запросы Pod чрезмерно высоки, попробуйте уменьшить запросы CPU или
memoryв YAML Pod или Deployment. - Увеличьте емкость кластера: Добавьте больше узлов в кластер Kubernetes.
- Очистите существующие рабочие нагрузки: Уменьшите масштаб несущественных рабочих нагрузок, удалите заброшенные задания или скорректируйте завышенные запросы в существующих развертываниях. Используйте
kubectl drainдля обслуживания узлов, а не как обычную команду очистки. - Используйте Limit Ranges: Если в вашем пространстве имен не определены ограничения ресурсов, внедрите объекты
LimitRange, чтобы предотвратить накопление ресурсов отдельными Pod'ами.
2. Селекторы узлов и правила Affinity/Anti-Affinity
Kubernetes позволяет детально контролировать, где Pod'ы могут или должны быть размещены, с помощью nodeSelector, nodeAffinity и podAffinity/podAntiAffinity.
Несоответствие селектора узлов
Если вы определяете nodeSelector, который не соответствует ни одной метке, присутствующей на каком-либо доступном узле, Pod не может быть запланирован.
Пример фрагмента YAML (причина сбоя):
spec:
nodeSelector:
disktype: ssd-fast
containers: [...] # Pod остается в состоянии Pending, если ни один узел не имеет disktype=ssd-fast
Решение: Убедитесь, что метка, указанная в nodeSelector, существует хотя бы на одном узле (kubectl get nodes --show-labels) и что регистр символов совпадает точно.
Используйте целенаправленные проверки меток, когда в кластере много меток:
kubectl get nodes -L disktype,topology.kubernetes.io/zone
kubectl describe node <node-name>
Распространенная ошибка — использование метки, которая существовала в предыдущей группе узлов, но не в заменяющей группе узлов. После обновления кластера или миграции группы автомасштабирования старые правила размещения могут незаметно стать невыполнимыми.
Ограничения Node Affinity
nodeAffinity предлагает более гибкие правила (например, requiredDuringSchedulingIgnoredDuringExecution или preferredDuringSchedulingIgnoredDuringExecution). Если обязательное правило (required) не может быть выполнено, Pod остается в состоянии Pending.
Совет по диагностике: При использовании сложных правил сродства в разделе Events часто указывается: node(s) didn't match node selector.
Pod Affinity и Anti-Affinity
Эти правила управляют размещением относительно других Pod'ов. Если, например, правило Anti-Affinity требует, чтобы Pod не запускался на узле, на котором размещена определенная служба, но все узлы уже размещают эту службу, планирование завершится ошибкой.
Решение: Внимательно проверьте ключ топологии и селектор в ваших правилах сродства. Если правило анти-сродства слишком строгое, ослабьте требование или убедитесь, что целевые Pod'ы, выбранные правилом, действительно работают на узлах, которых вы хотите избежать.
Отдавайте предпочтение preferredDuringSchedulingIgnoredDuringExecution, когда правило выражает предпочтение, а не жесткое требование. Обязательное анти-сродство полезно для распределения реплик критически важных служб, но оно может блокировать развертывания в небольших кластерах. Например, три реплики со строгим анти-сродством по зонам не могут быть чисто запланированы в кластере только с двумя используемыми зонами.
3. Taints и Tolerations
Taints применяются непосредственно к узлам, чтобы отпугивать Pod'ы, в то время как Tolerations добавляются в спецификации Pod, чтобы разрешить им доступ к узлам с taints.
- Taint: Отталкивает Pod'ы, если у них нет соответствующего toleration.
- Toleration: Разрешает Pod'у быть запланированным на узел с соответствующим taint.
Выявление отклонения из-за Taint
В разделе Events будет явно указана причина отклонения:
0/3 nodes are available: 2 node(s) had taint {dedicated: special-workload, effect: NoSchedule}, that the pod didn't tolerate.
Решения для Taints и Tolerations
У вас есть два основных пути:
Измените Pod (рекомендуется для подов приложений): Добавьте необходимые
tolerationsв спецификацию Pod, соответствующие taint узла.Пример Toleration:
spec: tolerations: - key: "dedicated" operator: "Equal" value: "special-workload" effect: "NoSchedule" containers: [...]Измените узел (рекомендуется для администраторов кластера): Удалите taint с узла, если ограничение больше не требуется.
# Чтобы удалить taint kubectl taint nodes <node-name> dedicated:special-workload:NoSchedule-
Предупреждение о лучших практиках: Избегайте допуска глобального taint
node-role.kubernetes.io/master:NoScheduleна подах приложений, если вы не планируете намеренно размещать критические компоненты управления на мастер-узлах.
В более новых кластерах узлы управления обычно используют taint node-role.kubernetes.io/control-plane вместо старой терминологии master или наряду с ней. Проверьте фактические taints перед копированием toleration из старого манифеста:
kubectl describe node <node-name> | grep -i taints
Расширенные ограничения планирования
Менее распространенные, но важные ограничения также могут блокировать планирование:
Ограничения томов хранения
Если Pod запрашивает PersistentVolumeClaim (PVC), который в настоящее время не может быть привязан к доступному узлу (например, из-за особых требований провайдера хранилища или недоступности тома), Pod может остаться в состоянии Pending.
Диагностика: Сначала проверьте статус PVC (kubectl describe pvc <pvc-name>). Если PVC застрял в состоянии Pending, планирование Pod приостанавливается до тех пор, пока том не станет доступен.
Хранилище также может быть намеренно задержано с помощью volumeBindingMode: WaitForFirstConsumer в StorageClass. В этом режиме привязка ожидает, пока планировщик выберет подходящий узел, поскольку том может потребоваться создать в той же зоне, что и pod. Это нормально, но если ни один узел не удовлетворяет одновременно ограничениям pod и хранилища, pod остается в состоянии ожидания.
DaemonSets и распределение топологии
DaemonSets будут планироваться только на узлы, соответствующие их критериям выбора (если таковые имеются). Если кластер разделен или новый узел не соответствует селектору DaemonSet, он не будет запущен.
Ограничения распределения топологии (если определены) обеспечивают равномерное распределение. Если текущее распределение препятствует размещению на каком-либо узле при соблюдении ограничений распределения, планирование завершится ошибкой.
Сбои распределения топологии часто проявляются после частичного отказа. Предположим, одна зона недоступна, и развертывание имеет строгие ограничения распределения по зонам. Kubernetes может отказаться размещать новые реплики в оставшихся зонах, потому что это нарушило бы правило перекоса. Такое поведение защищает цели распределения, но во время сбоя вам может потребоваться временно ослабить ограничение, чтобы восстановить емкость.
Квоты пространств имен и LimitRanges
Pod также может быть заблокирован политикой пространства имен. ResourceQuota контролирует совокупное использование в пространстве имен. LimitRange может устанавливать значения по умолчанию или минимальные и максимальные значения ресурсов.
Проверьте их, когда спецификация pod выглядит разумной, но создание или планирование все равно не удается:
kubectl get resourcequota -n <namespace>
kubectl describe resourcequota -n <namespace>
kubectl get limitrange -n <namespace>
kubectl describe limitrange -n <namespace>
Проблемы с квотами распространены в общих кластерах разработки. У команды может быть достаточно физической емкости кластера, но ее квота пространства имен исчерпана старыми средами предварительного просмотра или завершенными заданиями, которые никогда не были очищены.
Реалистичная последовательность отладки
Когда pod находится в состоянии ожидания, используйте этот порядок:
- Запустите
kubectl describe podи скопируйте самое новое событие планирования. - Проверьте запрошенные CPU и память по сравнению с выделяемой емкостью узла с помощью
kubectl describe node. - Проверьте метки узлов, если pod использует
nodeSelector, сродство узлов или ключи топологии. - Проверьте taints на узлах-кандидатах и tolerations на pod.
- Проверьте PVC и StorageClasses, если pod монтирует постоянное хранилище.
- Проверьте квоты пространств имен и LimitRanges.
- Если ожидается, что Cluster Autoscaler поможет, проверьте его журналы или события.
Этот порядок важен, потому что ожидающий pod не является проблемой среды выполнения приложения. Перезапуск развертывания редко помогает, если только базовое ограничение не изменилось.
Лучшие практики для успешного планирования
Чтобы минимизировать проблемы с планированием, применяйте следующие операционные лучшие практики:
- Явно определяйте запросы ресурсов: Всегда устанавливайте разумные
requests(и необязательныеlimits) для CPU и памяти. Это позволяет планировщику точно оценивать емкость узла. - Используйте метки узлов для зонирования: Внедрите согласованную маркировку узлов (например,
hardware=gpu,zone=us-east-1a) и используйтеnodeSelectorилиnodeAffinityдля направления рабочих нагрузок на соответствующее оборудование. - Документируйте Taints и Tolerations: Если узлы помечены taints для обслуживания или аппаратной сегрегации, документируйте эти taints централизованно. Убедитесь, что манифесты приложений, требующие доступа к ресурсам с taints, включают соответствующие tolerations.
- Мониторьте Cluster Autoscaler (если используется): Если вы полагаетесь на решения масштабирования, убедитесь, что они функциональны. Отсутствие емкости, которое должно вызывать масштабирование, может молча терпеть неудачу, оставляя Pod'ы в ожидании.
- Просматривайте журналы планировщика (продвинутый уровень): Для глубокого погружения в диагностику просмотрите журналы самого компонента
kube-scheduler. В управляемых кластерах доступ может различаться в зависимости от провайдера, поэтому начните с событий pod и журналов панели управления, специфичных для провайдера.
Исправьте ограничение, а не симптом
Правильное исправление зависит от того, является ли ограничение случайным или преднамеренным. Если pod запрашивает 8 CPU, потому что кто-то скопировал производственный манифест в крошечный staging-кластер, уменьшите запрос для этой среды. Если pod нуждается в GPU, а узла с GPU не существует, добавление toleration не поможет; кластеру нужно правильное оборудование. Если taint защищает узлы базы данных от общих рабочих нагрузок, не удаляйте taint только для того, чтобы запланировать несвязанный pod.
Для производственных изменений делайте причину видимой в Git. Метки узлов, taints, правила сродства и запросы ресурсов — это контракты на размещение. Будущие операторы должны знать, существует ли правило для производительности, соответствия требованиям, доступа к оборудованию, контроля затрат или просто исторической случайности.
Примеры вводящих в заблуждение быстрых исправлений
Несколько распространенных исправлений заставляют немедленный статус Pending исчезнуть, создавая при этом более серьезную проблему в будущем.
Снижение запросов CPU может помочь, если первоначальный запрос был завышен, но это не бесплатный инструмент для увеличения емкости. Если приложению действительно нужен этот CPU во время пикового трафика, pod может запланироваться, а затем работать плохо под нагрузкой. Прежде чем агрессивно сокращать запросы, проверьте историю использования и задержки.
Добавление широкого toleration может запланировать pod, но он может приземлиться на узлах, зарезервированных для другой цели. Toleration говорит: "этому pod разрешено здесь находиться". Он не говорит: "этому pod следует предпочесть здесь". Если вам нужны и разрешение, и намерение, комбинируйте tolerations с сродством узлов или селекторами узлов.
Удаление правила анти-сродства может быстро восстановить реплики, но оно может поместить каждую реплику на один узел или в одну зону. Иногда это приемлемо во время сбоя, но это должно быть осознанным временным изменением, а не тихим постоянным отклонением.
Расширение кластера часто является правильным ответом, но только после того, как вы узнаете, что ожидающий pod может использовать новые узлы. Если pod требует метку, которой не будет у группы узлов с автоматическим масштабированием, добавление узлов просто даст вам больше неподходящих узлов.
Финальная проверка
Ожидающий pod — это неудача переговоров между pod и кластером. Pod запрашивает ресурсы, метки, хранилище, топологию и разрешение на приземление на определенные узлы. Кластер отвечает емкостью, taints, метками, квотами и доступными томами. kubectl describe pod показывает, где эти переговоры провалились. Как только вы внимательно прочитаете событие, большинство исправлений становятся очевидными: измените требования pod, измените доступную емкость кластера или исправьте политику, которая больше не соответствует реальности.