← Kubernetes: планирование
6 мин · средне · Урок 4 из 7

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

node-1 · ssd · zone=a app node-2 · nvme · weight 0 node-3 · hdd · отсеяна
Required оставил ssd и nvme, preferred по zone=a привёл Pod на node-1.

Два режима:

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

NotIn и DoesNotExist дают «anti-affinity к нодам» без taints. Удобно, когда ты не админ кластера и не можешь taint-ить ноды, но хочешь оградиться от какой-то группы нод.

Эффект preferred-правил заметен только там, где у шедулера есть выбор. В кластере из одной ноды скоринг ничего не меняет, а вот required-ветка работает и там: не совпал label, Pod остаётся в Pending.

Что часто ломается

Связанные команды

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    # проверить текущий шаг

Коротко

Прошли материал до конца? Отметьте урок пройденным.