Node Affinity: required и preferred
Жёсткие и мягкие правила размещения, операторы matchExpressions и где в nodeSelectorTerms прячется OR.
Что вообще такое nodeAffinity
nodeAffinity решает те же задачи, что nodeSelector, но с
расширенным набором возможностей: операторы помимо равенства,
soft-правила (предпочтения) и OR-логика между группами условий.
nodeSelector - частный случай affinity: один In-operator с одним
значением в required-ветке.
nodeSelector ограничен строгим равенством одного ключа и одного
значения. Когда подходит и «SSD-нода», и «NVMe-нода» (OR), он не
справится. Когда нужно «лучше зона A, но сойдёт любая» (soft
preference), тоже. Affinity покрывает оба случая.
Required vs preferred
Два режима:
requiredDuringSchedulingIgnoredDuringExecution. Жёсткое требование. Без подходящей ноды Pod останется в Pending. Для критичных условий: GPU-приложение без GPU-ноды не запустится в принципе.preferredDuringSchedulingIgnoredDuringExecution. Пожелание. Шедулер посчитает каждой ноде суммуweightот сработавших preferred-правил и выберет лучшую. Никто не подошёл, поставит куда есть. Для оптимизаций: «лучше рядом с базой», «лучше зона A, но и B сойдёт».
IgnoredDuringExecution означает: если labels ноды поменяются
после старта Pod, его не тронут. Перепланировку под новые условия
Kubernetes пока не делает, только при старте Pod.
Есть ещё длинная форма requiredDuringSchedulingRequiredDuringExecution,
но она пока не реализована, не используй.
Минимальный YAML
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disk
operator: In
values: [ssd, nvme]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 50
preference:
matchExpressions:
- key: zone
operator: In
values: [a]
Required и preferred можно ставить вместе. Required отфильтрует ноды (только SSD или NVMe), preferred поднимет в скоринге ноды с zone=a.
nodeSelectorTerms: OR между группами
nodeSelectorTerms это массив. Условия ВНУТРИ одного term
объединяются через AND. Между разными term это OR. Удобно для
«или GPU-нода, или CPU-нода с тегом fallback».
matchExpressions внутри term тоже AND. Так что term это AND-блок,
массив term это OR-блок.
Перепутать эти два массива легко, и ошибка тихая: условия из разных term
склеиваются в AND вместо OR, подходящих нод не остаётся, Pod висит в
Pending.
Операторы matchExpressions
In. Value ноды входит в список values.NotIn. Не входит. Для «не сажай на spot-инстансы».Exists. У ноды есть такой key, value не проверяется.DoesNotExist. У ноды нет такого key.Gt,Lt. Численное сравнение, для labels типаcpu-cores=16.
NotIn и DoesNotExist дают «anti-affinity к нодам» без taints.
Удобно, когда ты не админ кластера и не можешь taint-ить ноды, но
хочешь оградиться от какой-то группы нод.
Эффект preferred-правил заметен только там, где у шедулера есть выбор.
В кластере из одной ноды скоринг ничего не меняет, а вот required-ветка
работает и там: не совпал label, Pod остаётся в Pending.
Что часто ломается
requiredслишком строгое. Подходящих нод нет, Pod в Pending, никто не замечает пока инцидент. Делай по возможности preferred с большим weight.- labels у ноды поставили после старта Pod. Affinity не
перепланирует. Нужно
kubectl rollout restart. - Используют только
preferredдля критичного условия. Шедулер поставит куда угодно, потом мониторинг кричит, что workload не на своей ноде. - Веса
weightсильно близкие. Скоринг не различает, ноды выбираются почти случайно. Делай разрыв минимум в 10.
Связанные команды
kubectl get nodes -L disk -L zone
kubectl get nodes --show-labels
kubectl get pod <name> -o yaml | yq '.spec.affinity'
kubectl describe pod <name> | grep -A10 Events
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, labels: .metadata.labels}'
kubectl explain pod.spec.affinity.nodeAffinity
Домашка
Теорию закрепляет задание на своём кластере: 2 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start k8s-node-affinity # развернуть стартовое состояние
./run.sh verify k8s-node-affinity # проверить текущий шаг
Коротко
nodeAffinityрасширяетnodeSelector: операторы, предпочтения и OR-логика.- Required-ветка фильтрует ноды, preferred-ветка только добавляет очков в скоринге.
nodeSelectorTermsобъединяет группы через OR,matchExpressionsвнутри группы через AND.- Операторы:
In,NotIn,Exists,DoesNotExist,Gt,Lt. IgnoredDuringExecutionзначит, что смена labels на ноде уже запущенный Pod не двигает.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс