This project demonstrates the deployment of a scalable, highly available three-tier web application architecture on AWS. The architecture separates presentation, application logic, and data storage into distinct tiers, providing improved security, scalability, and maintainability.
- High Availability: Multi-AZ deployment across two availability zones
- Auto Scaling: Automatic scaling based on demand for both web and application tiers
- Load Balancing: Internal and external load balancers for traffic distribution
- Security: Proper security group configuration and private subnet isolation
- Database: Amazon Aurora MySQL-compatible database with read replicas
- Architecture Overview
- Prerequisites
- Setup
- Networking and Security
- Database Deployment
- App Tier Instance Deployment
- Internal Load Balancing and Auto Scaling
- Web Tier Instance Deployment
- External Load Balancer and Auto Scaling
- Testing and Verification
- Troubleshooting
- Web Tier: Public-facing layer serving the React frontend
- Application Tier: Private layer hosting the Node.js backend API
- Database Tier: Private layer with Aurora MySQL database
Before starting this workshop, ensure you have:
- AWS Account with appropriate permissions
- Basic understanding of AWS services (VPC, EC2, RDS, ALB)
- Familiarity with Linux command line
- Git installed on your local machine
Clone the workshop repository to access the application code:
git clone https://github.com/aws-samples/aws-three-tier-web-architecture-workshop.git- Navigate to the S3 service in the AWS console and create a new S3 bucket.
- Provide a unique name for your bucket and select the region where you plan to deploy the entire architecture. This bucket will store your application code for deployment.
- Navigate to the IAM dashboard and create a new EC2 role with EC2 as the trusted entity.
IAM Role following AWS managed policies to the role:
AmazonSSMManagedInstanceCoreAmazonS3ReadOnlyAccess
These policies enable instances to download code from S3 and use Systems Manager Session Manager for secure connections without SSH keys.
- Navigate to the VPC dashboard and select Your VPCs β Create VPC.
- Choose VPC only and configure the following:
- Name tag: Descriptive name for your VPC
- CIDR range: Choose an appropriate CIDR block (e.g.,
10.0.0.0/16)
Create six subnets across two availability zones (three subnets per AZ):
- Navigate to Subnets β Create subnet
- Create the following subnet structure:
| Subnet Name | Availability Zone | CIDR Block | Tier |
|---|---|---|---|
| Public-Web-Subnet-AZ1 | us-east-1a | 10.0.1.0/24 | Web |
| Private-App-Subnet-AZ1 | us-east-1a | 10.0.2.0/24 | Application |
| Private-DB-Subnet-AZ1 | us-east-1a | 10.0.3.0/24 | Database |
| Public-Web-Subnet-AZ2 | us-east-1b | 10.0.4.0/24 | Web |
| Private-App-Subnet-AZ2 | us-east-1b | 10.0.5.0/24 | Application |
| Private-DB-Subnet-AZ2 | us-east-1b | 10.0.6.0/24 | Database |
- Create an Internet Gateway with a descriptive name.
- Navigate to NAT Gateways β Create NAT Gateway
- Configure the following:
- Name: Descriptive name
- Subnet: Select one of the public subnets
- Elastic IP: Allocate a new Elastic IP
- Create a route table for public subnets.
Create the following security groups with appropriate rules
| Setting | Inbound Rules | Values |
|---|---|---|
| WebTierSG | Web tier instances | HTTP (80) from 0.0.0.0/0, HTTPS (443) from 0.0.0.0/0 |
| PrivateInstanceSG | App tier instances | HTTP (4000) from WebTierSG |
| DatabaseSG | Database instances | MySQL (3306) from PrivateInstanceSG |
| InternalALBSG | Internal load balancer | HTTP (80) from WebTierSG |
| ExternalALBSG | External load balancer | HTTP (80) from 0.0.0.0/0, HTTPS (443) from 0.0.0.0/0 |
- Navigate to RDS dashboard β Subnet groups β Create DB subnet group
- Configure the subnet group:
- Name: Descriptive name for the subnet group
- Description: Brief description
- VPC: Select your created VPC
- Subnets: Add both database subnets from different AZs
- Navigate to Databases β Create database
- Configure the Aurora MySQL database.
- Engine type: Amazon Aurora
- Edition: MySQL-Compatible
- Template: Dev/Test
- DB cluster identifier: Choose a unique name
- Master username: Set username (note it down)
- Master password: Set password (note it down)
- Multi-AZ deployment: Create Aurora Replica in different AZ
- VPC: Select your created VPC
- DB subnet group: Select the created subnet group
- Public access: No
- Security group: Select DatabaseSG
- Note down the writer endpoint after database creation for later use.
- Navigate to EC2 dashboard β Instances β Launch Instances
- Configure the instance:
| Setting | Value |
|---|---|
| Name | App-Tier-Instance |
| AMI | Amazon Linux 2 AMI (HVM) - Kernel 5.10 |
| Instance Type | t2.micro |
| Key Pair | Proceed without a key pair |
| VPC | Your created VPC |
| Subnet | Private-App-Subnet |
| Security Group | PrivateInstanceSG |
| Auto-assign Public IP | Disable |
| IAM Instance Profile | Your created IAM role |
- Navigate to Instances and select your app tier instance.
- Click Connect β Session Manager β Connect.
# Switch to ec2-user
sudo -su ec2-user
# Test internet connectivity
ping 8.8.8.8- Install MySQL CLI:
sudo yum install mysql -y- Connect to your RDS database:
mysql -h YOUR-RDS-ENDPOINT -u YOUR-USERNAME -p- Create the application database:
CREATE DATABASE webappdb;- Verify database creation:
SHOW DATABASES;- Create the transactions table:
USE webappdb;
CREATE TABLE IF NOT EXISTS transactions(
id INT NOT NULL AUTO_INCREMENT,
amount DECIMAL(10,2),
description VARCHAR(100),
PRIMARY KEY(id)
);- Verify table creation:
SHOW TABLES;- Insert sample data:
INSERT INTO transactions (amount,description) VALUES ('400','groceries');- Verify data insertion:
SELECT * FROM transactions;- Exit MySQL client:
exit- Update the database configuration file
application-code/app-tier/DbConfig.json your local machine with your database credentials:
const config = {
hostname: "YOUR-RDS-WRITER-ENDPOINT",
user: "YOUR-DB-USERNAME",
password: "YOUR-DB-PASSWORD",
database: "webappdb",
};- Upload the
app-tierfolder to your S3 bucket.
Now we need to install all of the necessary components to run our backend application. Start by installing NVM.
# Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
source ~/.bashrc
# Install Node.js
nvm install 16
nvm use 16
# Install PM2 process manager
npm install -g pm2- Download application code from S3:
cd ~/
aws s3 cp s3://YOUR-BUCKET-NAME/app-tier/ app-tier --recursive- Install dependencies and start the application:
cd ~/app-tier
npm install
pm2 start index.js- Verify the application is running:
pm2 list- Configure PM2 to start on boot:
pm2 startup
# Copy and run the command from the output
pm2 save- Test the health check endpoint:
curl http://localhost:4000/healthExpected response: "This is the health check"
- Test database connectivity:
curl http://localhost:4000/transactionThis should return the transaction data from your database.
- Navigate to Instances β Select app tier instance β Actions β Image and templates β Create Image
- Provide a name and description for the AMI.
- Navigate to Target Groups β Create Target Group
- Configure the target group:
| Setting | Value |
|---|---|
| Target Type | Instances |
| Target Group Name | App-Tier-TG |
| Protocol | HTTP |
| Port | 4000 |
| VPC | Your created VPC |
| Health Check Path | /health |
- Navigate to Load Balancers β Create Load Balancer β Application Load Balancer
- Configure the internal ALB:
| Setting | Value |
|---|---|
| Name | Internal-ALB |
| Scheme | Internal |
| VPC | Your created VPC |
| Subnets | Private app subnets |
| Security Group | InternalALBSG |
| Listener | HTTP:80 β App-Tier-TG |
- Navigate to Launch Templates β Create Launch Template
- Configure the launch template:
Auto Scaling Groups β Create Auto Scaling Group 2. Configure the Auto Scaling Group:
| Auto Scaling Group Creation | | ------------------------------------------- | --------------------- | | Name | App-Tier-ASG | | Launch Template | App-Tier-LT | | VPC | Your created VPC | | Subnets | Private app subnets | | Target Group | App-Tier-TG | | Desired Capacity | 2 | | Minimum Capacity | 2 | | Maximum Capacity | 4 |
- Open the
application-code/nginx.conffile from your local repository. - Replace
[INTERNAL-LOADBALANCER-DNS]with your internal load balancer's DNS name. - Upload the updated
nginx.conffile and theweb-tierfolder to your S3 bucket.
- Launch a new EC2 instance with the following configuration
| Setting | Value |
|---|---|
| Name | Web-Tier-Instance |
| AMI | Amazon Linux 2 AMI (HVM) - Kernel 5.10 |
| Instance Type | t2.micro |
| Key Pair | Proceed without a key pair |
| VPC | Your created VPC |
| Subnet | Public-Web-Subnet |
| Security Group | WebTierSG |
| Auto-assign Public IP | Enable |
| IAM Instance Profile | Your created IAM role |
- Connect to the instance using Session Manager and switch to ec2-user:
sudo -su ec2-user
ping 8.8.8.8- Install Node.js and NVM:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
source ~/.bashrc
nvm install 16
nvm use 16- Download web tier code from S3:
cd ~/
aws s3 cp s3://YOUR-BUCKET-NAME/web-tier/ web-tier --recursive- Build the React application:
cd ~/web-tier
npm install
npm run build- Install and configure Nginx:
sudo yum install nginx -y
cd /etc/nginx
ls- Replace the default Nginx configuration:
sudo rm nginx.conf
sudo aws s3 cp s3://YOUR-BUCKET-NAME/nginx.conf .- Start and enable Nginx:
sudo service nginx restart
chmod -R 755 /home/ec2-user
sudo chkconfig nginx on- Create an AMI from your configured web tier instance following the same process as the app tier AMI.
- Create a target group for the web tier.
| Setting | Value |
|---|---|
| Target Type | Instances |
| Target Group Name | Web-Tier-TG |
| Protocol | HTTP |
| Port | 80 |
| VPC | Your created VPC |
| Health Check Path | / |
- Create an Application Load Balancer with the following configuration.
| Setting | Value |
|---|---|
| Name | External-ALB |
| Scheme | Internet-facing |
| VPC | Your created VPC |
| Subnets | Public web subnets |
| Security Group | ExternalALBSG |
| Listener | HTTP:80 β Web-Tier-TG |
D. Create a launch template for the web tier using the web tier AMI and appropriate configuration, same as created earlier.
- Create an Auto Scaling Group for the web tier:
| Setting | Value |
|---|---|
| Name | Web-Tier-ASG |
| Launch Template | Web-Tier-LT |
| VPC | Your created VPC |
| Subnets | Public web subnets |
| Target Group | Web-Tier-TG |
| Desired Capacity | 2 |
| Minimum Capacity | 2 |
| Maximum Capacity | 4 |
- Access the Application: Use the external load balancer's DNS name to access your web application.
- Health Checks: Verify that all target groups show healthy targets.
- Database Connectivity: Test the transaction functionality through the web interface.
- Auto Scaling: Monitor the Auto Scaling Groups to ensure they maintain the desired capacity.
Consider using tools like Apache Bench or AWS Load Testing solutions to verify the architecture's scalability.
- Verify security group rules allow necessary traffic
- Check route table associations
- Ensure NAT Gateway is properly configured for private instances
- Verify database security group allows traffic from app tier
- Check database endpoint and credentials
- Ensure database is in available state
- Verify target group health checks are passing
- Check security group rules for load balancers
- Ensure proper subnet associations
- Check PM2 process status:
pm2 list - Review application logs:
pm2 logs - Verify Node.js and npm installations
- Use CloudWatch to monitor instance metrics
- Enable VPC Flow Logs for network troubleshooting
- Configure CloudWatch Logs for application logging
Congratulations! You have successfully deployed a three-tier web architecture on AWS. This architecture provides:
- High Availability: Multi-AZ deployment ensures resilience
- Scalability: Auto Scaling Groups handle varying loads
- Security: Proper network segmentation and security groups
- Performance: Load balancers distribute traffic efficiently