๐Ÿ“– Read-only preview โ€” lessons, diagrams, and quizzes only. The hands-on labs, live grading, and progress tracking require running the real course locally against a Kubernetes cluster.Run it locally โ†’

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 stays Pending, 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 it Pending.

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

pod-1
pod-2
pod-3

๐Ÿ–ฅ node-b

pod-4
no spread constraint โ€” the scheduler is free to pack replicas unevenly if that's where capacity happened to be.

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 only

Goal

Fix a topologySpreadConstraint that's silently making every replica unschedulable, then confirm it actually spreads pods evenly across nodes.

Tasks

  1. Apply the starting manifests: kubectl apply -f manifests/
  2. Check the pods: kubectl get pods -n lab-21-affinity-and-topology-spread -o wide โ€” all 4 are Pending.
  3. 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.
  4. Check what topology keys your nodes actually carry: kubectl get nodes --show-labels โ€” notice there's no topology.kubernetes.io/zone label anywhere. A local kind cluster 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.
  5. Edit manifests/deployment.yaml: change topologyKey from topology.kubernetes.io/zone to kubernetes.io/hostname, then re-apply: kubectl apply -f manifests/deployment.yaml
  6. Confirm all 4 pods are Running, split 2-and-2 across k8s-course-worker and k8s-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.