4.5.12 · D2Software Engineering

Visual walkthrough — Kubernetes — pods, deployments, services, ingress (concepts)

2,415 words11 min readBack to topic

We will not assume you know what a "controller", a "state", or even a "loop" means here. We earn every word.


Step 1 — What is a "state"?

WHAT. Before we can talk about a gap between two states, we need to know what a single state is. A state is just a snapshot: a list of facts about the world right now. For us the fact we care about is one number: how many copies of my app are running?

WHY. Kubernetes cannot "want" anything abstract. It can only compare numbers and lists. So we reduce the whole messy idea of "my app is healthy" down to something countable — a count of running Pods. Everything visual on this page rides on that one countable quantity.

PICTURE. On the left, a jar labelled desired holds the number you asked for. On the right, a jar labelled observed holds the number that is actually running right now. These two jars are the entire drama.

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

In symbols we will write for the desired count and for the observed count. That's all the notation Step 1 introduces.


Step 2 — What is the "gap"?

WHAT. The gap is a single subtraction: Every symbol: is the gap (a plain number, can be positive, negative, or zero), is the desired count from Step 1, is the observed count from Step 1.

WHY subtraction and not, say, a ratio? Because subtraction tells you both how far off and in which direction in one shot, using a sign:

  • → you have too few Pods (want more than you have).
  • → you have too many Pods (want fewer than you have).
  • perfect, nothing to do.

A ratio would break when (division by zero) and hides the direction. Subtraction is the simplest tool that carries magnitude and sign together — exactly the two things a controller must know.

PICTURE. A number line. Mark and on it; the arrow between them is . When the arrow points right ( to the right of ) we need to create; when it points left we need to delete.

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

Step 3 — What is the action ?

WHAT. is the function that turns a gap into a move. The parent wrote . Now we say what does: it takes the signed number and outputs "create Pods" or "delete Pods".

WHY a function at all? Because we want the same rule to work no matter the numbers. Whether the gap is or , reacts the same way — it just scales. That reuse is the whole point of writing it as instead of a special case for every situation. Here means the size of the gap ignoring its sign (so ): the sign told us the direction, the size tells us how much.

PICTURE. A little machine: gap arrow goes in on the left, and out the right comes an instruction card — green "spawn 2 Pods" when the gap was , red "kill 2 Pods" when the gap was .

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

Step 4 — Closing the loop (why it repeats forever)

WHAT. One pass — measure , compute , act — is not enough. The world keeps changing after you act. So we loop: measure again, recompute the gap, act again, forever. This never-ending cycle is the reconciliation loop.

WHY loop instead of run once? Because has a mind of its own — a node can die a second after you reached . A one-shot fix would leave you broken until a human noticed. A loop means the system is self-correcting: any disturbance just reopens a gap the loop is already watching for.

PICTURE. A ring of four arrows: observe → compute → act → (world drifts) → observe.... Notice the loop has no exit. It idles quietly when and springs awake the instant .

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

Step 5 — Case: a Pod dies (the "heal" case)

WHAT. Start balanced: , , so . A node crashes; one Pod vanishes. Now , so .

WHY it self-heals. The gap became positive, so outputs "create Pod." The loop spawns a fresh Pod, climbs back to , and returns to . Crucially the reborn Pod is a brand-new Pod with a new IP — the loop only cares about the count, never the identity.

PICTURE. Timeline of the count over time: flat at 3, sudden drop to 2 (red), the loop notices , a new green Pod appears, back to 3.

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

Step 6 — Case: too many Pods (the "scale-down" case)

WHAT. Now the opposite. You lower the target: while . The gap is .

WHY it works with the same . No new logic. The gap is negative, so outputs "delete Pods." falls to , returns to . The exact same subtraction-and-sign machine handles growth and shrinkage — that's the payoff of building around a signed gap in Step 2.

PICTURE. The two jars again: desired jar drops to 1, the gap arrow now points left (delete), two red Pods get removed until the jars match.

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

Step 7 — Degenerate case: forever, and the rolling update

WHAT (idle). When , , and does nothing. The loop still runs — measuring, finding zero, resting. This is the healthy steady state most of the time.

WHAT (rolling update). A rolling update is the loop cleverly never letting the useful gap get dangerous. With = replicas, two knobs bound how far may wander during an upgrade: Term by term: is your steady replica count; is how far below the live count is allowed to dip; is how many are actually serving traffic; is how many extra Pods may exist mid-swap; is old-version plus new-version Pods combined.

WHY these bounds = zero downtime. As long as the floor stays , there is always at least one live Pod to serve requests while old ones retire and new ones warm up. The loop only removes an old Pod once a new one is confirmed healthy — it keeps pinned above the floor.

PICTURE. A band chart over time: green area = new-version Pods rising, blue area = old-version Pods falling, a dashed red floor line the total-available never crosses, a dashed yellow ceiling the total never exceeds.

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

This is the Deployment layer (see parent) enforcing a sequence of tiny, safe gaps rather than one big scary one — the same reconciliation loop, applied to versions instead of just counts. It's the mechanism behind safe CI-CD Pipelines deploys.


Step 8 — Where the loop lives across all four objects

WHAT. The same loop runs at several layers, each closing a different gap:

Controller (desired) (observed) Action
Deployment / ReplicaSet replica count live Pods create / delete Pods
Service (Endpoints) Pods matching label Pods currently in backend list add / drop backend IPs
Ingress Controller routing rules live proxy config rewrite proxy config

WHY it matters. Once you see the loop once, every Kubernetes object is just "another gap being closed." The Service's job in Microservices Architecture is a reconciliation loop over the label→Pod match: as Pods appear/vanish, it keeps its backend list equal to "all Pods with this label."

PICTURE. Three stacked identical loops, each fed by a different pair of jars, all the same shape — driving home that it is one idea reused.

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

The one-picture summary

Figure — Kubernetes — pods, deployments, services, ingress (concepts)

This single diagram holds the whole page: two jars ( and ) → subtraction giving signed gap → the machine acting only on reality → the ring that feeds the result back to be re-measured, forever.

Recall Feynman retelling — say it to a 12-year-old

Imagine a super-patient robot with two jars. The left jar holds the number you asked for — say "3 running copies of my app." The right jar holds the number actually alive right now. The robot does one boring thing over and over: it subtracts (right from left) to get a gap number. If the gap is a plus, it's short, so it makes new copies. If it's a minus, there are too many, so it kills some. If it's zero, it relaxes — but keeps watching. A copy crashes? The gap jumps to plus one; the robot makes a fresh copy (with a brand-new address, which is why you must talk to the stable Service and never memorise a copy's address). You want fewer? Lower the left jar; the gap goes minus; it removes some. Upgrading? The robot swaps copies a few at a time, never letting the alive-count drop below a safety floor, so users never notice. And this same jar-and-subtract robot runs at every level — one for keeping the right number of copies, one for keeping the list of live backends fresh, one for keeping the router up to date. Learn the jars-and-gap trick once, and you understand all of Kubernetes.


Recall

Recall Quick self-test

What are and ? ::: = desired count (you set it), = observed count (Kubernetes measures it). What does the sign of decide? ::: Positive → create Pods; negative → delete Pods; zero → do nothing. Why must the loop repeat instead of running once? ::: Reality () keeps drifting (crashes, node loss); a single pass can't stay correct, so the loop self-corrects continuously. Why does a healed Pod get a new IP? ::: The loop only enforces the count, not identity — it spawns a brand-new Pod, so you must front Pods with a stable Service. With , maxUnavailable=1, what is the floor of available Pods during an update? ::: . Scaling up and scaling down use... ::: the same loop; only the sign of differs.