Challenge ·Medium

Split a Deployment Evenly Across Two Zones

A storefront needs eight replicas, and the platform team wants them split down the middle between the two availability zones. An uneven split must be refused by the scheduler rather than quietly tolerated.

Scenario

The cluster has three nodes. Two of them are tagged with the zone they sit in, and the third is reserved for system workloads and refuses everything else:

Nodetopology.kubernetes.io/zoneTakes your Pods
node-01zone-ayes
node-02zone-byes
cplane-01noneno

Have a look at what the nodes actually carry before you start:

kubectl get nodes --show-labels
kubectl describe node cplane-01 | grep -i taint

The storefront namespace is empty.


Task

Create a Deployment named web in the storefront namespace:

  • 8 replicas, all running.
  • Image nginx:alpine-slim.
  • Pods labelled app: web, which is also what the Deployment selects on.
  • The replicas end up split 4 and 4 between the two zones, never 5 and 3. Group them by the nodes' topology.kubernetes.io/zone label, not by node name or any other label.
  • The split is enforced by the Deployment itself, through one rule that counts the app: web Pods. If a Pod cannot be placed without breaking the balance, the scheduler must leave it unscheduled rather than put it in the fuller zone.
Eight replicas from one Deployment landing four in each zone

Eight Pods, four per zone. A Pod that would make one zone fuller waits instead of landing there.

Important

That rule is the only placement setting in the Pod template. No nodeSelector, no node or Pod affinity, no tolerations, no nodeName. Leave the node labels and the taint on cplane-01 alone.


Hint 1: Who decides where a Pod lands?

A Deployment never picks nodes. It creates Pods and hands them to the scheduler, so a rule about placement is useless anywhere except the Pod template, which is the part of the Deployment the scheduler reads.

Start by seeing what a Pod can say for itself:

kubectl explain pod.spec

Read the field list, not the descriptions yet. A handful of fields are about where a Pod runs. Your job is to work out which of them can describe a set of Pods rather than a single one.

Hint 2: Ruling out the obvious candidates

Take the placement fields one at a time and ask what each can express:

  • one pins a Pod to a node by name, no scheduler involved
  • one requires or prefers nodes carrying particular labels
  • one lets a Pod onto a node that would otherwise turn it away

Every one of those describes a Pod's relationship to a node. An even split is a statement about this Pod's relationship to the other Pods like it, which none of them can say. One field in the list is left. Read its description in full:

kubectl explain pod.spec.<the remaining field>
Hint 3: The shape of the block

Four of its fields matter here, and the task has already decided three of them:

  • the node label whose values define the groups, which the task names outright
  • which Pods get counted, which the task fixes at app: web
  • what happens when the rule cannot be met, which the task describes in words
  • how far the group sizes may drift apart, which is the only one left to reason about

Filled in, it sits in the Pod template like this:

spec:
  template:
    spec:
      topologySpreadConstraints:
      - maxSkew: <...>
        topologyKey: <...>
        whenUnsatisfiable: <...>
        labelSelector:
          matchLabels:
            app: web
      containers:
      - name: nginx
        image: nginx:alpine-slim

See: Pod Topology Spread Constraints


⚒ Test Cases