Laptop to Production

Load balancers

One address in front of many servers.

Time
50 minutes
Level
Beginner

Part 1: The problem

QuickCart, the first busy weekend

The QuickCart app talked to its API server using the server’s public IP address. One Friday the server was stopped and started to change its size. It came back with a new IP. Every copy of the app in the world was now calling an address that went nowhere, until a new app release went out.

The next weekend was busy. One server couldn’t keep up, so Priya launched a second. But the app only knew one address, so the second server sat idle while the first one struggled.

QuickCart needs one address that never changes, in front of however many servers it runs, and something that stops sending customers to a server that has broken.

Part 2: The idea

A load balancer is one fixed address in front of many servers. Customers only ever talk to it. It passes each request on to one of the servers behind it, and it keeps checking which of those servers are healthy.

Servers can come and go, change size and change address. The address customers use never changes.

The pieces

AWS’s load balancer for websites and APIs is the Application Load Balancer, or ALB. It’s made of a few parts that you configure separately:

PieceWhat it isQuickCart’s
Load balancerThe front door. It has a DNS name and sits in a public subnet in each zonequickcart-alb, in two zones
ListenerA port and protocol it listens on, and what to do with requests that arriveHTTP on 80, HTTPS on 443
RuleA condition on a listener, like “the path starts with /orders”, and which target group gets those requestsEverything goes to one target group, for now
Target groupA list of servers, plus the health check that decides which of them get trafficquickcart-web-tg
TargetOne server in the groupEach API server

A request’s path: customer → listener → rule → target group → one healthy target.

Three kinds of load balancer

TypeUnderstandsUse it for
Application (ALB)HTTP: paths, hostnames, headersWebsites and APIs. QuickCart’s choice
Network (NLB)Only TCP and UDP connections, not what’s in themVery high traffic, non-HTTP protocols, fixed IP addresses
GatewayNetwork packets, sent through a security appliancePutting firewalls in the path. You’ll rarely need it

Health checks: who gets traffic

Every few seconds, the load balancer sends each target a small request, like GET /health. If the answer is right, the check passes. If it’s wrong, slow or missing, it fails.

SettingWhat it meansDefaultQuickCart
PathWhich URL to ask//health
IntervalHow often to ask30 seconds10 seconds
TimeoutHow long to wait for an answer. Must be less than the interval5 seconds5 seconds
Unhealthy thresholdFailures in a row before the target gets no traffic22
Healthy thresholdPasses in a row before it gets traffic again52
Success codesWhich HTTP status counts as a pass200200

How long before a broken server stops getting traffic? Interval × unhealthy threshold. For QuickCart that’s 10 × 2 = about 20 seconds. Requests that reach the broken server in that window fail. Checking every second would catch it sooner, but a single slow answer would then pull a healthy server out.

Key point: Health checks decide who gets traffic. Make them answer “is this server broken?” — not “is everything behind it working?”

Shallow or deep?

CheckWhat /health doesWhen the database fails
ShallowAnswers 200 if the app is running. Touches nothing elseEvery server stays in. Pages that need the database show an error. Degraded, not down
DeepAlso queries the database, the cache, other servicesEvery server fails at once. They’re all taken out — and with auto scaling, all replaced, and the replacements fail too. Down

QuickCart tested this. They removed the security group rule that let the servers reach the database. /health kept answering 200, and / answered 500. The site was degraded, not down: the servers stayed in, showed a clear error, and recovered the moment the rule was put back. An alarm on the error rate told a person what was wrong. A deep check would have failed all the servers together and turned a database problem into a total outage.

So database problems are a job for alarms, which tell a person. Health checks are for one broken server, which a machine can fix by replacing it.

Taking a server out gently: deregistration delay

When a target is removed on purpose — during a release, or when auto scaling shrinks the group — some requests to it may still be in progress. The deregistration delay stops new requests going to the target, but waits for those in progress to finish, up to a limit.

The default is 300 seconds. That’s safe, but it makes every release wait five minutes per batch. QuickCart’s API answers in well under a second, so 30 seconds is plenty. This is also called connection draining.

Status codes that come from the load balancer

When something goes wrong, the code tells you where to look:

CodeMeansUsually
502 Bad GatewayA target was chosen, but the connection failed or the answer was brokenThe app crashed or closed the connection
503 Service UnavailableThere was no target to send it toThe target group is empty
504 Gateway TimeoutThe target took too long to answerA slow query, or a security group dropping the traffic

HTTPS, and sending HTTP to it

The ALB can do the HTTPS work itself, so the servers behind it don’t have to. You add a listener on port 443, attach a certificate from AWS Certificate Manager (free for use with a load balancer), and forward to the target group.

Then the listener on port 80 changes from forward to redirect: send the browser to the same address with https://, with status 301, which means “moved for good”. Nobody who types the plain address gets an error, and nothing travels unencrypted. Domain names and HTTPS walks through getting the certificate.

Only the load balancer may talk to the servers

The servers shouldn’t accept traffic from the internet directly. Their security group allows port 80 only from the load balancer’s security group, not from an IP range. Security groups explains this chain.

Security groupAllows in
Load balancer’s80 and 443 from anywhere
Servers’80 from the load balancer’s security group only

An ALB always spreads requests across targets in every zone it’s in, so if one zone loses servers, the others take all the traffic. By default it takes turns, called round robin. The least outstanding requests setting instead sends each request to the target with the fewest in progress, which helps when some requests are much slower than others.

Part 3: See it

Three servers sit behind QuickCart’s load balancer, in an auto scaling group that replaces unhealthy ones. Send some requests and watch them spread. Then crash the app on one server and send again. Finally, break the database, and compare a shallow health check with a deep one.

Health check
Crash the app on
A customer's phone sends requests to a load balancer. The load balancer spreads them across three servers in a target group, two in zone us-east-1a and one in us-east-1b. All three servers use one database.CustomersGET /Load balancerlistener: HTTP 80checks GET /healthTarget groupServer Aus-east-1ahealthy: gets trafficServer Bus-east-1bhealthy: gets trafficServer Cus-east-1ahealthy: gets trafficDatabaseworking

200 OK: 0500 error: 0502 bad gateway: 0

ReadyAll three servers pass their health check. Choose Send 6 requests to watch the load balancer spread them.

What to notice: with the database broken and a shallow check, every request failed — but every server stayed in and answered. Switch to a deep check, and the same problem took out every server at once. Nothing about the servers changed. Only the question the health check asked.

Part 4: Do it

You’ll put two servers in two zones behind an Application Load Balancer, watch it take turns between them, then break one and watch the health check take it out. About 40 minutes.

You need the quickcart-ec2-ssm role from Servers on EC2. If you deleted it, create it again as in that lesson’s first step. Check the region selector says N. Virginia (us-east-1).

  1. Create the load balancer's security group

    In the AWS console: EC2Security GroupsCreate security group

    FieldEnter
    Security group namequickcart-alb-sg
    DescriptionQuickCart load balancer - HTTP from anywhere
    VPCThe default VPC

    Under Inbound rules, choose Add rule:

    TypeSource
    HTTPAnywhere-IPv4 (0.0.0.0/0)

    Leave the outbound rule as it is, and choose Create security group.

    This lab stays on plain HTTP, because a trusted HTTPS certificate needs a domain name. Domain names and HTTPS adds the 443 listener and the redirect.

  2. Create the servers' security group

    In the AWS console: EC2Security GroupsCreate security group

    FieldEnter
    Security group namequickcart-app-sg
    DescriptionQuickCart servers - HTTP from the load balancer only
    VPCThe default VPC

    Under Inbound rules, choose Add rule:

    TypeSource
    HTTPCustom, then start typing quickcart-alb-sg and pick it

    Leave the outbound rule, and choose Create security group.

    You should see

    One inbound rule on port 80 whose source is a security group ID starting sg-, not an IP range. Only things in quickcart-alb-sg can reach these servers.

  3. Launch two servers, one in each zone

    In the AWS console: EC2InstancesLaunch instances

    FieldEnter
    Namequickcart-web-a
    Application and OS ImagesAmazon Linux 2023 AMI, 64-bit (x86)
    Instance typet3.micro
    Key pair (login)Proceed without a key pair

    Under Network settings, choose Edit:

    FieldEnter
    VPCThe default VPC
    SubnetThe default subnet in us-east-1a
    Auto-assign public IPEnable — so the server can reach Systems Manager and download nginx
    Firewall (security groups)Select existing security group, then quickcart-app-sg

    Under Advanced details:

    FieldEnter
    IAM instance profilequickcart-ec2-ssm
    Metadata versionV2 only (token required)
    User dataThe script below
    user-data.sh
    #!/bin/bash
    dnf install -y nginx
    hostname > /usr/share/nginx/html/index.html
    echo ok > /usr/share/nginx/html/health
    systemctl enable --now nginx
    LineWhat it does
    #!/bin/bashRun this as a bash script
    dnf install -y nginxInstall the nginx web server, answering yes automatically
    hostname > /usr/share/nginx/html/index.htmlMake the home page the server’s own name, so you can see which server answered
    echo ok > /usr/share/nginx/html/healthCreate a page at /health that just says ok. This is the shallow health check: if nginx can answer, it passes
    systemctl enable --now nginxStart nginx now, and on every boot

    Choose Launch instance. Then launch a second one the same way, with only two differences:

    FieldEnter
    Namequickcart-web-b
    SubnetThe default subnet in us-east-1b
    You should see

    Two instances, quickcart-web-a in us-east-1a and quickcart-web-b in us-east-1b, both Running.

  4. Create the target group and its health check

    In the AWS console: EC2Target groupsCreate target group

    FieldEnter
    Choose a target typeInstances
    Target group namequickcart-web-tg
    Protocol : PortHTTP : 80
    IP address typeIPv4
    VPCThe default VPC
    Protocol versionHTTP1
    Health check protocolHTTP
    Health check path/health

    Open Advanced health check settings:

    FieldEnter
    Health check portTraffic port
    Healthy threshold2
    Unhealthy threshold2
    Timeout5 seconds
    Interval10 seconds
    Success codes200

    Choose Next. Tick both quickcart-web-a and quickcart-web-b, check the port says 80, and choose Include as pending below. Then choose Create target group.

    Now shorten the deregistration delay. Open quickcart-web-tg, go to the Attributes tab, and choose Edit:

    FieldEnter
    Deregistration delay30 seconds

    Choose Save changes.

    You should see

    On the Targets tab, both instances with health status Unused, and a note that the target group isn’t used by any load balancer yet. Health checks only start once a load balancer uses the group.

  5. Create the load balancer

    In the AWS console: EC2Load balancersCreate load balancer

    Under Application Load Balancer, choose Create.

    FieldEnter
    Load balancer namequickcart-alb
    SchemeInternet-facing
    Load balancer IP address typeIPv4
    VPCThe default VPC
    Availability Zones and subnetsTick us-east-1a and us-east-1b, each with its default subnet
    Security groupsquickcart-alb-sg. Remove the default group

    Under Listeners and routing:

    ProtocolPortDefault action
    HTTP80Forward to quickcart-web-tg

    Choose Create load balancer.

    You should see

    quickcart-alb with state Provisioning. After a few minutes it becomes Active. On its Details tab is a DNS name ending in elb.amazonaws.com. Copy it.

  6. Check both targets are healthy

    In the AWS console: EC2Target groupsquickcart-web-tgTargets

    You should see

    Both targets go from Initial to Healthy within about half a minute.

  7. Watch it take turns

    In a terminal on your computer, ask the load balancer for its home page. Replace ALB_DNS_NAME with the DNS name you copied:

    Terminal
    curl --connect-timeout 10 http://ALB_DNS_NAME/
    PartWhat it means
    curlmake a web request and print the answer
    --connect-timeout 10give up after 10 seconds if nothing answers, instead of waiting over a minute
    http://ALB_DNS_NAME/the load balancer’s address. Paste yours in place of ALB_DNS_NAME
    You should see
    ip-172-31-20-15.ec2.internal

    That’s the name of the server that answered. Run the same command four or five more times.

    You should see

    The answer switches between two names: one for each server. Your names will differ, but each is the server’s private address written with dashes. Match them to your instances in the console’s Private IPv4 addresses column.

  8. Break one server and watch it drop out

    In the AWS console: EC2Instancesquickcart-web-aConnectSession Manager

    Choose Connect, and in the terminal stop the web server:

    Terminal
    sudo systemctl stop nginx
    PartWhat it means
    sudorun as root
    systemctl stopstop a running service
    nginxthe web server, and with it the /health page

    Now go back to Target groups → quickcart-web-tg → Targets and refresh.

    You should see

    Within about 20 seconds — two failed checks, 10 seconds apart — quickcart-web-a shows Unhealthy, with a reason like Health checks failed.

    Run your curl command a few more times from your computer.

    You should see

    Every answer now comes from the same server: the one in us-east-1b. The load balancer only sends traffic to targets that pass their checks. If you were quick enough to run curl during those first 20 seconds, you may also have seen a 502 Bad Gateway: a request that went to the broken server before the checks caught up.

    Bring it back:

    Terminal
    sudo systemctl start nginx
    PartWhat it means
    systemctl startstart the service again
    nginxthe web server
    You should see

    After two passing checks, about 20 seconds, quickcart-web-a is Healthy again, and curl switches between both names once more.

  9. Clean-up step: Clean up

    Delete things in this order. The load balancer first, because it’s the most expensive, and because the security groups can’t be deleted while it uses them.

    In the AWS console: EC2Load balancersquickcart-albActionsDelete load balancer

    Type confirm and choose Delete.

    In the AWS console: EC2Target groupsquickcart-web-tgActionsDelete

    Choose Yes, delete.

    In the AWS console: EC2Instancesquickcart-web-a and quickcart-web-bInstance stateTerminate instance

    Tick both instances first. Newer consoles call it Terminate (delete) instance. Confirm it.

    In the AWS console: EC2Security Groupsquickcart-app-sgActionsDelete security groups

    Delete quickcart-app-sg first, then quickcart-alb-sg: the first one refers to the second.

    Keep the quickcart-ec2-ssm role for the next lesson; roles are free.

    You should see

    No load balancers, no target groups, both instances Terminated, and neither security group left. Nothing is billing.

Part 5: Check yourself

What decides which servers a load balancer sends requests to?

QuickCart's database goes down. With a shallow /health check, what do customers see?

Why can a deep health check, one that also queries the database, cause a total outage?

The health check interval is 10 seconds and the unhealthy threshold is 2. A server's app crashes. Roughly how long until it stops getting traffic?

What does the deregistration delay do?

What should QuickCart's port 80 listener do once HTTPS is set up?

Part 6: Cheat sheet

On one card
TermIn one line
Load balancerOne fixed address in front of many servers
ALBFor HTTP: can route by path and hostname, and do HTTPS
ListenerA port and protocol, and what to do with requests on it
Target groupThe servers, plus the health check that picks who gets traffic
Health checkA small request every few seconds. Fail enough in a row: no traffic
Time to take a broken server outInterval × unhealthy threshold. 10 s × 2 = about 20 s
Shallow check“Is this server broken?” — use this
Deep check“Is everything behind it working?” — one shared failure takes out every server
Deregistration delayLets in-progress requests finish. Default 300 s; QuickCart uses 30 s
502 / 503 / 504Broken answer / no target / too slow
Port 80 listenerRedirect to HTTPS, 301
Servers’ security groupPort 80 from the load balancer’s security group only
Timed out?Check the security groups first

Finished the build and the quiz? Mark it done to fill in its stop on the route map.