Service discovery and routing
Follow a request from logical service name to live backend while separating DNS, endpoint publication, connection reuse, and draining.
On this page
The question for today
What you’ll understand
- Separate service identity from the set of currently eligible backends.
- Explain why changing DNS does not move an existing connection.
- Calculate per-backend load and conservative drain intervals.
A client asks for a service, but packets reach a particular process through naming, resolution, endpoint publication, routing, and connection management. Their update clocks differ. A deployment can therefore remove a backend from the desired set while some clients still use it. A dependable platform defines the contract at every step and makes backend retirement an explicit protocol rather than an abrupt deletion.
Separate a name from a backend inventory
A Kubernetes Service provides a stable abstraction over a changing set of backends. Selectors commonly connect a Service to Pods, and EndpointSlices represent endpoint information used by the networking implementation. A normal Service's DNS record typically resolves to its virtual service address, while a headless Service exposes endpoint addresses through DNS. These are different discovery contracts; a client that balances discovered endpoints must handle membership changes itself.
Start a routing design by writing what the client learns and who selects a backend. Is the client receiving a virtual address, a set of addresses, or a proxy endpoint that makes a later decision? Then list caches and failure detectors along that path. A service name that resolves successfully only proves something about naming; it does not prove the selected process is ready, authorized, or able to reach its own dependencies.
Readiness and connections have different lifetimes
A readiness signal should answer whether a backend may receive new work under the service's contract. Making it depend on every distant dependency can remove all replicas during an upstream outage. Making it ignore a required local model can route work to a process that cannot serve it. Choose the signal from the application's actual recovery behavior and test it during startup, overload, and shutdown instead of treating any successful health URL as sufficient.
DNS time-to-live influences how long a resolver may cache an answer; it does not force an already established TCP connection to migrate. Connection pools, proxies, and client retry behavior introduce their own state. During retirement, stop advertising new availability, allow propagation, and bound existing work before termination. If requests can stream indefinitely, the policy needs a maximum stream age, a handoff, or a documented interruption behavior. Lowering a DNS TTL alone does not create such a protocol.
Design draining around observable work
A conservative retirement budget can add a measured endpoint-propagation bound, a bound on admitted request duration, and a safety margin. This is valid only if the routing implementation eventually stops sending new work and the request-duration bound is real. Observe active requests and connection ages on the retiring instance. A fixed sleep can be a fallback, but it should have a reason that is testable in the deployed network.
When backends disappear, recalculate both total surviving capacity and the concentration of traffic. A fleet can remain reachable while the survivors overload, causing retries that increase demand again. Prefer explicit deadlines and bounded admission over indiscriminate retries. Track errors by selected backend and route version so a stale endpoint, a failing application, and an overloaded healthy survivor do not collapse into one opaque availability graph. The discovery plane and data plane should each have a recovery test.
Work it through
Retire a backend without overloading its neighbors
A service receives 900 requests/s across six comparable backends. Each sustains 200 requests/s at the objective. One backend is retired. Measured route propagation takes at most four seconds, admitted requests at most twelve seconds, and the team allocates a three-second margin.
Before retirement, ideal equal load is 900 / 6 = 150 requests/s per backend, or 75% of each measured 200-request/s capacity.
With five backends, the same total demand becomes 900 / 5 = 180 requests/s each, or 90% of the measured capacity. The arithmetic leaves only ten percent headroom.
The conservative retirement budget is 4 + 12 + 3 = 19 seconds after availability withdrawal, assuming no new work arrives after propagation completes.
Check observed active work before termination and monitor the five survivors. Long-lived connections or uneven balancing can invalidate both the timing and equal-load assumptions.
A nineteen-second drain budget is defensible under the stated bounds, but surviving-capacity headroom is tight.
The living notebook
Explore the model
Interactive lab · queue
Explore surviving routing capacity
Vary λ and μ in the fluid backlog max(0, 4 + (λ − μ)t), interpreting μ as aggregate usable backend capacity. The separately labeled M/M/1 readout uses Poisson and exponential assumptions. The lab does not resolve DNS, rebalance connections, or simulate endpoint propagation.
- Fluid backlog
- 4
- Net rate · λ − μ
- −1 /s
- M/M/1 mean residence
- 1 s
Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forReachable survivors still need enough capacity for the traffic redirected to them.
Pause & reason
Check your understanding
Your turn
Try it yourself
Traffic is 720 requests/s with four backends each measured at 240 requests/s. What happens to ideal utilization if one backend is withdrawn? A route update bound is three seconds and request bound eight seconds; add a four-second margin to calculate a drain budget.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Recalculate capacity using three survivors and add the three timing terms.
Compare with the solution
Four backends carry 180 requests/s each, or 75% of measured capacity. Three carry 240 each, reaching 100% with no modeled headroom. The conservative drain budget is 3 + 8 + 4 = fifteen seconds. Timing the withdrawal correctly does not solve the survivor-capacity problem; add capacity, reduce admitted load, or delay retirement until adequate headroom exists.
Keep the idea
Take it with you
- Service names, endpoint inventories, and live connections are separate state.
- Withdrawal must include propagation and bounded in-flight work.
- Recalculate survivor capacity whenever routing membership changes.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.