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

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