Load Balancing and Health
A service with one server is a direction. A service with several is a small promise: if these are interchangeable, spread the work, and stop sending visitors to the one that fell over. Both halves of that promise were exercised in the lab (transcript t05) with the fern service running on two ports of the same machine — the geography was trivial, the mechanism identical to servers on different continents.
Several servers, one service
services:
fern:
loadBalancer:
servers:
- url: "http://127.0.0.1:9001"
- url: "http://127.0.0.1:9003"
Nothing else changes — not the router,
not the name, not the visitor's
experience, except that four identical
requests answered fern, fern-two, fern,
fern-two: the default strategy is
round robin, each healthy server in
turn. The backends must genuinely be
interchangeable for this to be safe. Two
copies of a stateless service are; two
copies of something that keeps sessions,
uploads or a database in local files are
two different services wearing one name,
and no proxy setting fixes that —
it is the commonest load-balancing
mistake and it is an architecture
mistake, not a Traefik mistake.
Health checks: the active kind
Traefik notices a dead server on its own eventually — a refused connection fails a real request, at a real visitor's expense. An active health check has Traefik ask, on a schedule, so the finding happens before a visitor does:
healthCheck:
path: /healthz
interval: 5s
timeout: 3s
The backend must answer that path with a
success status; the lab's backends answer
/healthz with 200 (and the
transcript shows the whole play): a server was killed, one interval
passed, and every subsequent request went
to the survivor. The server was started
again, and within an interval it was back
in rotation. No configuration changed at
any point — the list of servers is an
intention; health is a measurement.
Choose the path deliberately. Checking
/ proves the server process answers;
checking a purpose-built /healthz can
also prove its database and disk are
reachable, at the risk of marking every
server down for a shared database's bad
day. Intervals are a trade: the lab's 5
seconds is eager; production checks every
10–30 seconds are typical, and the right
number depends on how much traffic a
dead server may eat before eviction —
stated as a trade-off because it is one,
not a default to copy.
Sticky sessions, briefly
If a service truly cannot share state,
sticky: true on the load balancer pins
each visitor to one server with a cookie.
Treat it as a splint, not a cure: the
pinned server dying still ends those
sessions, and the load stops balancing
evenly as regulars accumulate. The
longer answer is shared state in the
service; the honest short one is that
Traefik offers the splint knowingly.
What health checks do not do
They do not restart anything, alert
anybody, or explain anything. A service
can be healthily answering /healthz
while returning errors for every real
page, if the check path is shallower
than the failure. And when all servers
fail the check, the service answers 503
— Traefik saying "I know this site, and
it is currently not there", which is a
different sentence from a routing 404 and
worth being able to hear (chapter 15).