Как поднять контейнер в двух репликах, чтобы они не встретились на одной worker-ноде
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Обеспечение anti-affinity для Docker Swarm и Kubernetes
Для гарантии, что реплики контейнера не окажутся на одной worker-ноде, используются механизмы anti-affinity (распределенности). Конкретная реализация зависит от оркестратора.
Docker Swarm Mode
В Docker Swarm используется распределение задач (tasks) и ограничения размещения (placement constraints).
Способ 1: Глобальная служба (global service) При создании службы в глобальном режиме Docker автоматически запускает ровно одну задачу на каждой подходящей ноде в кластере. Это гарантирует, что реплики не встретятся.
docker service create --name my_global_service --mode global nginx:alpine
Способ 2: Ограничения размещения с anti-affinity
Для реплицируемой службы (replicated service) можно использовать --placement-pref, чтобы задания не размещались на одной ноде.
# Создание overlay-сети (если требуется)
docker network create -d overlay my_network
# Создание службы с двумя репликами и anti-affinity
docker service create \
--name my_service \
--replicas 2 \
--placement-pref 'spread=node.labels.az' \
--network my_network \
nginx:alpine
Ключевой параметр spread=node.labels.az указывает Swarm максимально "размазать" задачи по значениям метки az (например, availability zone) нод. Можно использовать и другие метки, например, spread=node.id для распределения по уникальным ID нод.
Kubernetes
В Kubernetes управление размещением Pod'ов осуществляется через affinity/anti-affinity правила в спецификации Pod или Deployment.
Пример: Anti-affinity на уровне Pod (podAntiAffinity)
Создадим Deployment с двумя репликами и правилом, которое не позволит планировщику разместить Pod'ы на одной ноде.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: "kubernetes.io/hostname"
Ключевые моменты этого манифеста:
podAntiAffinity: Определяет правило "отталкивания" Pod'ов.requiredDuringSchedulingIgnoredDuringExecution: Жесткое правило. Если планировщик не может найти подходящую ноду, Pod останется в состоянииPending. Есть более мягкий вариантpreferredDuringSchedulingIgnoredDuringExecution.labelSelector: Указывает, к каким Pod'ам применять правило. Здесь правило применяется к Pod'ам с меткойapp: nginx, включая сами Pod'и этого Deployment. Это заставляет их "избегать" друг друга.topologyKey: "kubernetes.io/hostname": Это самый важный параметр. Он определяет домен топологии. Планировщик гарантирует, что в рамках одного значения этого ключа (то есть на одной физической или виртуальной ноде) не будет больше одного Pod'а, соответствующегоlabelSelector. Использование стандартной метки нодыkubernetes.io/hostname— самый прямой способ гарантировать размещение на разных нодах.
Более надежная стратегия:
В продакшн-средах часто используют метки failure-domain, такие как topology.kubernetes.io/zone, чтобы распределять Pod'ы не только по разным нодам, но и по разным зонам доступности (Availability Zones), повышая отказоустойчивость.
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: nginx
topologyKey: "topology.kubernetes.io/zone"
- labelSelector:
matchLabels:
app: nginx
topologyKey: "kubernetes.io/hostname"
Этот пример определяет два правила: сначала требуется разместить Pod'ы в разных зонах, а затем, внутри зоны, на разных нодах.
Выводы и рекомендации
- Для Docker Swarm предпочтительным способом является использование глобального режима (
--mode global) для системных сервисов или правил размещения сspread(--placement-pref) для контроля распределения. - Для Kubernetes стандартом де-факто является Pod Anti-Affinity с
topologyKey: "kubernetes.io/hostname". Это четкое и ясное указание планировщику. - Важно помнить: Если в кластере меньше worker-нод, чем требуется реплик, "лишние" Pod'ы или задачи останутся в состоянии ожидания (
Pendingв K8s), так как правило не может быть выполнено. - Для проверки в Kubernetes используйте команды:
kubectl get pods -o wide # Покажет, на каких нодах запущены Pod'ы kubectl describe pod <pod_name> # В событиях (Events) можно увидеть логику принятия решений планировщиком
Таким образом, использование механизмов anti-affinity — обязательная практика для развертывания отказоустойчивых stateful или критичных приложений, гарантирующая, что выход из строя одной физической ноды не приведет к полной недоступности сервиса.