This page is the "run every case" companion to Kubernetes — pods, deployments, services, ingress (concepts) . The parent gave you the four objects (Pod, Deployment, Service, Ingress) and the rolling-update inequalities. Here we push those rules through every boundary case: normal counts, zero, degenerate one-replica, overloaded surge, dead nodes, and the tricky arithmetic exam question everyone gets wrong.
Before we start, one reminder of the two inequalities the parent derived — because almost every example below is just plugging numbers into them.
Every case this topic can throw at you, and which example covers it.
Cell
Case class
What's tricky about it
Example
A
Normal rolling update
The everyday R , u , s arithmetic
Ex 1
B
Percentage knobs
maxUnavailable: 25% must round
Ex 2
C
Zero surge (maxSurge=0)
Update must shrink first
Ex 3
D
Degenerate R = 1
One replica → downtime unless you surge
Ex 4
E
Node death / self-heal
Reconciliation loop, not update
Ex 5
F
Scaling + label selector
Endpoints list grows, clients unchanged
Ex 6
G
Service-type choice (reachability)
Internal vs external, cost trap
Ex 7
H
Ingress fan-out (one LB, many Services)
L7 host/path routing arithmetic
Ex 8
I
Exam twist: worst-case simultaneous down
Combine floor + ceiling at once
Ex 9
The two "numbers" cases (A–D, I) all live inside the inequality box above. The rest (E–H) test the concepts .
Worked example Ex 1 — the everyday case
A Deployment has R = 10 replicas, u = maxUnavailable = 2 , s = maxSurge = 3 . During a rolling update, what is the minimum number of Pods that can serve traffic, and the maximum number of Pods that can exist at once?
Forecast: Guess two numbers before reading. (Floor? Ceiling?)
Capacity floor. P available ≥ R − u = 10 − 2 = 8 .
Why this step? This is the whole point of maxUnavailable: it caps how much serving capacity you're allowed to lose. Losing at most 2 out of 10 keeps 8 live.
Resource ceiling. P total ≤ R + s = 10 + 3 = 13 .
Why this step? maxSurge lets Kubernetes spin up new-version Pods before killing old ones, so briefly you have extras. 3 extra on top of 10 = 13.
Answer: at least 8 serving, at most 13 existing.
Verify: Sanity — floor 8 < R = 10 < ceiling 13 . Good: you never drop below the target and never exceed target-plus-surge. Units are "Pods" throughout; no percentages involved.
Kubernetes lets you write the knobs as percentages. The rule the exam loves: maxUnavailable rounds DOWN, maxSurge rounds UP. Why opposite directions? Because both rounding choices are safe : rounding availability down keeps more Pods up; rounding surge up is harmless (you just get more headroom).
Worked example Ex 2 — percentages that don't divide evenly
R = 10 , maxUnavailable: 25%, maxSurge: 25%. Find u , s , the floor and the ceiling.
Forecast: 25% of 10 is 2.5 — but Pods are whole. Which way does each round?
Convert maxUnavailable. 0.25 × 10 = 2.5 → round down ⇒ u = 2 .
Why this step? Availability must never be worse than declared, so we take fewer allowed-down. Rounding 2.5 down to 2 means at most 2 down (safer than 3).
Convert maxSurge. 0.25 × 10 = 2.5 → round up ⇒ s = 3 .
Why this step? Extra Pods are just temporary cost, never a safety risk, so K8s rounds up to keep churn smooth.
Floor = R − u = 10 − 2 = 8 . Ceiling = R + s = 10 + 3 = 13 .
Answer: u = 2 , s = 3 , floor 8 , ceiling 13 .
Verify: Notice this lands on the same numbers as Ex 1 — that's a good cross-check that the percentage form is just a different way to spell the same integers. % resolved to whole Pods before any inequality was used.
25% of 10 is 2.5, so 2 or 3 either way."
Feels right: rounding is rounding. Wrong: the two knobs round in opposite directions on purpose. maxUnavailable down (safety), maxSurge up (headroom). Mix them up and your exam floor/ceiling are off by one.
maxSurge = 0
R = 4 , u = 1 , s = 0 . There is a hard cluster quota: no more than 4 Pods of this app may ever exist. How does the update proceed step by step, and what is the lowest serving capacity?
Forecast: With no room for extra Pods, can K8s add a new Pod before removing an old one? What must happen first?
Ceiling pins total at 4. P total ≤ R + s = 4 + 0 = 4 . No spare slot exists.
Why this step? With s = 0 you cannot create a new Pod while all 4 old ones live — that would be 5, over the ceiling.
So K8s deletes first. It removes 1 old Pod (allowed: u = 1 ), leaving 3 serving, then creates 1 new Pod in the freed slot.
Why this step? The only way to make room under a zero-surge ceiling is to remove before adding — the mirror image of the normal surge-first flow.
Repeat 4 times. Each cycle: 4 → 3 (delete old) → 4 (add new). Lowest serving = R − u = 4 − 1 = 3 .
Answer: proceeds "delete-then-create" one at a time; minimum serving capacity = 3 .
Verify: Floor = 4 − 1 = 3 ✓ matches step 3. Ceiling = 4 , and indeed total never exceeds 4 ✓. This is the trade-off: zero surge saves resources but forces a real (small) capacity dip.
R = 1 , the "there is no zero-downtime" trap
A Deployment has R = 1 . (a) With defaults u = 1 , s = 1 , is a zero-downtime update possible? (b) With u = 0 , s = 1 ?
Forecast: With only ONE copy, is there any way to update without a gap?
(a) Compute the floor with u = 1 . R − u = 1 − 1 = 0 .
Why this step? The floor tells you the guaranteed minimum serving Pods. A floor of 0 means K8s is allowed to have zero Pods serving during the swap → possible downtime.
(a) Verdict: NOT guaranteed zero-downtime. The default may kill the single Pod before the new one is ready.
(b) Now u = 0 , s = 1 . Floor = 1 − 0 = 1 ; ceiling = 1 + 1 = 2 .
Why this step? Forcing u = 0 means the old Pod may not go down until the replacement is ready — surge creates a temporary 2nd Pod, then the old one leaves. Floor 1 ⇒ always ≥1 serving.
Answer: (a) downtime possible; (b) zero-downtime, at the cost of briefly running 2 Pods.
Verify: (a) floor = 0 , so serving can hit 0 ✓. (b) floor = 1 > 0 ✓, ceiling = 2 ✓. Lesson: for R = 1 you MUST set maxUnavailable: 0 (and rely on surge) to avoid a gap.
R = 1 is the boundary case
Every "high availability" story assumes you have a spare to fail over to. With one replica there is no spare — so the only way to overlap old and new is to temporarily make a second one (surge). Set u = 0 or accept a blink of downtime.
Worked example Ex 5 — a whole node crashes
R = 6 , spread across 3 nodes (2 Pods each). Node C dies, taking 2 Pods with it. Walk through what the Deployment does and how many Pods end up running. This is not an update — no image changed.
Forecast: Does the Service break? How many Pods after healing?
Observed drops to 4. The reconciliation loop sees desired = 6 , observed = 4 , gap = 2 .
Why this step? Recall the parent's core loop act = f ( desired − observed ) . A nonzero gap triggers action.
Scheduler places 2 new Pods on nodes A and B (wherever capacity exists), restoring 6 .
Why this step? The Deployment's job is count , not update . It doesn't care the old ones died from a node fault; it just refills to R .
Service self-corrects. The 2 dead Pods' IPs drop out of the Endpoints list; the 2 new Pods (same label app=x) join automatically.
Why this step? The Service selects by label , so replacements with the right label are picked up with no manual step — this is the whole point of label-based selection.
Answer: back to 6 Pods; Service keeps working with zero client changes.
Verify: Desired − observed = 6 − 6 = 0 after healing ✓ (loop reaches its fixed point). No inequality needed — this is steady-state reconciliation, not a bounded update. Contrast with Load Balancing : the Service is the load balancer whose backend set just shrank then regrew.
Worked example Ex 6 — Endpoints arithmetic under scale-out
Service web selects app=web. Deployment scales R : 3 → 8 . Before scaling, each of the 3 Pods received an equal share of 900 requests/sec. After scaling to 8 (traffic unchanged), what is the per-Pod load, and how many clients must reconfigure?
Forecast: Requests per Pod after, and how many client config changes?
Before: 900/3 = 300 req/s per Pod.
Why this step? A Service spreads traffic roughly evenly across its Endpoints (matching healthy Pods).
After scale-out: new Pods carry label app=web, so Endpoints grows 3 → 8 . Per-Pod load = 900/8 = 112.5 req/s.
Why this step? Same total traffic, more backends → each does less. The Service IP/DNS name never changed.
Client reconfigurations: 0 .
Why this step? Clients only ever knew the stable Service address. The whole indirection payoff: scale the backend without touching the front.
Answer: 300 → 112.5 req/s per Pod; 0 clients reconfigured.
Verify: Load conservation: 8 × 112.5 = 900 ✓ equals the original total. 3 × 300 = 900 ✓. Units req/s consistent. Ties to Microservices Architecture — this is how a single microservice absorbs a spike.
Worked example Ex 7 — internal vs external, and the money trap
You run 5 microservices. 4 talk only to each other; 1 (gateway) must be reachable from the public internet. A junior sets all 5 to type LoadBalancer. (a) How many cloud load balancers (and public IPs) does that provision? (b) What's the minimal correct design and its LB count?
Forecast: LB count for the naive vs. the correct design?
(a) Naive count. Each LoadBalancer Service = 1 cloud LB with 1 public IP. 5 services ⇒ 5 LBs.
Why this step? LoadBalancer literally asks the cloud provider for an external LB per Service — you pay for each.
(b) Correct types. The 4 internal services → ClusterIP (reachable only inside the cluster). The public path → 1 Ingress in front of a ClusterIP gateway service.
Why this step? ClusterIP is private and free of external LBs; Ingress gives ONE external entry point (backed by one LB) that routes by host/path.
(b) LB count. 1 (the Ingress Controller's LB).
Why this step? All external traffic funnels through the single Ingress; internal traffic never leaves the cluster.
Answer: (a) 5 LBs; (b) 1 LB — a 5 → 1 reduction (80% fewer external LBs).
Verify: Reduction = ( 5 − 1 ) /5 = 0.8 = 80% ✓. Reachability check: 4 internal services need no external IP → ClusterIP correct; 1 public need met by Ingress → correct. Relates to Declarative vs Imperative Programming : you declare which Service is public, K8s wires it.
Worked example Ex 8 — one entry point, many routes
An Ingress fronts these rules:
api.shop.com (any path) → api-svc
shop.com/cart → cart-svc
shop.com/pay → pay-svc
shop.com/ (everything else) → web-svc
A request arrives for https://shop.com/cart/item/42. (a) Which Service handles it? (b) How many external load balancers back this whole setup?
Forecast: Which of the 4 rules wins, and total LB count?
(a) Host match first. Host is shop.com, so the api.shop.com rule is out (different host).
Why this step? Ingress routes on host then path ; a host mismatch eliminates a rule entirely. See DNS — the host header comes from the domain the client resolved.
(a) Path match — longest prefix wins. /cart/item/42 starts with /cart, which is a longer, more specific match than /. So cart-svc.
Why this step? L7 routers pick the most specific prefix; / is the catch-all fallback, used only when nothing more specific matches.
(b) LB count. All 4 Services are ClusterIP behind ONE Ingress → 1 external LB.
Why this step? Fan-out is the reason Ingress exists: multiplex many Services over a single L7 entry point.
Answer: (a) cart-svc; (b) 1 LB for all 4 Services.
Verify: Prefix check: /cart/item/42.startswith(/cart) is true and length 5 > 1 (the / rule), so cart-svc wins over the catch-all ✓. Compare Ex 7: 4 LoadBalancer Services would be 4 LBs; here 4 Services share 1 → same 4:1 style saving.
Worked example Ex 9 — the "gotcha" combining both bounds mid-update
R = 20 , u = 3 , s = 5 . During a rolling update, at the single worst instant: (a) what is the fewest Pods that can be serving , and (b) the most Pods that can exist , and (c) at that most-Pods instant, how many of them are "extra" (beyond desired)?
Forecast: Three numbers. The trap is thinking the min-serving and max-total happen together — check whether they can.
(a) Fewest serving. R − u = 20 − 3 = 17 .
Why this step? maxUnavailable = 3 caps serving loss; serving never drops below 17.
(b) Most existing. R + s = 20 + 5 = 25 .
Why this step? maxSurge = 5 caps total Pods at 25 (20 desired + 5 surge).
(c) Extra at the ceiling. ( R + s ) − R = 25 − 20 = 5 = s .
Why this step? By definition the "extra" over desired is exactly maxSurge when you hit the ceiling.
Answer: (a) 17 serving, (b) 25 existing, (c) 5 extra.
Verify: Floor 17 < R = 20 < ceiling 25 ✓. Extra = s = 5 ✓. Sanity on the trap: even at the 25-Pod moment, at least 17 are ready — so K8s can push aggressively (surge + unavailable) yet still honor both bounds simultaneously.
Recall Every knob and boundary in one place
Floor of serving Pods during update ::: R − maxUnavailable
Ceiling of total Pods during update ::: R + maxSurge
maxUnavailable: 25\% of 10 replicas rounds to ::: 2 (rounds DOWN)
maxSurge: 25\% of 10 replicas rounds to ::: 3 (rounds UP)
To get zero-downtime with R = 1 you must set ::: maxUnavailable: 0 (rely on surge)
With maxSurge: 0, the update must ::: delete an old Pod first, then create the new one
After a node dies, the count is restored by ::: the reconciliation loop (Deployment refills to R )
Scaling 3 → 8 Pods requires this many client changes ::: zero (Service address is stable)
5 microservices, 1 public: correct external LB count ::: 1 (Ingress in front of ClusterIP services)
Ingress path matching rule ::: most specific (longest) prefix wins; / is the fallback