Module 10 ยท Lesson 10.2
Affinity & Topology Spread Constraints
Taints and tolerations push pods away from nodes. This lesson is about the opposite direction: pods expressing where they'd like to land, relative to nodes or to each other.
Node affinity: a richer nodeSelector
nodeAffinity does what nodeSelector does โ match pods to nodes by
label โ but with two levels of commitment:
requiredDuringSchedulingIgnoredDuringExecutionโ a hard requirement. No matching node, no scheduling (the pod staysPending, same as a taint blocking it, just from the opposite direction).preferredDuringSchedulingIgnoredDuringExecutionโ a weighted preference. The scheduler tries, but will place the pod elsewhere rather than leave itPending.
The "IgnoredDuringExecution" part matters: if a node's labels change after
a pod is already running there, that pod is not evicted โ affinity is
only evaluated at scheduling time, not continuously enforced like a taint's
NoExecute effect.
Pod affinity and anti-affinity: relative to other pods, not nodes
Instead of matching node labels, podAffinity/podAntiAffinity match
other pods' labels, then place relative to wherever those pods are:
affinity co-locates (put my cache next to the service that uses it,
same zone or even same node), anti-affinity spreads (don't put two
replicas of the same Deployment on the same node โ classic availability
pattern).
Topology spread constraints
๐ฅ node-a
๐ฅ node-b
Topology spread constraints: the newer, more precise tool
podAntiAffinity can spread pods, but its "spread evenly" logic gets
awkward with more than two replicas. topologySpreadConstraints says
directly what you mean: spread these pods across a given topology key
(kubernetes.io/hostname for nodes, topology.kubernetes.io/zone for
zones) within maxSkew of each other. For "give me even distribution
across nodes/zones," prefer this over podAntiAffinity โ it's purpose-built
for exactly that job, where anti-affinity is a more general (and clunkier)
tool bent toward the same end.
One more field worth knowing: nodeTaintsPolicy. By default it's
Ignore, meaning a tainted, unschedulable node still counts as a valid
topology domain when the scheduler computes skew โ which can leave a
constraint unsatisfiable for reasons that have nothing to do with topology
at all. Setting it to Honor excludes nodes your pod couldn't land on
anyway from that count. (If you did the previous lesson's lab and left
k8s-course-worker tainted, this is exactly why this lesson's lab sets
nodeTaintsPolicy: Honor too.)
The infrastructure analogy
nodeAffinity is a pod choosing its subnet/AZ by tag, the way you'd pin an
instance to a specific placement group. podAntiAffinity and topology
spread are what an ASG's spread-across-AZs setting does for you
automatically โ except here you're expressing it per-workload, not once at
the group level, so you can intentionally co-locate one workload while
spreading another.
In the lab, you'll apply a topology spread constraint to a small Deployment and confirm its replicas land on different nodes instead of piling onto one.
๐งช Lab: lab-21-affinity-and-topology-spread
Preview onlyGoal
Fix a topologySpreadConstraint that's silently making every replica
unschedulable, then confirm it actually spreads pods evenly across nodes.
Tasks
- Apply the starting manifests:
kubectl apply -f manifests/ - Check the pods:
kubectl get pods -n lab-21-affinity-and-topology-spread -o wideโ all 4 arePending. - Find out why:
kubectl describe pod <any-pod-name> -n lab-21-affinity-and-topology-spreadโ read the Events. The scheduler names the exact constraint it couldn't satisfy. - Check what topology keys your nodes actually carry:
kubectl get nodes --show-labelsโ notice there's notopology.kubernetes.io/zonelabel anywhere. A localkindcluster doesn't simulate cloud zones by default;kubernetes.io/hostname(every node has this one) is the key that actually distinguishes your 2 worker nodes here. - Edit
manifests/deployment.yaml: changetopologyKeyfromtopology.kubernetes.io/zonetokubernetes.io/hostname, then re-apply:kubectl apply -f manifests/deployment.yaml - Confirm all 4 pods are
Running, split 2-and-2 acrossk8s-course-workerandk8s-course-worker2:kubectl get pods -n lab-21-affinity-and-topology-spread -o wide
Check
Run the check once all 4 pods are Running and no single node has more
than 2 of them.
This lab runs against a real local Kubernetes cluster with an automated grader โ clone the repo and run make start to do it for real.
๐ Quiz
1. A pod has requiredDuringSchedulingIgnoredDuringExecution nodeAffinity for a label no node currently has. What happens?
2. A pod is already running on a node satisfying its nodeAffinity rule. Later, someone removes the matching label from that node. What happens to the running pod?scenario
3. You want 4 replicas of a Deployment spread evenly across your nodes, with a clear 'at most 1 extra per node' guarantee. What's the most direct tool?
4. Which of these describe podAffinity/podAntiAffinity correctly? (select all that apply)
5. In the ASG analogy, topologySpreadConstraints is closest to:
Progress isn't saved in this preview โ run the course locally to track completion and grade labs for real.