K8s by Example: Topology Spread Constraints

Topology spread ensures even Pod distribution across zones or nodes. More flexible than anti-affinity for complex spreading requirements. Controls the “skew” between domains.

topology-spread.yaml

Topology spread is defined in topologySpreadConstraints. maxSkew is the allowed imbalance. With 6 Pods across 3 zones and maxSkew=1, each zone gets 2 Pods.

spec:
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: topology.kubernetes.io/zone
      whenUnsatisfiable: DoNotSchedule
      labelSelector:
        matchLabels:
          app: my-app
topology-spread-multi.yaml

maxSkew: 1 means zones can differ by at most 1 Pod. DoNotSchedule enforces strictly; ScheduleAnyway is best-effort. Multiple constraints can have different strictness.

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: my-app
  - maxSkew: 2
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: ScheduleAnyway
    labelSelector:
      matchLabels:
        app: my-app
topology-spread-mindomains.yaml

minDomains ensures Pods spread across at least N domains. Prevents all Pods landing in one zone when cluster is small. Only applies with DoNotSchedule.

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    minDomains: 3
    labelSelector:
      matchLabels:
        app: my-app
topology-spread-taints.yaml

nodeTaintsPolicy controls whether tainted nodes are considered. Honor (default) excludes nodes with taints Pod doesn’t tolerate. Ignore includes all nodes in calculation.

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: DoNotSchedule
    nodeTaintsPolicy: Honor
    labelSelector:
      matchLabels:
        app: my-app
topology-spread-affinity.yaml

nodeAffinityPolicy controls whether node affinity is considered in spread calculation. Honor only counts nodes matching affinity. Ignore counts all nodes.

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: workload-type
                operator: In
                values: [compute]
  topologySpreadConstraints:
    - maxSkew: 1
      topologyKey: kubernetes.io/hostname
      whenUnsatisfiable: DoNotSchedule
      nodeAffinityPolicy: Honor
      labelSelector:
        matchLabels:
          app: my-app
topology-spread-production.yaml

Combine zone and node spreading for production workloads. Strict zone spread prevents zone failure impact. Flexible node spread handles uneven node counts.

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: my-app
  - maxSkew: 1
    topologyKey: kubernetes.io/hostname
    whenUnsatisfiable: ScheduleAnyway
    labelSelector:
      matchLabels:
        app: my-app
topology-spread-rollout.yaml

matchLabelKeys automatically selects Pods from the same controller (Deployment revision). Prevents spread calculation from including Pods from old ReplicaSets during rollout.

topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        app: my-app
    matchLabelKeys:
      - pod-template-hash
terminal

Debug topology spread by checking Pod distribution and scheduler events. Verify zone labels on nodes. Use custom columns to see topology at a glance.

$ kubectl get pods -l app=my-app -o wide
NAME         READY   STATUS    NODE
my-app-1     1/1     Running   node-1
my-app-2     1/1     Running   node-2
my-app-3     1/1     Running   node-3

$ kubectl get nodes -L topology.kubernetes.io/zone
NAME     STATUS   ZONE
node-1   Ready    us-east-1a
node-2   Ready    us-east-1b
node-3   Ready    us-east-1c

$ kubectl describe pod my-app-xyz
Events:
  Type     Reason            Message
  Warning  FailedScheduling  0/3 nodes available:
    3 node(s) didn't match topology spread constraints

$ kubectl get pod my-app-1 -o yaml | grep -A20 topologySpreadConstraints

Index | GitHub | Use arrow keys to navigate |