open atlas
↑ Back to track
Docker, containers as a system DOCK · 10 · 04

Capstone: a mini-PaaS — registry, build, run, health, and zero-downtime rollout

A mini-PaaS ties the whole track together: a registry pins immutable digests, the build ships slim distroless artifacts, run enforces resource limits, honest health gates traffic, and a rolling deploy with maxUnavailable and preStop swaps versions with zero dropped requests.

DOCK Senior ◷ 18 min
Level
FoundationsJuniorMiddleSenior

The rollback didn’t roll back. An incident had just been “fixed” by redeploying the previous version — myapp:latest from before the bad change — but the new pods came up running the broken code anyway, and the outage continued for another twenty minutes while everyone stared at a deploy that said success. The cause was a mutable tag: latest had been overwritten by the bad build, so “redeploy latest” pulled the bug. Worse, half the surviving old pods were still on the previous latest from cache, so the cluster was running two different versions under one tag and nobody could tell which pod had which code. The fix was a rule that turned a fragile workflow into a real platform: every image is referenced by its immutable digest (myapp@sha256:...), never a moving tag. This capstone assembles the whole track — registry, build, run, health, rollout — into the smallest system that has this property and the four others a production platform needs: immutability, bounded resources, honest health, and a rollout that never drops a request.

The five stages, as one pipeline

Before reading the five contracts below, notice what the hook incident actually was: not a single missing feature, but a missing composition. Each contract individually looked acceptable; together they were missing the one that makes the others meaningful.

Everything in this unit is a stage of the same machine. A minimal production platform — your mini-PaaS — is the composition of five contracts, each of which fails the others if it is wrong.

1 · Registry — immutability. The registry is the source of truth for artifacts, and its one non-negotiable rule is that deployments reference an immutable digest, not a mutable tag. myapp:latest can be overwritten; myapp@sha256:9f2c... cannot — the same digest always pulls the exact same bytes. Pin digests in the deploy manifest and a rollback genuinely returns to known-good code, two pods can never disagree about what latest means, and an image-pull is reproducible across nodes and time. Tags are convenient labels for humans; digests are the contract for machines.

2 · Build — slim and reproducible. A multi-stage build (from the slimming lesson) compiles in a toolchain stage and ships a ~25 MB distroless artifact, so the new version pulls onto cold nodes in ~2 s during the rollout instead of stalling it for 90 s. The build is pinned (base image by digest, dependencies by lockfile) so the digest is deterministic — the same source produces the same image.

3 · Run — bounded resources. Every container runs with a memory limit (from the OOM lesson) so one bad release cannot OOM the node and take down the other version mid-rollout, plus a cgroup-aware runtime target (GOMEMLIMIT / MaxRAMPercentage) so the runtime stays under the wall. Bounded resources are what make it safe to run old and new versions side by side during a rollout.

4 · Health — honest gating. A readiness probe that runs a real query (from the health lesson) gates traffic: a new pod receives no requests until it is genuinely ready, and a sick pod drains. This is the signal the rollout controller waits on — without honest readiness, “rollout complete” is a lie and traffic hits a pod that cannot serve.

Quiz

A rollback redeploys myapp:latest yet the pods still run the broken code, and surviving old pods run a different build under the same tag. What platform rule prevents this class of bug?

The rollout — composing it into zero downtime

The fifth contract is the rolling deploy, and it only works because the other four hold. A rolling update replaces pods incrementally under two knobs: maxUnavailable (how many existing pods may be down at once — set low, e.g. 0 or 25%, to preserve capacity) and maxSurge (how many extra pods may be created above the desired count, e.g. 25%, to add new-version capacity before removing old).

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 0      # never drop below desired capacity
    maxSurge: 1            # bring up one new pod before retiring an old one

The sequence ties the whole track together. The controller creates a new pod from the digest-pinned slim image (stages 1–2); it pulls in ~2 s and starts under its memory limit (stage 3); the controller waits for the new pod’s honest readiness probe to pass before sending it traffic (stage 4); only then does it terminate an old pod, which runs its preStop → SIGTERM → drain lifecycle (from the health lesson) so its in-flight requests finish; and it repeats until every pod is the new version — with maxUnavailable: 0 capacity never dips, and with honest readiness no traffic ever lands on a not-ready pod. Remove any one contract and the rollout breaks: a mutable tag makes rollback unreliable, a fat image stalls the surge, a missing memory limit lets the new version OOM the node hosting the old one, a dishonest probe routes traffic to a dead pod, and a missing preStop drops the in-flight requests of the pods being retired. That is the lesson of the whole unit: production containers are not five independent best practices — they are one pipeline where each stage is load-bearing for the next.

Quiz

In a maxUnavailable:0, maxSurge:1 rolling update, what does the controller wait on before terminating an old pod, and why does that gate matter?

Recall before you leave
  1. 01
    Why must a deployment reference an image by digest rather than a tag, and what specific failures does digest pinning prevent?
  2. 02
    Walk a zero-downtime rolling update (maxUnavailable:0, maxSurge:1) and explain how each of the unit's five contracts is load-bearing for it.
Recap

This capstone assembles the unit into the smallest system that behaves like a production platform: a mini-PaaS that is five contracts composed into one pipeline, each load-bearing for the next. The registry’s rule is immutability — reference images by digest, not by a mutable tag, so a rollback returns to exact known-good bytes, no two pods disagree about what a tag means, and pulls are reproducible across nodes and time; the opening incident, where redeploying an overwritten latest re-shipped the bug, is precisely what digest pinning kills. The build is multi-stage and slim, shipping a ~25 MB distroless artifact that pulls onto cold nodes in ~2 s so the rollout’s surge is fast rather than stalled, and pinned so the same source yields the same digest. The run is resource-bounded — a memory limit plus a cgroup-aware target like GOMEMLIMIT — so a bad release cannot OOM the node that is hosting the other version mid-rollout, which is what makes it safe to run two versions side by side. Health is honest readiness that runs a real query, gating traffic so a new pod takes no requests until it can genuinely serve and a sick pod drains; this is the signal the rollout controller waits on. And the rolling deploy ties it together: with maxUnavailable:0 capacity never dips and maxSurge brings up a ready new pod before retiring an old one, which then drains through preStop → SIGTERM so its in-flight requests finish. Pull any single thread and the whole rollout unravels — mutable tags break rollback, fat images stall the surge, missing limits crash the node, dishonest probes misroute, and no preStop drops requests. The unit’s thesis is exactly this composition: production containers are bounded resources, honest health, slim images, and a graceful lifecycle, working as one system under an orchestrator. Now when you review a Dockerfile, a Kubernetes manifest, or a deploy runbook, you can read it as a pipeline: does each stage hold the contract that the next stage depends on?

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 4 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.