В этом разделе описано, как в кластере Managed Kubernetes настроить запуск подов на рабочих узлах в определенных зонах доступности с использованием зональной топологии. Такой способ размещения полезен, когда:
подам необходим доступ к данным, хранящимся на зональном диске;
поды должны работать совместно с другими зональными ресурсами;
поды необходимо распределить по разным зонам доступности для обеспечения отказоустойчивости.
Создайте кластер и две группы узлов в разных зонах доступности. Например, в зонах ru.AZ-1 и ru.AZ-2.
Если кластер создан ранее, убедитесь, что группы узлов в нем развернуты в каждой зоне, которую вы хотите использовать. Каждая группа узлов размещается только в одной зоне доступности.
Чтобы проверить, в каких зонах находятся узлы кластера, выполните команду:
kubectl get nodes -L topology.kubernetes.io/zone
Пример вывода:
NAME STATUS ROLES AGE VERSION ZONEnp-az1-7f9c4-abc***** Ready <none> 2d v1.34.1 ru.AZ-1np-az2-5d8b2-fgh***** Ready <none> 2d v1.34.1 ru.AZ-2
Чтобы разрешить размещение в нескольких зонах, используйте nodeAffinity. В режиме required под будет размещен только в разрешенных зонах, а в режиме preferred планировщик постарается выбрать одну из них, но не гарантирует это.
Создайте файл cloudru-node-affinity.yaml и сохраните следующий манифест:
apiVersion: apps/v1kind: Deploymentmetadata:name: cloudru-node-affinityspec:replicas: 3selector:matchLabels:app: cloudru-node-affinitytemplate:metadata:labels:app: cloudru-node-affinityspec:containers:- name: nginximage: emk8s.cr.cloud.ru/nginx:latestports:- containerPort: 80affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: topology.kubernetes.io/zoneoperator: Invalues:- ru.AZ-1- ru.AZ-2
Манифест создает Deployment с тремя репликами и размещает поды в зонах ru.AZ-1 или ru.AZ-2 в зависимости от доступности узлов в этих зонах.
Для более гибкого размещения можно использовать параметр preferredDuringSchedulingIgnoredDuringExecution. Он задает мягкое предпочтение зоны — если подходящих узлов в ней нет, под будет запущен в другой доступной зоне.
Пример использования параметра:
affinity:nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100preference:matchExpressions:- key: topology.kubernetes.io/zoneoperator: Invalues:- ru.AZ-1
Выполните команду:
kubectl apply -f cloudru-node-affinity.yaml
Kubernetes создаст поды на узлах в одной из указанных зон. На одном узле может работать несколько подов.
Чтобы разместить поды в определенной зоне, используйте nodeSelector. Он ограничивает выбор узлов по метке зоны: под будет размещен только на узлах с указанной меткой.
Создайте файл cloudru-node-selector.yaml и сохраните следующий манифест:
apiVersion: apps/v1kind: Deploymentmetadata:name: cloudru-node-selectorspec:replicas: 3selector:matchLabels:app: cloudru-node-selectortemplate:metadata:labels:app: cloudru-node-selectorspec:nodeSelector:topology.kubernetes.io/zone: "ru.AZ-1"containers:- name: nginximage: emk8s.cr.cloud.ru/nginx:latestports:- containerPort: 80
Манифест предписывает кластеру Managed Kubernetes разместить все реплики Deployment в зоне ru.AZ-1.
Правило nodeSelector эквивалентно правилу nodeAffinity с параметром requiredDuringSchedulingIgnoredDuringExecution, в котором указана одна зона.
Правила nodeSelector и nodeAffinity можно использовать одновременно — тогда узел для размещения подов должен соответствовать требованиям обоих.
Выполните команду:
kubectl apply -f cloudru-node-selector.yaml
Kubernetes создаст поды на узлах в указанной зоне доступности. На одном узле может работать несколько подов.
Чтобы равномерно распределить поды между зонами доступности, используйте topologySpreadConstraints. Правило задает максимально допустимое расхождение в количестве подов между зонами и применяется только в момент размещения пода.
Создайте файл cloudru-zone-spread.yaml и сохраните следующий манифест:
apiVersion: apps/v1kind: Deploymentmetadata:name: cloudru-zone-spreadspec:replicas: 6selector:matchLabels:app: cloudru-zone-spreadtemplate:metadata:labels:app: cloudru-zone-spreadspec:topologySpreadConstraints:- maxSkew: 1topologyKey: topology.kubernetes.io/zonewhenUnsatisfiable: DoNotSchedulelabelSelector:matchLabels:app: cloudru-zone-spreadmatchLabelKeys:- pod-template-hash- maxSkew: 1topologyKey: kubernetes.io/hostnamewhenUnsatisfiable: ScheduleAnywaylabelSelector:matchLabels:app: cloudru-zone-spreadmatchLabelKeys:- pod-template-hashcontainers:- name: nginximage: emk8s.cr.cloud.ru/nginx:latestports:- 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.
Выполните команду:
kubectl apply -f cloudru-zone-spread.yaml
Будет создан Deployment с шестью репликами и правилами:
Распределение по зонам.
Количество подов в любых двух зонах может отличаться не больше чем на один. В кластере с тремя зонами в каждой зоне будет по два пода.
Правило обязательное: если планировщик не может его выполнить, под остается в статусе «Pending».
Распределение по узлам внутри зоны.
Планировщик пытается разместить поды одной зоны на разных узлах.
Правило рекомендуемое: если в зоне только один узел, все поды этой зоны будут работать на нем.
Особенности 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: каждая реплика получит свой диск в своей зоне.