Node Affinity
nodeSelector works for exact label matching. But what if you want to schedule on nodes in eu-west-1a OR eu-west-1b? Or prefer nodes with SSD storage but allow HDD if no SSD is available? nodeAffinity provides these expressions through a richer set of operators and two scheduling modes: required and preferred.
Two scheduling modes
nodeAffinity operates in two modes:
requiredDuringSchedulingIgnoredDuringExecution: the Pod must be scheduled on a node that matches. If no matching node exists, the Pod stays Pending. This is equivalent tonodeSelectorbut more expressive.preferredDuringSchedulingIgnoredDuringExecution: the scheduler tries to place the Pod on a matching node but falls back to any node if none matches. This is a soft preference.
The IgnoredDuringExecution suffix means the rule only applies at scheduling time. If a node’s labels change after a Pod is already running there, the Pod is not evicted (just as with nodeSelector).
Required affinity
kubectl label node sim-worker zone=eu-west-1akubectl label node sim-worker2 zone=eu-west-1bnano required-affinity.yamlapiVersion: v1kind: Podmetadata: name: required-affinity-podspec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: zone operator: In values: - eu-west-1a - eu-west-1b containers: - name: app image: busybox:1.36 command: ['sh', '-c', 'sleep 3600']kubectl apply -f required-affinity.yamlkubectl get pod required-affinity-pod -o wideThe Pod is scheduled on either sim-worker (eu-west-1a) or sim-worker2 (eu-west-1b). Both match the In expression. This is impossible to express with nodeSelector.
Available operators
| Operator | Description |
|---|---|
In | Label value is in the list |
NotIn | Label value is not in the list |
Exists | Label key exists (any value) |
DoesNotExist | Label key does not exist |
Gt | Label value is greater than (numeric) |
Lt | Label value is less than (numeric) |
A Pod needs to schedule on nodes that do NOT have env=prod. Which operator expresses this?
Reveal answer
Use NotIn with key: env and values: [prod]. Or use DoesNotExist on the env key if you want to exclude any node labeled with env regardless of value. NotIn: [prod] still matches nodes with env=staging or other values.
Preferred affinity
nano preferred-affinity.yamlapiVersion: v1kind: Podmetadata: name: preferred-affinity-podspec: affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 80 preference: matchExpressions: - key: zone operator: In values: - eu-west-1a - weight: 20 preference: matchExpressions: - key: zone operator: In values: - eu-west-1b containers: - name: app image: busybox:1.36 command: ['sh', '-c', 'sleep 3600']kubectl apply -f preferred-affinity.yamlkubectl get pod preferred-affinity-pod -o wideThe scheduler adds weight points for each matching preference and scores nodes accordingly. eu-west-1a nodes score 80 points, eu-west-1b nodes score 20 points. The scheduler prefers eu-west-1a but will use any node if none in the preferred zones are available.
preferredDuringScheduling is a hint, not a guarantee. If the preferred node is full (not enough resources), the scheduler places the Pod on a less preferred node without any warning. Do not rely on preferences for hard placement requirements like data locality or GPU availability. Use required for those.
Combining required and preferred
You can use both modes together: a required rule ensures the Pod only goes to specific zones, while a preferred rule within that set favors certain nodes over others.
kubectl delete pod required-affinity-pod preferred-affinity-podkubectl label node sim-worker zone-kubectl label node sim-worker2 zone-A Pod has requiredDuringScheduling that limits scheduling to nodes with disk=ssd. The cluster has 3 SSD nodes: one in zone-A (weight=100 preferred) and two in zone-B (weight=10 preferred). All three are schedulable. Where does the Pod land?
Reveal answer
The zone-A node wins. The scheduler first filters to only the 3 SSD nodes (required rule). Then it applies the preferred weights within that filtered set. Zone-A scores 100, zone-B nodes score 10. The Pod goes to the highest-scoring node unless resource constraints override the preference.
Node affinity extends nodeSelector with OR conditions, negative selectors, and soft preferences. The next lesson compares taints/tolerations with node affinity to clarify which tool to use for each scenario.