Load balancers, proxies & CDNs
One server can only do so much, and it will break one day. Real sites run several copies of their app and put a load balancer in front: it spreads the visitors out and quietly stops sending them to a copy that's broken. Further out, a CDN keeps copies of the site's files all over the world, close to the visitors. This lesson puts both in front of the club's website.
You will learn
- What a load balancer does, and how it chooses a server
- Health checks: why one broken server doesn't take the site down
- Layer 4 vs layer 7, reverse vs forward proxies, and
X-Forwarded-For - Setting up HAProxy on Rocky and Ubuntu
- How a CDN caches, and how to read
HIT,MISSandAge
Spreading the load
┌─▶ web1 (127.0.0.1:8081)
visitors ─▶ HAProxy :80 ─▶ web2 (127.0.0.1:8082)
└─▶ web3 (127.0.0.1:8083)
Visitors only ever talk to the load balancer. It picks a server for each request (or connection), using one of a few algorithms:
| Algorithm | How it picks | Good for |
|---|---|---|
roundrobin | Each server in turn: 1, 2, 3, 1, 2, 3… | Most web apps (the default) |
leastconn | The server with the fewest open connections | Long requests, like downloads or websockets |
source | The same visitor always goes to the same server (a hash of their IP) | Apps that keep a user's session in one server's memory ("sticky sessions") |
Health checks
A load balancer keeps knocking on every server (every 2 seconds by default in HAProxy). When a server stops answering, it's marked DOWN and gets no more visitors until it's healthy again. That's how you can restart or update one server at a time without anyone noticing. Forget the check keyword and HAProxy never notices a dead server: some visitors get errors.
Layer 4 vs layer 7
- Layer 4 (TCP) balancers just pass connections through. They're fast and work for anything (databases, mail…), but can't look inside.
- Layer 7 (HTTP) balancers read each request: they can send
/api/to one group of servers and/images/to another, add headers, and handle HTTPS themselves (TLS termination: the certificate lives on the balancer).
Because the app now only sees connections from the balancer, it loses the visitor's real address. Layer 7 balancers add it to a header, X-Forwarded-For (HAProxy: option forwardfor), so logs and rate limits still work.
A reverse proxy sits in front of servers and acts for them, like HAProxy here or Apache in lesson 6. A forward proxy sits in front of users and acts for them, like a school's web filter. Same idea, opposite side.
HAProxy on each family
HAProxy's config is the same everywhere. Installing and starting it is where the families differ:
| Task | Rocky / RHEL | Ubuntu / Debian |
|---|---|---|
| Install | sudo dnf install haproxy (2.4) | sudo apt install haproxy (2.8) |
| After installing | not running; the sample config listens on port 5000 | already running, with a config that does nothing yet |
| Config file | /etc/haproxy/haproxy.cfg same on both | |
| Check the config | sudo haproxy -c -f /etc/haproxy/haproxy.cfg same on both | |
| Logs | journalctl -u haproxy | journalctl -u haproxy and /var/log/haproxy.log |
| A real-life gotcha | SELinux may stop HAProxy reaching servers on unusual ports: sudo setsebool -P haproxy_connect_any 1 | (AppArmor doesn't confine HAProxy by default) |
A minimal config has a frontend (where visitors arrive) and a backend (the servers):
frontend club
bind *:80
default_backend club_apps
backend club_apps
balance roundrobin
server web1 127.0.0.1:8081 check
server web2 127.0.0.1:8082 check
CDNs: copies close to the visitors
A CDN (content delivery network, like Cloudflare, Fastly or CloudFront) is a huge network of caching reverse proxies in cities all over the world. DNS sends each visitor to a nearby edge server. If the edge has a fresh copy of the file, it answers straight away (HIT). If not, it fetches it from your server, the origin, keeps a copy, and answers (MISS).
Cache-Control: public, max-age=86400(sent by your origin) says "anyone may keep this for a day".Age: 120says the cached copy is two minutes old.- Changed a file? Either purge it from the CDN, or give it a new name (
logo.v2.png,?v=2). That second trick is called cache busting. - Pages that are different for every user (anything with a login) must not be cached:
Cache-Control: privateorno-store.
Try it: put a load balancer in front 🎛️
The club's web app now runs three copies on this server (web1, web2, web3, ports 8081–8083), but nothing spreads the visitors over them. A colleague left a starter config in ~/club-lb.cfg. Set up HAProxy, prove it balances, then break a server on purpose.
Quick check
1. You stop one of three web servers behind HAProxy. Visitors keep getting the site with no errors. Why?
✓ That's what check on each server line is for.
2. Behind a load balancer, your app's logs show every visitor as 127.0.0.1. What's the fix?
✓ The app sees the balancer's connection; the real visitor address travels in the header.
3. You changed logo.png on your server, but visitors still see the old one. The CDN says x-cache: HIT, age: 3000. Why?
✓ HIT means the origin wasn't even asked. Caches are a speed-up and a source of "why is it still the old one?".