Skip to content

Labs — Phase 9, service mesh & L7

Twenty-five exercises in the order they are meant to run, for the phase most likely to breach the lab's ceiling. A mesh's control plane, its sidecars, a workload and an observability stack do not co-reside on a 5.0GB topology — so the sequence was costed before it was written, and exercise 4 carries the whole arithmetic, including four things a normal mesh install brings that this one declines and why each is not needed. Nothing about the mesh itself was softened: a real istiod, real sidecars, a real ambient data plane with ztunnel and a waypoint, and a capstone that walks one request from a netfilter rule to an endpoint IP.

Each file states one claim to test or one artifact to produce, links its topology rather than restating a footprint, and ends with a teardown that does two things: deletes what that exercise created, then says whether the topology stays or goes. This phase's teardowns are unusually specific, because its risk is accretion — a half-broken nat table, a five-second fault filter or eight sidecars left running makes the next exercise fail for a reason that has nothing to do with it.

The framing — why each module exists, what to read and the question to answer from it — stays in phases/09-service-mesh.md. These files hold only what you type and what you should see.

# Exercise Why it exists
1 Four objects in a file you typed The object model is cheaper to learn from twenty lines you wrote than from a 10,000-line dump.
2 A dump with nothing pushed into it Establishes the control: zero version_info fields, so the dynamic case has something to differ from.
3 Which object owns the failure Kills an upstream and finds the fix belongs to the cluster, not the listener — the object model as a prediction.
4 The request that does not fit The chart default is un-installable here, and the scheduler says so in two clauses. The phase's whole footprint argument lives in this file.
5 A pod the webhook rewrote The Deployment still has one container. Mutation is a property of pod admission, and that is checkable.
6 Interception reduced to netfilter Four chains, and every port in them is an istio-init arg. The module's entire internals payload.
7 The UID that breaks the loop Deletes the exemptions and measures the loop as a counter ratio rather than describing it.
8 9.C1 — a port outside the mesh The request still returns 200; the identity header is gone. What a bypass looks like when nothing alerts.
9 The same dump, now dynamic Same object types, one push away. The three counts that separate a config file from a control plane.
10 One Service, four resource types LDS→RDS→CDS→EDS, ending on a port the Service does not advertise — which proves the last hop is EndpointSlice data.
11 An ACK and a NACK One EnvoyFilter applied, one rejected, and a version that does not move. istiod accepts both; the proxy decides.
12 A scale event is one resource type Why xDS is four types and not one, measured from istiod's push counters with no telemetry stack installed.
13 9.C4 — a delay you declared The fault filter was already in every listener. The chaos engine ships inside the thing being studied.
14 9.C3 — istiod killed and the planes come apart Traffic flows, a new Service is never heard of, and no pod can be created — the third result is the honest limit of "separate planes".
15 A certificate that names a ServiceAccount No DNS name, no IP, one URI — and the projected token it was traded for.
16 STRICT and the plaintext refused The policy removes filter chains. The refusal happens before any HTTP exists to refuse.
17 The boundary is the netns Pod IP refused, 127.0.0.1 not. Where mTLS stops being a property of the Service.
18 9.C2 — a root that no longer signs Two workloads, two issuers, one trust bundle — and a recovery that is part of the artifact.
19 What a sidecar costs The phase's one deliberate walk toward the node's limit, measured from the kubelet's summary API.
20 A namespace with no sidecars Sockets in the pod's netns held by a process in another one, proved by a file descriptor. Not an overlay, not Geneve.
21 The same curve, flat The comparison the ambient claim rests on — and three caveats that keep it honest.
22 The waypoint L7 policy needs An HTTP-shaped rule with no HTTP-aware proxy in the path, then the one Envoy per namespace that fixes it.
23 The waypoint removed, and the silence The policy stays Applied and stops applying. No event, no status, no warning.
24 Linkerd, read and never installed Four questions answered from a source tree on forge, with citations — and a footprint number explicitly not claimed.
25 The capstone: one request, both halves Two netfilter rules and four jq paths for one GET. Ends the phase and releases the topology.

Order, topology and what arrives when

Exercises 1, 2, 3 and 24 need no cluster. The first three run entirely on forge with a container image and two python3 -m http.server upstreams, which is why the phase's first RAM is spent at exercise 4 and not at exercise 1. Exercise 24 is a source clone, and pair may be up and idle beside it.

One cluster runs exercises 4 through 23 and 25. pair comes up at exercise 4 and is destroyed by the capstone. Two workloads — httpbin and sleep — are created once at exercise 5 and are the only application in the phase.

The data plane changes shape twice. Sidecar mode from exercise 4, ambient from exercise 20, and back to sidecar at the capstone — which is a deliberate reinstall, because a capstone that walks per-pod interception rules needs a data plane that has some. The Gateway API CRDs arrive at exercise 22 and are the only cluster-scoped objects the phase adds.

Exceptions

  • Nothing observability-shaped is installed. Every objective's evidence is an iptables rule, a config dump, an Envoy counter read through pilot-agent request GET stats, or istiod's own :15014/metrics reached from a pod with curl. Exercise 4 costs the addons that were declined and says which exercise would have used each.
  • No Chaos Mesh, for the first time since P6. The phase's drill table makes 9.C4 mesh-native and the other three by-hand, so the tool would cost 582Mi to inject faults the mesh injects itself — and using it for 9.C4 would hide the filter being taught.
  • The workload is httpbin and sleep, not bookinfo. Three Java reviews pods demonstrate traffic splitting, which no objective in this phase asks for; httpbin echoes its request headers, which is what turns 9.C1 and the identity exercise into one-line observations.
  • istiod is pinned to the control-plane node. Not for capacity — it is the only node with room — but so that the worker stays a clean instrument for the two cost curves.
  • Exercises 7, 8, 16, 17, 18 and 23 leave the cluster in a deliberately broken state mid-file. Each ends by proving the repair, not by asserting it: a 200, a restored fingerprint, a rule back in the chain. Do not skip those last blocks.
  • Two version numbers are recorded rather than quoted. Exercise 1 records istioctl version --remote=false and derives every image tag from it; exercise 22 records the Gateway API release its Istio version expects. Ambient GA — Istio 1.24 or later — is the only hard requirement.
  • Three exercises ask you to record an outcome rather than confirm one: the loop's failure mode, the root-replacement direction, and an unenforceable L7 policy failing open or closed. All three are version-dependent, all three have a version-independent observable beside them, and writing down the outcome you expected instead of the one you got defeats the exercise.