Распределение модулей по нескольким узлам/зонам/регионам
В крупномасштабном кластере K8s, например, с более чем 50 рабочими узлами или рабочими узлами, расположенными в разных зонах или регионах, вы можете захотеть распределить свои модули рабочей нагрузки по разным узлам, зонам или даже регионам.
Это позволяет вашим рабочим нагрузкам извлекать выгоду из высокой доступности и использования кластера. Например:
Управлять распределением модулей в кластере непросто. Функция Pod affinity
и anti-affinity
в K8s позволяет в некоторой степени контролировать размещение Pod. Однако эти функции решают только часть вариантов использования распространения Pods.
Чтобы равномерно распределить модули Pod по кластеру для обеспечения высокой доступности и эффективного использования ресурсов кластера, был введен плагин планирования PodTopologySpread
. Первый стабильный релиз этого плагина был в K8s v1.19.
Поле топологииSpreadConstraints
Это поле topologySpreadConstraints
основано на метках узлов для определения топологического домена (доменов), в котором находится каждый рабочий узел. Вы можете определить одну или несколько записей topologySpreadConstraints
, чтобы указать kube-scheduler
, как размещать каждый входящий модуль по отношению к существующим модулям в вашем кластере.
Когда Pod определяет более одного topologySpreadConstraint, эти ограничения объединяются с помощью логической AND
операции: kube-scheduler
ищет узел для входящего Pod, который удовлетворяет всем настроенным ограничениям.
Шаблон topologySpreadConstraints
выглядит так:
--- apiVersion: v1 kind: Pod metadata: name: example-pod spec: # Configure a topology spread constraint topologySpreadConstraints: - maxSkew: <integer> minDomains: <integer> # optional; beta since v1.25 topologyKey: <string> whenUnsatisfiable: <string> labelSelector: <object> matchLabelKeys: <list> # optional; alpha since v1.25 nodeAffinityPolicy: [Honor|Ignore] # optional; alpha since v1.25 nodeTaintsPolicy: [Honor|Ignore] # optional; alpha since v1.25 ### other Pod fields go here
maxSkew
Он описывает степень неравномерного распределения подов. Вы должны указать это поле, и число должно быть больше нуля.
Если вы установите whenUnsatisfiable: DoNotSchedule
, то maxSkew
определяет максимально допустимую разницу между количеством совпадающих модулей в целевой топологии и глобальным минимумом (минимальное количество совпадающих модулей в подходящем домене или ноль, если количество подходящих доменов меньше MinDomains). .
Например, в кластере из 3 зон MaxSkew
устанавливается равным 1, а модули с одинаковым labelSelector
распределяются как 1/1/0:us-east-1a/us-east-1b/us-east-1c, если MaxSkew
равно 1, входящий модуль может быть запланирован только для us-east-1c, чтобы стать 1/1/1; планирование его на us-east-1a или us-east-1b приведет к тому, что ActualSkew(2-0) на us-east-1a(us-east-1b) нарушит MaxSkew(1); если MaxSkew
равно 2, входящий модуль может быть запланирован для любой зоны.
Когда whenUnsatisfiable=ScheduleAnyway
, он используется для предоставления более высокого приоритета топологиям, которые его удовлетворяют. Это обязательное поле. Значение по умолчанию — 1, 0 не допускается.
minDomains
Указывает минимальное количество подходящих доменов. Это поле является необязательным. Домен — это конкретный экземпляр топологии. Допустимый домен — это домен, узлы которого соответствуют селектору узлов. Это бета-поле, и оно включено по умолчанию в K8sv1.25
.
ключ топологии
Это ключ меток узла. Узлы, имеющие метку с этим ключом и одинаковыми значениями, считаются находящимися в одной топологии. Мы называем каждый экземпляр топологии (другими словами, пару «ключ-значение») доменом.
Планировщик попытается разместить сбалансированное количество модулей в каждом домене. Кроме того, мы определяем подходящий домен как домен, узлы которого соответствуют требованиям nodeAffinityPolicy
и nodeTaintsPolicy
.
Например, в шаблоне пода вы должны определить следующее:
topologySpreadConstraints:
maxSkew: 1 topologyKey: topology.kubernetes.io/zone
Если ваши рабочие узлы имеют метку: topology.kubernetes.io/zone=us-east-1a/b/c/d
.
Обратите внимание, что в
K8sv1.21
есть ошибка, из-за которой кластерный автомасштабировщик не реагирует должным образом на новую меткуtopology.kubernetes.io/zone
, вам нужно будет использоватьfailure-domain.beta.kubernetes.io/zone
.
когда неудовлетворительно
Это указывает, как поступать с подом, если он не удовлетворяет ограничению распространения:
- DoNotSchedule (по умолчанию) указывает планировщику не планировать его.
- ScheduleAnyway указывает планировщику по-прежнему планировать его, отдавая приоритет узлам, которые минимизируют перекос.
селектор меток
Он используется для поиска подходящих модулей. Поды, соответствующие этому селектору меток, подсчитываются для определения количества подов в соответствующем домене топологии.
matchLabelKeys
Это список ключей меток стручков для выбора стручков, по которым будет рассчитываться распространение. Ключи используются для поиска значений из меток модулей, эти метки «ключ-значение» объединяются по И с labelSelector
для выбора группы существующих модулей, по которым будет рассчитываться распространение для входящего модуля. Ключи, которых нет в метках модулей, будут игнорироваться. Нулевой или пустой список означает совпадение только с labelSelector.
С matchLabelKeys
пользователям не нужно обновлять pod.spec
между разными версиями. Контроллеру/оператору просто нужно установить разные значения для одного и того же ключа метки для разных версий. Планировщик автоматически примет значения на основе matchLabelKeys
.
Например, если пользователи используют Deployment
, они могут использовать метку с ключом pod-template-hash, который автоматически добавляется контроллером Deployment
, чтобы различать разные версии в одном развертывании.
Например:
topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule matchLabelKeys: - app - pod-template-hash
Обратите внимание, что поле matchLabelKeys
— это альфа-поле, добавленное в версии 1.25. Вы должны включить шлюз функции MatchLabelKeysInPodTopologySpread, чтобы использовать его.
nodeAffinityPolicy
Он указывает, как мы будем обрабатывать nodeAffinity/nodeSelector
пода при расчете перекоса распространения топологии пода. Есть два варианта:
- Честь: в расчеты включаются только узлы, соответствующие nodeAffinity/nodeSelector.
- Игнорировать: nodeAffinity/nodeSelector игнорируются. В расчеты включаются все узлы.
Если это значение равно null, поведение эквивалентно политике чести.
Это поле является полем альфа-уровня в версии 1.25.
nodeTaintsPolicy
Он указывает, как мы будем обрабатывать дефекты узлов при расчете перекоса распространения топологии модуля. Как и в случае с nodeAffinityPolicy, есть два варианта:
- Честь: включаются узлы без испорченных данных, а также испорченные узлы, для которых входящий модуль имеет допуск.
- Игнорировать: дефекты узла игнорируются. Все узлы включены. Если это значение равно null, поведение эквивалентно политике игнорирования.
Это поле является полем альфа-уровня в версии 1.25.
Демонстрация топологии модуля
Предположим, у вас есть кластер из 4 узлов, в котором 3 пода, помеченные как foo: bar, расположены в узлах node1, node2 и node3 соответственно:
Для 4-го модуля, если вы хотите запланировать его на node4 в us-east-1b, вы можете настроить его следующим образом:
kind: Pod apiVersion: v1 metadata: name: myapp labels: foo: bar spec: topologySpreadConstraints: - maxSkew: 1 topologyKey: zone whenUnsatisfiable: DoNotSchedule labelSelector: matchLabels: foo: bar containers: - name: nginx image: nginx:1.21.1
В приведенном выше примере конфигурация topologyKey: zone
означает, что равномерное распределение будет применяться только к узлам с меткой zone: <any value>
, узлы без метки zone
будут пропущены. Поле whenUnsatisfiable: DoNotSchedule
сообщает планировщику, что входящий под должен оставаться в ожидании, если планировщик не может найти способ удовлетворить ограничение.