Load-Balanced Web Application with ALB and Custom AMI
Topics: Application Load Balancer, Target Groups, Custom AMI, High Availability, Auto Scaling, Apache
Overview
Application Load Balancers (ALB) distribute incoming HTTP/HTTPS traffic across multiple EC2 instances, ensuring your application remains available even if individual instances fail. This lab demonstrates deploying a load-balanced web architecture.
You'll create a custom Amazon Machine Image (AMI) containing your pre-configured web application, use it to launch multiple identical EC2 instances, configure a Target Group to manage instance health, and deploy an Application Load Balancer to distribute traffic across instances.
Key Concepts
| Concept | Description |
|---|---|
| Application Load Balancer (ALB) | Layer 7 HTTP/HTTPS load balancer that intelligently distributes traffic across multiple targets based on request content, paths, and headers |
| Target Group | Logical grouping of EC2 instances (targets) that receive traffic from ALB; includes health check configuration to monitor instance availability |
| Custom AMI (Amazon Machine Image) | Snapshot of configured EC2 instance including OS, applications, and data; enables launching identical instances for consistency |
| Health Check | Automated monitoring that ALB performs on each target to determine if it's healthy and should receive traffic |
| Load Balancing Algorithm | Method ALB uses to distribute requests (Round Robin for equal distribution across healthy targets) |
| High Availability | Architecture design ensuring services remain accessible even when individual components fail |
| Horizontal Scaling | Adding more instances to handle increased load rather than making existing instances larger (vertical scaling) |
| DNS Name (ALB) | Publicly accessible domain name automatically assigned to ALB for accessing your application |
| Instance State (Target Group) | Health status showing if instance is healthy, unhealthy, initial, draining, or unused |
| Listener | ALB component that checks for connection requests using configured protocol and port (e.g., HTTP port 80) |
Prerequisites
- AWS account with EC2 and Elastic Load Balancing permissions
- Completed EC2 and VPC labs (understanding of instances, security groups, subnets)
- VPC with at least two public subnets in different Availability Zones
- EC2 key pair for SSH access (.pem file)
- Basic understanding of HTTP web servers and DNS
- Familiarity with Apache/httpd (from previous EC2 web server labs)
Architecture Overview
Click to expand Architecture Diagram
Phase 1: Launch and Configure Base EC2 Instance
Create the foundational instance that will become your custom AMI.
Sign in to AWS Management Console.
Navigate to EC2 service → Click Launch Instance.
Configure instance details:
- Name:
Base-WebServer - AMI: Amazon Linux 2 AMI (Free tier eligible)
- Instance type:
t3.micro - Key pair: Select existing key pair
- Name:
Configure Network Settings:
- VPC: Default VPC or custom VPC
- Subnet: Any public subnet
- Auto-assign Public IP: Enable
Configure Security Group:
- Create new security group:
- Name:
WebServer-SG - Description: "Allow HTTP and SSH"
- Name:
- Inbound rules:
- Rule 1: HTTP (80) from 0.0.0.0/0
- Rule 2: SSH (22) from My IP
- Create new security group:
Click Launch instance.
Wait for instance state: Running, status checks: 2/2 passed.
Phase 2: Install Apache and Create Custom Web Content
Connect to instance via SSH:
bashssh -i your-key.pem ec2-user@<Public-IP>Update system packages:
bashsudo yum update -yInstall Apache web server:
bashsudo yum install httpd -yStart and enable Apache:
bashsudo systemctl start httpd sudo systemctl enable httpdVerify Apache is running:
bashsudo systemctl status httpdNavigate to web document root:
bashcd /var/www/htmlCreate dynamic web page showing instance information:
bashecho "<h1>Hello from Instance 1 - $(hostname)</h1>" | sudo tee /var/www/html/index.html
Hostname Variable
The $(hostname) command dynamically inserts the instance's hostname. When you create the AMI and launch multiple instances, each will display its unique hostname, allowing you to verify load balancing is working. Refreshing the browser will show different hostnames as ALB distributes traffic.
- Test the web server:
- Open browser:
http://<Public-IP> - Verify the page displays with hostname
- Open browser:
Enhanced Web Page
For better visualization, create a more detailed page:
echo '<!DOCTYPE html>
<html>
<head><title>Load Balanced Server</title></head>
<body style="text-align:center; background-color:#e3f2fd; font-family:Arial; padding:50px;">
<h1 style="color:#0d47a1;">Server: '$(hostname)'</h1>
<p style="font-size:18px;">Instance IP: '$(hostname -I | awk '{print $1}')'</p>
<p>Request served by this instance</p>
</body>
</html>' | sudo tee /var/www/html/index.htmlPhase 3: Create Custom AMI
Create a reusable image from your configured instance.
In EC2 console, navigate to Instances.
Select
Base-WebServerinstance.Click Actions → Image and templates → Create image.
Configure AMI:
- Image name:
WebServer-AMI - Image description: "Apache web server with custom homepage"
- Reboot: Checked (recommended for data consistency)
- Image name:
Click Create image.
Monitor AMI creation:
- Navigate to AMIs (left sidebar under Images)
- Wait for status: Available (takes 3-5 minutes)
- Note the AMI ID (e.g., ami-0abc12345def67890)
AMI Availability
The AMI must show status "Available" before you can launch instances from it. Creating the AMI takes a snapshot of all EBS volumes attached to the instance, which requires a few minutes. Don't proceed to Target Group creation until this is complete.
Phase 4: Create Target Group
Target Groups define which instances receive traffic from the load balancer and how ALB monitors their health.
In EC2 console left navigation, scroll down to Load Balancing section.
Click Target Groups → Create target group.
Specify group details:
- Choose a target type: Instances
- Target group name:
WebApp-TG - Protocol: HTTP
- Port: 80
- VPC: Select your VPC (Default VPC or custom)
Configure health checks:
- Health check protocol: HTTP
- Health check path:
/ - Advanced health check settings:
- Healthy threshold: 2 (number of consecutive successful checks)
- Unhealthy threshold: 2 (number of consecutive failed checks)
- Timeout: 5 seconds
- Interval: 30 seconds
Health Check Path
The health check path / means ALB will request http://instance-ip/ every 30 seconds. If it receives HTTP 200 OK response twice in a row, the instance is marked healthy. If it fails twice, the instance is marked unhealthy and stops receiving traffic.
Click Next.
Register targets: Skip this step (click Next without selecting instances)
- We'll register instances later when launching from AMI
- Or let Auto Scaling Group automatically register instances
Review and click Create target group.
Verify Target Group created successfully:
- Shows in Target Groups list
- Status: No targets registered (expected at this stage)
Phase 5: Create Application Load Balancer
Deploy the load balancer that will distribute traffic across your instances.
In EC2 console left navigation, click Load Balancers.
Click Create load balancer.
Select load balancer type:
- Choose Application Load Balancer
- Click Create
Configure basic settings:
- Load balancer name:
WebApp-ALB - Scheme: Internet-facing
- Internet-facing: Accessible from internet (public)
- Internal: Accessible only within VPC (private)
- IP address type: IPv4
- Load balancer name:
Configure network mapping:
- VPC: Select your VPC
- Mappings: Select at least 2 Availability Zones
- Check boxes for 2 or more AZs (e.g., us-east-1a, us-east-1b)
- For each AZ, select a public subnet
Multi-AZ Requirement
ALB requires subnets in at least two different Availability Zones for high availability. If your VPC only has subnets in one AZ, the creation will fail. This ensures if one AZ fails, the ALB continues operating in the other AZ.
Configure security groups:
- Security groups: Create new or select existing
- If creating new:
- Name:
ALB-SG - Inbound rules: HTTP (80) from 0.0.0.0/0
- Name:
- Remove default security group if needed
Configure listeners and routing:
- Listeners:
- Protocol: HTTP
- Port: 80
- Default action: Forward to target group
- Target group: Select
WebApp-TG
- Target group: Select
- Listeners:
Listener Concept
A listener is a process that checks for connection requests. The listener forwards matching requests to the target group. You can add multiple listeners (e.g., HTTPS on port 443) later.
Review all settings:
- Load balancer: Internet-facing, IPv4
- Network: 2+ Availability Zones selected
- Security group: Allows HTTP (80)
- Listener: HTTP:80 → WebApp-TG
Click Create load balancer.
Wait for provisioning:
- State: Provisioning (2-3 minutes)
- State changes to: Active
- Copy the DNS name (e.g.,
WebApp-ALB-123456789.us-east-1.elb.amazonaws.com)
DNS Propagation
Even after ALB shows "Active," DNS propagation may take 1-2 minutes. If the DNS name doesn't resolve immediately, wait a moment and try again.
Phase 6: Launch Instances from Custom AMI
Launch multiple instances from your AMI to demonstrate load balancing.
Launch Instance 1
Navigate to EC2 → Click Launch Instance.
Configure instance:
- Name:
WebServer-Instance-1 - AMI: Click My AMIs tab → Select
WebServer-AMI - Instance type:
t3.micro - Key pair: Select your key pair
- Name:
Network settings:
- VPC: Same VPC as ALB
- Subnet: Select public subnet in first AZ (e.g., us-east-1a)
- Auto-assign Public IP: Enable (for testing)
Security group:
- Select existing:
WebServer-SG(created in Phase 1) - Or create new allowing HTTP (80) from 0.0.0.0/0
- Select existing:
Click Launch instance.
Launch Instance 2
Click Launch Instance again.
Configure instance:
- Name:
WebServer-Instance-2 - AMI: My AMIs →
WebServer-AMI - Instance type:
t3.micro - Key pair: Same as Instance 1
- Name:
Network settings:
- VPC: Same VPC
- Subnet: Select public subnet in second AZ (e.g., us-east-1b)
- Auto-assign Public IP: Enable
Availability Zone Distribution
Launching instances in different AZs provides high availability. If one AZ experiences an outage, instances in the other AZ continue serving traffic.
Security group:
- Select existing:
WebServer-SG
- Select existing:
Click Launch instance.
Wait for both instances:
- State: Running
- Status checks: 2/2 passed
- Both instances show in Instances list
Phase 7: Register Instances with Target Group
Add your instances to the Target Group so ALB can route traffic to them.
Navigate to Target Groups.
Select
WebApp-TG.Click Targets tab (bottom panel).
Click Register targets button.
Select instances to register:
- Check box for
WebServer-Instance-1 - Check box for
WebServer-Instance-2 - Both should appear in available instances list
- Check box for
Click Include as pending below button.
Verify instances appear in "Targets to be registered" section.
Click Register pending targets button.
Monitor health status:
- Status: Initial → Healthy (takes 30-60 seconds)
- Health status checks:
- Initial: Waiting for first health check
- Healthy: Instance passed 2 consecutive checks
- Unhealthy: Instance failed 2 consecutive checks
Troubleshooting Unhealthy Status
If instances show "Unhealthy":
- Verify security group allows HTTP (80) from ALB security group
- Check httpd service is running:
sudo systemctl status httpd - Test instance directly:
http://<instance-public-ip> - Check health check path returns HTTP 200:
curl http://localhost/ - Review Target Group health check settings (path, port, thresholds)
- Wait for both instances to show Healthy status.
Phase 8: Test Load Balancer
Verify ALB is distributing traffic across your instances.
Copy the ALB DNS name:
- Navigate to Load Balancers
- Select
WebApp-ALB - Copy DNS name from Description tab
Open web browser.
Navigate to ALB DNS:
http://WebApp-ALB-123456789.us-east-1.elb.amazonaws.comObserve the response:
- Page displays: "Hello from Instance 1 - ip-10-0-1-15"
- Or: "Hello from Instance 2 - ip-10-0-2-20"
Refresh the browser multiple times (F5 or Ctrl+R):
- First refresh: Shows Instance 1 hostname
- Second refresh: Shows Instance 2 hostname
- Third refresh: Shows Instance 1 hostname
- Pattern continues (Round Robin distribution)
Load Balancing Verification
The changing hostnames prove ALB is distributing requests across both instances. Each refresh may go to a different instance. If you see the same instance repeatedly, ALB uses sticky sessions or your browser cached the response—try a different browser or incognito mode.
Test high availability:
- Note which instance hostname displays
- In EC2 console, stop that instance
- Refresh browser
- Observe: ALB automatically routes to the remaining healthy instance
- No downtime experienced
Restart stopped instance:
- Start the stopped instance
- Wait for health check to mark it Healthy (60 seconds)
- Refresh browser
- Observe: Load balancing resumes across both instances
Advanced Testing
Use command line for rapid testing:
# Make 10 requests and show unique responses
for i in {1..10}; do curl -s http://your-alb-dns.elb.amazonaws.com | grep -o 'Instance [0-9]'; doneYou should see a mix of Instance 1 and Instance 2 in the output.
Validation
Validation
Verify successful completion:
Custom AMI:
- AMI appears in AMIs list with status "Available"
- AMI includes Apache and custom web content
- AMI ID noted for reference
Target Group:
- Target Group
WebApp-TGcreated successfully - Protocol HTTP, Port 80 configured
- Health check path
/with 30-second interval - Two instances registered and showing Healthy status
- No unhealthy or draining instances
- Target Group
Application Load Balancer:
- ALB
WebApp-ALBcreated with state "Active" - Scheme: Internet-facing
- At least 2 Availability Zones configured
- Listener HTTP:80 forwarding to
WebApp-TG - Security group allows HTTP (80) from 0.0.0.0/0
- DNS name accessible and returns web content
- ALB
EC2 Instances:
- Two instances running in different Availability Zones
- Both launched from
WebServer-AMI - Status checks: 2/2 passed
- Security group allows HTTP (80)
- Apache serving custom web content on port 80
Load Balancing Functionality:
- ALB DNS name accessible in browser
- Refreshing browser shows alternating instance hostnames
- Stopping one instance doesn't cause downtime
- Restarting stopped instance resumes load balancing
- Round Robin distribution verified
Cost Considerations
Cost Considerations
Application Load Balancer:
- Hourly charge: ~$0.0225/hour = ~$16.20/month (us-east-1)
- LCU (Load Balancer Capacity Unit) charges: $0.008 per LCU-hour
- Minimum cost: Even with no traffic, ~$16.20/month for ALB running 24/7
- Free Tier: No free tier for ALB (charges apply immediately)
EC2 Instances (2× t3.micro):
- Free Tier: 750 hours/month for first 12 months (covers both instances)
- After Free Tier: 2× $0.0104/hour = $0.0208/hour = ~$15/month
EBS Storage (2× 8 GB gp3):
- Free Tier: 30 GB/month (covers 16 GB total)
- After Free Tier: $0.08/GB-month = $1.28/month for 16 GB
AMI Storage (Snapshot):
- EBS snapshot: ~$0.05/GB-month = $0.40/month for 8 GB snapshot
- Free Tier: Not covered (charged from creation)
Data Transfer:
- ALB to instances: Free (within same AZ)
- Cross-AZ: $0.01/GB each direction (if instances in different AZs)
- Outbound to internet: First 100 GB free, then $0.09/GB
Total Cost (1 hour lab, Free Tier eligible):
- ALB: $0.0225
- 2× EC2 instances: $0 (free tier)
- Data transfer: ~$0
- Total: ~$0.02 for 1-hour lab
Total Cost (30 days, after Free Tier):
- ALB: ~$16.20
- EC2: ~$15
- EBS: ~$1.28
- AMI snapshot: ~$0.40
- Total: ~$32.88/month
ALB Costs
Application Load Balancer is the primary cost driver in this architecture. Unlike EC2 instances (which have free tier), ALB charges start immediately at ~$0.0225/hour. For learning purposes, delete the ALB immediately after completing the lab to avoid ongoing charges.
Cost Optimization
- Delete ALB when not in use (~$16/month savings)
- Stop EC2 instances when testing complete (saves compute, but EBS charges continue)
- For production: ALB cost is justified by high availability and scalability benefits
- Use CloudWatch to monitor actual LCU consumption
Cleanup
Cleanup
Delete resources in this order to avoid dependency errors and stop all charges:
1. Deregister Targets from Target Group
- Navigate to Target Groups → Select
WebApp-TG. - Click Targets tab.
- Select all registered instances.
- Click Actions → Deregister.
- Confirm deregistration.
- Wait for targets to be removed.
Deregistration Requirement
You cannot delete a Target Group while instances are registered. ALB must also be deleted before Target Group deletion.
2. Delete Application Load Balancer
- Navigate to Load Balancers.
- Select
WebApp-ALB. - Click Actions → Delete load balancer.
- Type
confirmto confirm deletion. - Click Delete.
- Wait for deletion to complete (2-3 minutes).
Delete ALB First
ALB must be deleted before you can delete the Target Group. ALB deletion is not immediate—wait for it to disappear from the list before proceeding.
3. Delete Target Group
- Navigate to Target Groups.
- Select
WebApp-TG. - Click Actions → Delete.
- Confirm deletion.
4. Terminate EC2 Instances
- Navigate to Instances.
- Select
WebServer-Instance-1andWebServer-Instance-2. - Click Instance state → Terminate instance.
- Confirm termination.
- Wait for state: Terminated.
5. Deregister AMI
- Navigate to AMIs (left sidebar under Images).
- Select
WebServer-AMI. - Click Actions → Deregister AMI.
- Confirm deregistration.
AMI Deregistration
Deregistering the AMI doesn't delete the underlying snapshot. You must delete the snapshot separately to avoid storage charges.
6. Delete EBS Snapshots
- Navigate to Snapshots (left sidebar under Elastic Block Store).
- Find snapshot associated with
WebServer-AMI:- Description shows AMI ID or AMI name
- Select the snapshot.
- Click Actions → Delete snapshot.
- Confirm deletion.
7. Delete Security Groups (Optional)
- Navigate to Security Groups.
- Select
WebServer-SGandALB-SG. - Click Actions → Delete security groups.
- If error "has dependent object," wait 2-3 minutes for network interfaces to detach.
- Retry deletion.
8. Terminate Base EC2 Instance
- Navigate to Instances.
- Select
Base-WebServer. - Click Instance state → Terminate instance.
- Confirm termination.
Verification
- All instances show "Terminated" state
- ALB removed from Load Balancers list
- Target Group removed from Target Groups list
- AMI deregistered from AMIs list
- Snapshot deleted from Snapshots list
- No unexpected charges in Billing dashboard (check after 24 hours)
Result
You have successfully deployed a production-ready load-balanced web architecture using Application Load Balancer, Target Groups, and custom AMIs. This configuration provides high availability by distributing traffic across multiple instances in different Availability Zones, ensuring your application remains accessible even if individual instances or entire data centers fail.
Viva Questions
What is the difference between an Application Load Balancer and a Network Load Balancer?
- Application Load Balancer (ALB) operates at Layer 7 (HTTP/HTTPS) and makes routing decisions based on request content like URLs, headers, and paths. It supports advanced features like host-based routing, path-based routing, and WebSocket. ALB is ideal for web applications and microservices. Network Load Balancer (NLB) operates at Layer 4 (TCP/UDP) and routes based on IP protocol data, offering ultra-low latency and handling millions of requests per second. NLB is ideal for TCP/UDP applications, extreme performance needs, or when you need static IP addresses. Classic Load Balancer operates at both layers but is legacy technology.
Why do we create a custom AMI instead of manually installing Apache on each instance?
- Custom AMIs ensure deployment consistency—every instance launched from the AMI has identical configuration, eliminating human error and configuration drift. AMIs enable faster scaling (launch in seconds vs. minutes for manual installation), simplify disaster recovery (quickly replace failed instances), and support infrastructure as code practices. For Auto Scaling, AMIs are essential—the Auto Scaling Group must launch pre-configured instances automatically without human intervention. Additionally, AMIs can be shared across regions and accounts, versioned for rollback, and include security hardening and compliance configurations that would be tedious to repeat manually.
Explain how health checks work and what happens when an instance becomes unhealthy.
- ALB sends HTTP requests to each registered target at the health check path (e.g.,
/) every 30 seconds (configurable interval). If the instance responds with HTTP 200 OK, it increments the healthy count. After 2 consecutive successful checks (healthy threshold), the instance is marked Healthy and receives traffic. If the instance fails to respond or returns 4xx/5xx errors, it increments the unhealthy count. After 2 consecutive failures (unhealthy threshold), ALB marks the instance Unhealthy and stops routing new requests to it (existing connections may continue draining). ALB continuously monitors unhealthy instances and automatically re-adds them to the rotation when they pass health checks again, providing self-healing capabilities.
- ALB sends HTTP requests to each registered target at the health check path (e.g.,
Why must ALB be deployed in at least two Availability Zones?
- ALB is designed for high availability at the load balancer level itself. By requiring subnets in at least two Availability Zones, AWS ensures the ALB infrastructure can survive an entire AZ failure. Each AZ has independent power, cooling, and networking—physically separated by miles. If you deploy ALB in only one AZ and that AZ experiences an outage (power failure, natural disaster, network issue), your entire load balancer becomes unavailable, making your application inaccessible even if backend instances in other AZs are healthy. Multi-AZ ALB deployment distributes load balancer nodes across AZs, ensuring if one AZ fails, ALB continues operating from other AZs, providing true fault tolerance.
What is the purpose of the Target Group, and how does it differ from a Security Group?
- A Target Group is a logical collection of instances (targets) that ALB routes traffic to based on listener rules. It defines which instances receive traffic, how ALB monitors their health (health check configuration), what protocol/port to use, and load balancing algorithm. Target Groups are application-layer concepts for traffic distribution. Security Groups are network-layer firewalls controlling what traffic can reach instances based on protocol, port, and source IP/security group. They're completely different: Target Groups determine IF an instance should receive traffic from ALB (based on health), while Security Groups determine WHAT traffic is allowed to reach instances (based on network rules). You need both—Target Group for load balancing logic, Security Group for network security.
