Skip to content

Why Orchestration

The overview promised that orchestration earns its keep the moment you have many containers. This page makes that concrete. We are going to imagine running a real service with nothing but Docker and a few machines, hit every wall, and watch the hand-written script grow until it is — quietly, by accident — a bad reimplementation of Kubernetes. The point is not that scripting is wrong; it’s that the same five problems come up every time, and an orchestrator solves them once, correctly, so you don’t.

The recurring thread frames the whole page: what manual, error-prone step does orchestration remove — and how does it make production safer?

Say you have a web app, three machines, and you want it to be reliable. Here is what goes wrong.

1. Scheduling — which machine runs this?

Section titled “1. Scheduling — which machine runs this?”

You have to place containers on machines. Which box has spare CPU and memory right now? You could hard-code it — “web on box 1, worker on box 2” — but the moment a box fills up or dies, your map is wrong, and you’re SSHing around eyeballing free -m and docker ps to decide where things fit. This is a bin-packing problem, and it changes every minute.

Containers crash. Processes leak memory and get OOM-killed (see cgroups). Whole machines reboot. Something must notice and restart the work — ideally somewhere healthy. docker run --restart=always handles a process crash on a surviving box, but it does nothing when the box itself dies. So you write a health-checker. Now you maintain a health-checker.

Black Friday hits and three replicas aren’t enough; you need twelve, then back to three by midnight. By hand that means starting containers, finding room for them, and — crucially — telling the load balancer they exist (and that the old ones are gone). Every step is a chance to fat-finger production.

You can’t just stop v1 and start v2; users would see errors in the gap. You want to bring up v2 gradually, watch it stay healthy, shift traffic over, and keep v1 ready to snap back if v2 misbehaves. That is a careful, stateful dance — exactly the kind of thing humans do inconsistently under pressure.

5. Networking — the addresses keep changing

Section titled “5. Networking — the addresses keep changing”

Every time a container restarts or moves, it gets a new IP. But other services were talking to the old IP. So you need a stable name that always points at the current healthy set — service discovery, the problem from Part 2, now churning constantly because containers are ephemeral by design.

The hand-rolled script that grows into a bad Kubernetes
┌──────────────────────────────────────────────────────────┐
│ place_container() ← scheduling (which box has room?) │
│ watch_and_restart() ← healing (crashed? node dead?) │
│ scale_to(n) ← scaling (start/stop + tell LB) │
│ rolling_update() ← rollouts (v2 up, drain v1, undo) │
│ update_dns() ← networking (new IPs every restart) │
└──────────────────────────────────────────────────────────┘
Each function is fragile, untested, and yours to keep alive.

Kubernetes turns all five into one move: you declare desired state, and built-in controllers make reality match it and keep it matching. You don’t write the loop; you state the goal.

The wallWhat you’d script by handWhat you declare to Kubernetes
SchedulingPick a machine, hope it fits”Run this Pod”; the scheduler places it
HealingA health-checker daemon”Keep 4 replicas”; the controller restarts/reschedules
ScalingStart/stop + update the LBreplicas: 12 (or a target metric)
RolloutsA careful update/rollback scriptA Deployment strategy; one command to roll back
NetworkingRewrite DNS on every restartA Service: one stable name, members updated for you

Here is the entire “keep four replicas healthy forever” problem, declared:

apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 4 # desired state: I want four
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: web
image: myapp:1.4.0
Terminal window
kubectl apply -f web.yaml
# Kill a pod, kill a node — the count returns to 4 on its own.
kubectl scale deployment/web --replicas=12 # scaling is one line
kubectl rollout undo deployment/web # rollback is one line

Notice what’s gone: no placement decisions, no restart daemon, no LB edits, no DNS rewrites. The manual, error-prone steps from the table above are absorbed by the platform. That is the safety win — not that Kubernetes never fails, but that the routine, repetitive, 3am work is now done the same correct way every time, by software that doesn’t get tired or distracted.

How does it actually do this? Through a control plane and a relentless loop — the subjects of the next two pages, Kubernetes Architecture and The Reconciliation Loop.

Step back from the five walls and ask the questions that decide whether a team adopts an orchestrator at all:

  • Why does it exist? Because running many containers on many machines forces five problems — scheduling, healing, scaling, rollouts, networking — and solving each by hand grows into an untested, one-person reimplementation of Kubernetes that breaks at 3am.
  • What problem does it solve? It replaces five fragile hand-written scripts with one idea — you declare desired state (replicas: 4, this image, this Service) and built-in controllers make reality match and keep it matching.
  • What are the trade-offs? You trade bespoke scripts you fully understand for a large platform you must learn and operate; the routine 3am toil is absorbed, but you take on cluster complexity, etcd to protect, and a control plane to keep healthy.
  • When should I avoid it? When you have few containers on few machines — a single box with docker run --restart=always already handles a process crash, and the five walls never get tall enough to justify an orchestrator.
  • What breaks if I remove it? The five problems return as manual work: SSHing around to place containers, a hand-rolled health-checker, fat-fingered scale-and-tell-the-LB steps, an inconsistent rollout dance, and DNS rewritten on every restart — every cluster reinventing the same load-bearing code, badly.
  1. Name the five problems that appear once you run many containers across many machines.
  2. docker run --restart=always handles one kind of failure but not another. Which does it fix, and which does it miss?
  3. Why does the “rewrite DNS on every restart” problem get worse with containers than it was with long-lived VMs?
  4. Kubernetes replaces five hand-written scripts with one idea. What is that idea, in your own words?
  5. Using the book’s thread: pick one of the five walls and explain how moving it from a script to a declaration makes production safer, not just easier.
Show answers
  1. Scheduling (which machine runs this?), healing (notice and restart failed work), scaling (add/remove replicas with load), rollouts (ship a new version without downtime), and networking (a stable name despite churning IPs).
  2. It fixes a process crash on a surviving box — Docker restarts the container in place. It misses a whole-machine failure: if the box itself dies, there’s nothing left to do the restart, so the work just vanishes.
  3. Because containers are ephemeral by design — they restart and move constantly, getting a new IP each time — whereas long-lived VMs kept the same address for months. The churn that used to be rare is now continuous, so hand-rewriting DNS becomes a never-ending, error-prone job.
  4. Declare desired state: instead of writing the loop yourself, you state the goal (replicas: 4, this image, this Service) and built-in controllers make reality match it and keep it matching.
  5. For example, healing: a hand-written health-checker is untested, undocumented code only its author understands, and it breaks at 3am on a holiday. Declaring replicas: 4 hands that responsibility to a battle-tested controller that does the same correct thing every time — so recovery no longer depends on one person’s fragile script running at the worst possible moment.