Распределение подов
Общее устройство планировщика
Планировщик (scheduler) — это компонент, отвечающий за распределение подов между узлами кластера на основе доступных ресурсов и заданных правил. Он выбирает наиболее подходящий узел для запуска каждого пода, учитывая множество факторов, таких как доступность CPU и памяти, расположение данных, топологию сети, метки узлов и специфические требования приложений.
Планировщик работает поэтапно, проходя через несколько фаз, в каждой из которых задействуются различные плагины. Эти плагины оценивают узлы на соответствие заданным критериям и помогают выбрать оптимальный вариант для размещения пода.
Основные задачи планировщика:
- Фильтрация (Filtering) — определение узлов, которые соответствуют требованиям пода.
- Оценка (Scoring) — присвоение баллов узлам на основе различных критериев (чем выше балл, тем лучше узел).
- Принятие решения (Binding) — назначение пода на лучший узел по итогам оценки.
Используемые плагины могут работать в одной или нескольких фазах. Например, один плагин может участвовать только в фильтрации, а другой — как в фильтрации, так и в оценке.
Примеры плагинов:
-
ImageLocality (фаза: Scoring) — отдает предпочтение узлам, на которых уже есть образы контейнеров, необходимые для пода. Это помогает сократить время развертывания, так как контейнеру не нужно загружать образ с удаленного репозитория.
-
TaintToleration (фазы: Filtering, Scoring) — реализует механизм taints и tolerations. Позволяет избежать назначения подов на узлы с нежелательными характеристиками или, наоборот, принудительно размещать их на специальных узлах (например, с выделенными ресурсами).
-
NodePorts (фаза: Filtering) — проверяет, есть ли у узла свободные порты, необходимые для работы пода. Это особенно важно для сервисов с фиксированными NodePort, так как на одном узле не может быть двух подов, использующих один и тот же порт.
С полным списком плагинов можно ознакомиться в документации Kubernetes.
Этапы планирования подов
Процесс назначения подов на узлы в DKP проходит через несколько ключевых этапов. Планировщик анализирует доступные узлы, применяет различные критерии фильтрации и оценки, а затем выбирает наиболее подходящий узел для запуска пода.
На первой фазе Filtering — активируются плагины фильтрации (filter-плагины), которые из всех доступных узлов выбирают те, которые удовлетворяют определенным условиям фильтрации (например, taints, nodePorts, nodeName, unschedulable и другие).
Если узлы распределены по различным зонам (availability zones), DKP чередует выбор зон, чтобы избежать размещения всех подов в одной зоне. Например, если узлы расположены так:
Zone 1: Node 1, Node 2, Node 3, Node 4
Zone 2: Node 5, Node 6
То выбор будет происходить в таком порядке:
Node 1, Node 5, Node 2, Node 6, Node 3, Node 4
Важно отметить, что планировщик не проверяет все узлы в кластере, а лишь их часть, что оптимизирует процесс планирования. Количество проверяемых узлов зависит от размера кластера:
- В небольших кластерах (≤50 узлов) проверяются все узлы;
- В средних кластерах (100 узлов) — 50% узлов;
- В очень крупных кластерах (5000 узлов) — 10% узлов;
- Минимальный порог — 5% узлов, если их количество превышает 5000.
После завершения этапа фильтрации начинается фаза оценки (Scoring). На этом этапе каждый узел из отфильтрованного списка получает баллы от различных scoring-плагинов, которые анализируют его характеристики и соответствие требованиям пода.
Каждый плагин оценивает узел по своему критерию и присваивает ему определенное количество баллов. Затем все оценки суммируются, формируя общий рейтинг узла.
Основные факторы, которые учитываются при оценке:
- Загрузка узла (
pod capacity
) – учитывается доступное количество CPU и памяти. Узлы с большим количеством свободных ресурсов получают более высокий балл. - Локальность образов контейнеров (ImageLocality) – узлы, на которых уже загружены необходимые образы контейнеров, получают преимущество.
- Учет
affinity
иanti-affinity
– если под должен размещаться рядом с определенными подами или, наоборот, быть от них изолирован, узлы, соответствующие этим требованиям, получают более высокий приоритет. - Доступность хранилища (
volume provisioning
) – узлы, на которых доступны необходимые PersistentVolumes, оцениваются выше.
После суммирования всех оценок узел с наибольшим итоговым баллом выбирается для запуска пода.
Если несколько узлов получили одинаковую максимальную оценку, выбор происходит случайным образом, чтобы избежать неэффективного предсказуемого распределения нагрузки.
Как изменить или расширить логику работы планировщика
Планировщик можно гибко настраивать и расширять с помощью дополнительных плагинов. Это позволяет адаптировать его поведение под специфические требования кластера, например, учитывать нестандартные метрики, особые правила распределения нагрузки или приоритетность узлов.
Каждый такой плагин представляет собой вебхук, который должен соответствовать следующим требованиям:
- Использование TLS – соединение с плагином должно быть защищено с использованием TLS.
- Доступность – плагин должен быть развернут как сервис внутри кластера и доступен через HTTP(S).
- Поддержка стандартных оепраций Verbs,
filterVerb = filter
– участвует в фильтрации узлов перед выбором места для пода,prioritizeVerb = prioritize
– участвует в оценке узлов на этапе Scoring. - Кэширование информации об узлах – предполагается, что все подключаемые плагины могут кэшировать информацию об узле (параметр
nodeCacheCapable: true
) для повышения производительности.
Подключение расширенного (extender) плагина осуществляется через ресурс KubeSchedulerWebhookConfiguration – ресурс определяет конфигурацию внешнего вебхука планировщика (kube-scheduler) и позволяет учитывать более сложные условия при планировании нагрузки в кластере, например:
- Размещение подов приложений организации хранилища данных ближе к самим данным,
- Приоритизация узлов в зависимости от их состояния (сетевой нагрузки, состояния подсистемы хранения и т.д.),
- Разделение узлов на зоны, и т.п.
Пример подключения внешнего плагина планировщика через вебхук:
apiVersion: deckhouse.io/v1alpha1
kind: KubeSchedulerWebhookConfiguration
metadata:
name: sds-replicated-volume
webhooks:
- weight: 5
failurePolicy: Ignore
clientConfig:
service:
name: scheduler
namespace: d8-sds-replicated-volume
port: 8080
path: /scheduler
caBundle: ABCD=
timeoutSeconds: 5
Если используется параметр failurePolicy: Fail
, то при сбое работы вебхука планировщик полностью прекратит свою работу, и новые поды не смогут быть запущены. Рекомендуется тщательно тестировать работу расширяемых плагинов перед развертыванием в продуктивной среде.
Дополнительные механизмы управления размещением подов
Deckhouse Kubernetes Platform предоставляет гибкие механизмы для управления размещением подов в кластере. Эти механизмы позволяют оптимизировать балансировку нагрузки, повышать отказоустойчивость и изолировать системные и пользовательские нагрузки.
Основные способы управления размещением подов:
- Использование меток узлов (
NodeGroup.spec.nodeTemplate.labels
). Позволяет явно указать, на каких узлах должны запускаться определенные поды, с помощьюspec.nodeSelector
илиspec.affinity.nodeAffinity
. - Настройка taints и tolerations. Позволяет ограничить запуск подов только на определенных узлах, предотвращая их размещение на неподходящих хостах.
- Использование пользовательских toleration-ключей (
settings.modules.placement.customTolerationKeys
). Позволяет управлять запуском критически важных компонентов Deckhouse (например, CNI и CSI) на выделенных узлах.
-
Использование nodeSelector — позволяет явно указывать узлы, на которых должны запускаться поды. Это делается путем назначения лейблов узлам и последующего использования
spec.nodeSelector
в манифесте пода. Пример:Допустим, у нас есть узел
kube-system-1
, предназначенный для сервисов мониторинга. Добавим на него метку:d8 k label node kube-system-1 node-role/monitoring=""
Теперь, чтобы развернуть поды только на этом узле, в Deployment добавляется nodeSelector:
nodeSelector: node-role/monitoring: ""
-
Использование taints и tolerations — в отличие от nodeSelector, taints позволяют запретить запуск подов на узлах, если у них нет соответствующего toleration. Это гарантирует, что на узле будут запускаться только определенные сервисы. Пример:
Например, есть узел
kube-frontend-1
, который предназначен только для Ingress-контроллеров. Добавим на него taint:d8 k taint node kube-frontend-1 node-role/frontend="":NoExecute
Теперь на этом узле не будет запускаться ничего, кроме подов, у которых есть соответствующий toleration:
tolerations: - effect: NoExecute key: node-role/frontend
Это позволяет защитить узел от случайного запуска на нем других подов, не относящихся к его предназначению.
-
DKP поддерживает механизм
customTolerationKeys
, который позволяет явно определять допустимые toleration-ключи. Это полезно, если требуется разместить системные сервисы (CNI, CSI и другие) на определенных узлах. Пример:customTolerationKeys: - dedicated.example.com - node-dedicated.example.com/master
Этот механизм позволяет:
- Разделять узлы на зоны, выделяя одни под Ingress-контроллеры, другие под системные сервисы (Prometheus, VPN, CoreDNS).
- Изолировать критически важные приложения от системных компонентов, избегая конкуренции за ресурсы.
Профили планировщика
Планировщик поддерживает несколько профилей, которые определяют стратегию размещения подов в кластере. В зависимости от выбранного профиля поды будут распределяться на узлы с разной логикой балансировки нагрузки.
default-scheduler
— стандартный профиль, который старается равномерно распределять поды по узлам, отдавая предпочтение менее загруженным.high-node-utilization
— профиль, который размещает поды на более загруженных узлах. Это может быть полезно в сценариях, когда необходимо уплотнить нагрузку и освободить неиспользуемые узлы для дальнейшего выключения или перераспределения ресурсов.
Для выбора профиля планировщика укажите его в параметре spec.schedulerName
манифеста пода. Пример:
apiVersion: v1
kind: Pod
metadata:
name: scheduler-example
labels:
name: scheduler-example
spec:
schedulerName: high-node-utilization
containers:
- name: example-pod
image: registry.k8s.io/pause:2.0
Перераспределение подов
DKP каждые 15 минут анализирует состояние кластера и выполняется вытеснение (eviction) подов, которые соответствуют условиям, описанным в активных стратегиях планирования. Вытеснённые поды вновь проходят стандартный процесс планирования с учётом текущего состояния кластера. Это позволяет перераспределить рабочие нагрузки в соответствии с выбранной стратегией и, при необходимости, освободить ресурсы некоторых узлов.
Учет класса приоритета пода
DKP создает набор классов приоритетов, которые определяют важность подов в кластере и порядок их вытеснения при перераспределении нагрузки.
Если в кластере не хватает ресурсов, поды с низким приоритетом могут быть вытеснены в пользу более важных подов. Это помогает гарантировать работу критически важных сервисов, даже если узлы перегружены.
Как DKP учитывает класс приоритета
Deckhouse Kubernetes Platform использует механизм приоритетов подов для определения порядка их вытеснения при нехватке ресурсов. Чем выше приоритет пода, тем меньшая вероятность его удаления в процессе перераспределения нагрузки.
Для управления этим механизмом используется модуль descheduler
, в манифесте которого можно задать порог приоритета с помощью параметра spec.priorityClassThreshold
. Это позволяет ограничить вытеснение подов, имеющих приоритет ниже заданного значения.
Порог можно указать двумя способами:
- По названию класса: если указать
priorityClassThreshold.name
, то вытеснению будут подлежать только поды с приоритетом ниже указанного класса приоритета. - По числовому значению: если указать
priorityClassThreshold.value
, то вытеснению подлежат поды с приоритетом ниже заданного значения (целочисленное значение).
Пример:
apiVersion: deckhouse.io/v1alpha2
kind: Descheduler
metadata:
name: custom
spec:
priorityClassThreshold:
name: high-priority
Какие поды не вытесняются
-
DKP не вытесняет под в следующих случаях:
- Под находится в пространстве имен
d8-*
илиkube-system
; - Под имеет priorityClassName
system-cluster-critical
илиsystem-node-critical
; - Под связан с локальным хранилищем;
- Под связан с DaemonSet;
- Вытеснение пода нарушит Pod Disruption Budget (PDB);
- Нет доступных узлов для запуска вытесненного пода.
- Под находится в пространстве имен
Если несколько подов подходят под критерии вытеснения, DKP применяет дополнительную логику:
- Сначала вытесняются поды с наименьшим приоритетом (
BestEffort
); - Затем — поды с приоритетом
Burstable
; - В последнюю очередь вытесняются
Guaranteed
поды, если это возможно.
DKP позволяет точно настроить, на какие поды и узлы будет распространяться вытеснение:
spec.podLabelSelector
— ограничивает поды по меткам;spec.namespaceLabelSelector
— фильтрует пространства имен, из которых поды будут рассматриваться для вытеснения;spec.nodeLabelSelector
— выбирает узлы по меткам.
Каждый из этих полей содержит стандартные поля matchExpressions
и matchLabels
, в которых можно указывать операторы In, NotIn, Exists, DoesNotExist и нужные значения меток.
Как включить или отключить перераспределение
Для включения функции перераспределения подов необходимо включить модуль descheduler
.
Это можно сделать следующими способами:
-
Через ресурс ModuleConfig (например, ModuleConfig/descheduler). Установите параметр
spec.enabled
в значениеtrue
илиfalse
:apiVersion: deckhouse.io/v1alpha1 kind: ModuleConfig metadata: name: descheduler spec: enabled: true # или false для отключения
-
Через команду
d8
(в подеd8-system/deckhouse
):d8 platform module enable descheduler # или, чтобы отключить: d8 platform module disable descheduler
-
Через веб-интерфейс Deckhouse:
- Перейдите в раздел «Deckhouse - «Модули»;
- Найдите модуль
descheduler
и нажмите на него; - Включите тумблер «Модуль включен».
У модуля нет обязательных настроек, то есть можно включить его и не настраивать дополнительно. При этом он будет работать со значениями по умолчанию.
Стратегии перераспределения
В параметре spec.strategies
перечисляются стратегии, которые вы хотите включить или настроить. Каждая стратегия имеет флаг enabled
(по умолчанию false
).
Ниже приведён список основных стратегий, доступных в DKP.
HighNodeUtilization — концентрирует нагрузку на меньшем числе узлов, вытесняя поды с недостаточно нагруженных узлов, чтобы они перезапустились на других узлах. Требуется:
- Специальная настройка модуля
descheduler
— MostAllocated; - (Опционально) включенный автоскейлинг кластера — чтобы неиспользуемые узлы могли быть выключены.
Стратегия включается параметром spec.strategies.highNodeUtilization.enabled
.
Параметр thresholds
задаёт порог для признания узла «недостаточно нагруженным». Если ресурс (CPU, память и т.д.) ниже всех пороговых значений, узел считается недогруженным, и DKP попытется вытеснить с него поды.
Пример:
---
apiVersion: deckhouse.io/v1alpha2
kind: Descheduler
metadata:
name: high-node-utilization
spec:
strategies:
highNodeUtilization:
enabled: true
thresholds:
cpu: 50
memory: 50
В GKE (Google Kubernetes Engine) нельзя настроить MostAllocated по умолчанию, но можно использовать стратегию optimize-utilization
.
LowNodeUtilization — более равномерно нагружает узлы. Стратегия выявляет недостаточно нагруженные узлы и вытесняет поды с других, избыточно нагруженных узлов. Стратегия предполагает, что пересоздание вытесненных подов произойдет на недостаточно нагруженных узлах.
Стратегия включается параметром spec.strategies.lowNodeUtilization.enabled
.
Недостаточно нагруженный узел — узел, использование ресурсов которого меньше всех пороговых значений, заданных в секции параметров strategies.lowNodeUtilization.thresholds
.
Избыточно нагруженный узел — узел, использование ресурсов которого больше хотя бы одного из пороговых значений, заданных в секции параметров strategies.lowNodeUtilization.targetThresholds
.
Узлы с использованием ресурсов в диапазоне между thresholds
и targetThresholds
считаются оптимально используемыми. Поды на таких узлах вытесняться не будут.
Пример:
apiVersion: deckhouse.io/v1alpha2
kind: Descheduler
metadata:
name: low-node-utilization
spec:
strategies:
lowNodeUtilization:
enabled: true
thresholds:
cpu: 20
targetThresholds:
cpu: 50
RemoveDuplicates — не позволяет нескольким подам одного контроллера (ReplicaSet/ReplicationController/StatefulSet/Job) одновременно находиться на одном узле (исключая DaemonSet). Если по каким-то причинам на одном узле оказалось 2+ подов от одного контроллера, стратегия вытеснит «лишние» поды, чтобы они равномернее распределились по кластеру.
Ситуация может возникать после кратковременного выхода из строя узлов, когда поды «переехали», а узел вернулся в строй, но поды так и остались сконцентрированы на другом узле.
Стратегия включается параметром strategies.removeDuplicates.enabled
.
Пример:
spec:
strategies:
removeDuplicates:
enabled: true
RemovePodsViolatingInterPodAntiAffinity — вытесняет любые поды, нарушающие правила inter-pod affinity/anti-affinity
. Например, если под2 и под3 имеют anti-affinity
по отношению к под1, но все трое по каким-то причинам оказались на одном узле, модуль вытеснит под1, чтобы под2 и под3 могли продолжать работу в соответствии со своими правилами.
Стратегия включается параметром strategies.removePodsViolatingNodeAffinity.enabled
.
Пример:
spec:
strategies:
removePodsViolatingInterPodAntiAffinity:
enabled: true
RemovePodsViolatingNodeAffinity — эта стратегия отвечает за вытеснение подов, которые больше не соответствуют своим требованиям node affinity
. Node affinity
определяет, какие узлы подходят поду при размещении:
requiredDuringSchedulingIgnoredDuringExecution
:- Под с таким типом правила обязан запускаться только на узле, удовлетворяющем заданным условиям (например, наличию определённой метки).
- Если со временем узел перестаёт удовлетворять этим условиям (например, метку убрали), а в кластере есть другой узел, который им по-прежнему соответствует, DKP вытеснит под, чтобы он заново запустился на подходящем узле.
preferredDuringSchedulingIgnoredDuringExecution
:- Под с таким типом правила может быть размещён на узле, который удовлетворяет предпочтению, но если подходящего узла не было, он запускается там, где мог.
- Если позже в кластере появляется более соответствующий узел, DKP может вытеснить под со старого узла, чтобы он перезапустился уже оптимальных условиях.
Таким образом, стратегия RemovePodsViolatingNodeAffinity помогает поддерживать соответствие подов их актуальным правилам привязки к узлам и гарантировать, что при появлении более подходящих вариантов поды не застрянут в неудачном местоположении.
Стратегия включается параметром spec.strategies.removePodsViolatingNodeAffinity.enabled
.
Пример:
spec:
strategies:
removePodsViolatingNodeAffinity:
enabled: true
nodeAffinityType:
- requiredDuringSchedulingIgnoredDuringExecution
- preferredDuringSchedulingIgnoredDuringExecution