Распределение модулей по нескольким узлам/зонам/регионам

В крупномасштабном кластере 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 сообщает планировщику, что входящий под должен оставаться в ожидании, если планировщик не может найти способ удовлетворить ограничение.