Облачная платформаEvolution

Управлять размещением подов на узлах в разных зонах доступности


В этом разделе описано, как в кластере Managed Kubernetes настроить запуск подов на рабочих узлах в определенных зонах доступности с использованием зональной топологии. Такой способ размещения полезен, когда:

  • подам необходим доступ к данным, хранящимся на зональном диске;

  • поды должны работать совместно с другими зональными ресурсами;

  • поды необходимо распределить по разным зонам доступности для обеспечения отказоустойчивости.

Перед началом работы

  1. Создайте кластер и две группы узлов в разных зонах доступности. Например, в зонах ru.AZ-1 и ru.AZ-2.

    Если кластер создан ранее, убедитесь, что группы узлов в нем развернуты в каждой зоне, которую вы хотите использовать. Каждая группа узлов размещается только в одной зоне доступности.

  2. Чтобы проверить, в каких зонах находятся узлы кластера, выполните команду:

    kubectl get nodes -L topology.kubernetes.io/zone

    Пример вывода:

    NAME STATUS ROLES AGE VERSION ZONE
    np-az1-7f9c4-abc***** Ready <none> 2d v1.34.1 ru.AZ-1
    np-az2-5d8b2-fgh***** Ready <none> 2d v1.34.1 ru.AZ-2

Разместить поды в одной из нескольких зон

Чтобы разрешить размещение в нескольких зонах, используйте nodeAffinity. В режиме required под будет размещен только в разрешенных зонах, а в режиме preferred планировщик постарается выбрать одну из них, но не гарантирует это.

  1. Создайте файл cloudru-node-affinity.yaml и сохраните следующий манифест:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: cloudru-node-affinity
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: cloudru-node-affinity
    template:
    metadata:
    labels:
    app: cloudru-node-affinity
    spec:
    containers:
    - name: nginx
    image: emk8s.cr.cloud.ru/nginx:latest
    ports:
    - containerPort: 80
    affinity:
    nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
    nodeSelectorTerms:
    - matchExpressions:
    - key: topology.kubernetes.io/zone
    operator: In
    values:
    - ru.AZ-1
    - ru.AZ-2

    Манифест создает Deployment с тремя репликами и размещает поды в зонах ru.AZ-1 или ru.AZ-2 в зависимости от доступности узлов в этих зонах.

    Для более гибкого размещения можно использовать параметр preferredDuringSchedulingIgnoredDuringExecution. Он задает мягкое предпочтение зоны — если подходящих узлов в ней нет, под будет запущен в другой доступной зоне.

    Пример использования параметра:

    affinity:
    nodeAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
    - weight: 100
    preference:
    matchExpressions:
    - key: topology.kubernetes.io/zone
    operator: In
    values:
    - ru.AZ-1
  2. Выполните команду:

    kubectl apply -f cloudru-node-affinity.yaml

Kubernetes создаст поды на узлах в одной из указанных зон. На одном узле может работать несколько подов.

Разместить поды в определенной зоне

Чтобы разместить поды в определенной зоне, используйте nodeSelector. Он ограничивает выбор узлов по метке зоны: под будет размещен только на узлах с указанной меткой.

  1. Создайте файл cloudru-node-selector.yaml и сохраните следующий манифест:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: cloudru-node-selector
    spec:
    replicas: 3
    selector:
    matchLabels:
    app: cloudru-node-selector
    template:
    metadata:
    labels:
    app: cloudru-node-selector
    spec:
    nodeSelector:
    topology.kubernetes.io/zone: "ru.AZ-1"
    containers:
    - name: nginx
    image: emk8s.cr.cloud.ru/nginx:latest
    ports:
    - containerPort: 80

    Манифест предписывает кластеру Managed Kubernetes разместить все реплики Deployment в зоне ru.AZ-1.

    Правило nodeSelector эквивалентно правилу nodeAffinity с параметром requiredDuringSchedulingIgnoredDuringExecution, в котором указана одна зона.

    Правила nodeSelector и nodeAffinity можно использовать одновременно — тогда узел для размещения подов должен соответствовать требованиям обоих.

  2. Выполните команду:

    kubectl apply -f cloudru-node-selector.yaml

Kubernetes создаст поды на узлах в указанной зоне доступности. На одном узле может работать несколько подов.

Распределить поды равномерно по зонам

Чтобы равномерно распределить поды между зонами доступности, используйте topologySpreadConstraints. Правило задает максимально допустимое расхождение в количестве подов между зонами и применяется только в момент размещения пода.

  1. Создайте файл cloudru-zone-spread.yaml и сохраните следующий манифест:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: cloudru-zone-spread
    spec:
    replicas: 6
    selector:
    matchLabels:
    app: cloudru-zone-spread
    template:
    metadata:
    labels:
    app: cloudru-zone-spread
    spec:
    topologySpreadConstraints:
    - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
    matchLabels:
    app: cloudru-zone-spread
    matchLabelKeys:
    - pod-template-hash
    - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: ScheduleAnyway
    labelSelector:
    matchLabels:
    app: cloudru-zone-spread
    matchLabelKeys:
    - pod-template-hash
    containers:
    - name: nginx
    image: emk8s.cr.cloud.ru/nginx:latest
    ports:
    - containerPort: 80

    Где:

    • maxSkew — максимальное расхождение в числе подов между двумя зонами или узлами. Чем меньше значение, тем равномернее распределение. Значение 1 задает самое равномерное распределение.

    • topologyKey — метка узла, которая определяет домен топологии.

      Возможные значения:

      • topology.kubernetes.io/zone — зона доступности.

      • kubernetes.io/hostname — узел.

      Managed Kubernetes автоматически ставит метку зоны на каждый узел.

    • whenUnsatisfiable — определяет поведение, когда выполнить правило распределения невозможно.

      Возможные значения:

      • DoNotSchedule — не размещать под.

      • ScheduleAnyway — все равно разместить под, но постараться минимизировать перекос.

    • labelSelector — указывает, какие поды учитываются при расчете распределения. Укажите те же метки, что и в template.metadata.labels. Если селектор не совпадает с метками пода, планировщик не учитывает размещенные поды. В таком случае распределение по зонам не будет равномерным.

    • matchLabelKeys — позволяет учитывать только поды той же ревизии Deployment. При обновлении Deployment старые поды не влияют на распределение новых.

      По умолчанию работает с версии Kubernetes 1.27.

  2. Выполните команду:

    kubectl apply -f cloudru-zone-spread.yaml

Будет создан Deployment с шестью репликами и правилами:

  1. Распределение по зонам.

    Количество подов в любых двух зонах может отличаться не больше чем на один. В кластере с тремя зонами в каждой зоне будет по два пода.

    Правило обязательное: если планировщик не может его выполнить, под остается в статусе «Pending».

  2. Распределение по узлам внутри зоны.

    Планировщик пытается разместить поды одной зоны на разных узлах.

    Правило рекомендуемое: если в зоне только один узел, все поды этой зоны будут работать на нем.

Особенности topologySpreadConstraints

  • Планировщик учитывает только зоны, в которых есть узлы. Если узлы присутствуют лишь в двух зонах, поды будут распределены между этими двумя зонами.

    Чтобы задать минимальное количество зон, добавьте в правило для зон параметр minDomains: 3. Параметр используется только с whenUnsatisfiable: DoNotSchedule. Стабилен с версии Kubernetes 1.30.

    Если доступных зон меньше указанного значения, лишние поды остаются в статусе «Pending». Если в группе узлов недостающей зоны включено автомасштабирование, Cluster Autoscaler добавит в эту группу узлы.

  • Чтобы ограничить набор зон, используйте Node Affinity. Если в той же спецификации указать nodeAffinity или nodeSelector, планировщик будет распределять поды только по разрешенным зонам.

  • По умолчанию планировщик считает доступными и те зоны, в которых есть только узлы с Taints. Если в зоне присутствуют только группы узлов с Taints, которые под не допускает, то есть нет подходящих toleration, поды для этой зоны останутся в статусе «Pending».

    Чтобы не учитывать такие зоны, добавьте в правило параметр nodeTaintsPolicy: Honor.

  • Распределение применяется только в момент размещения пода. Планировщик не перемещает уже работающие поды. Например, после восстановления зоны или добавления группы узлов поды останутся на своих местах. Чтобы распределить их заново, перезапустите Deployment:

    kubectl rollout restart deployment cloudru-zone-spread
  • Если поды используют PersistentVolumeClaim с классами хранения cloudru-*, каждый диск создается в зоне своего пода, так как используется volumeBindingMode: WaitForFirstConsumer. После этого под может работать только в зоне своего диска.

    Для приложений с состоянием используйте StatefulSet: каждая реплика получит свой диск в своей зоне.