DEV Community

Fidelis Ikoroje
Fidelis Ikoroje

Posted on

Building a Scalable Multi-Tier Web Application on AWS

Introduction

In this post, I’ll walk through the architecture and implementation of a multi-tier web application hosted on AWS. Designed for scalability and high availability, this application uses an autoscaling group of EC2 instances to serve web requests, with a dedicated backend infrastructure for queue management, database storage, and caching. From setting up the infrastructure to securing and optimizing it, here’s a complete look at how I brought this project to life on AWS.


Requirements

Before diving into the implementation, let’s start with the requirements:

  • Web Application Server: Hosted on Tomcat 10, listening on port 8080 on EC2 instances.
  • Web Application Source Code: Build one or fork from github
  • Load Balancing and Autoscaling: Application Load Balancer (ALB) in front of an EC2 Autoscaling Group to handle web traffic.
  • Backend Servers:
    • RabbitMQ for message queuing services.
    • MySQL for user data and web app credentials.
    • Memcached for caching content to improve performance.
  • Domain and SSL: A custom domain with HTTPS enabled using AWS Certificate Manager (ACM).
  • DNS Management: Route 53 to manage DNS for both public and private traffic.
  • AWS CLI & Maven: AWS CLI to interact with AWS environment & Maven to build code

Architecture Design

This is a classic three-tier architecture:

  1. Presentation Layer (Web App Frontend): Autoscaling EC2 instances running Tomcat.
  2. Application Layer (Service Backend): Consists of RabbitMQ, MySQL, and Memcached instances.
  3. Data Layer (Database and Caching): MySQL for persistence and Memcached for caching.

Implementation Steps

1. Creating Key Pairs and Security Groups

  • Key Pair: Created a key pair to access all EC2 instances via SSH.
  • Security Groups: Configured three security groups:
    • Web Application Security Group: Allows inbound traffic on port 8080 from the ALB.
    • Backend Security Group: Allows traffic from the web application for communication with RabbitMQ (port 5672), MySQL (port 3306), and Memcached (port 11211) on their respective ports.
    • Load Balancer Security Group: Allows inbound traffic on ports 80 (HTTP) and 443 (HTTPS) for the ALB. Also, allow SSH on port 22 into all your instances and pick the source as MYIP.

inbound rules for backend security group

2. Launching EC2 Instances with User Data Scripts

  • Database Instance: Launched MySQL instance and included a user data script to automatically install the MySQL server upon boot. SSH into the instance from your local system then confirm the database is running.
ssh -i "keypair.pem" ubuntu@ec2-public-ip
Enter fullscreen mode Exit fullscreen mode
systemclt status mariadb
Enter fullscreen mode Exit fullscreen mode

Image description

Create the database and check the tables

mysql -u username -p accounts
Enter fullscreen mode Exit fullscreen mode
show tables;
Enter fullscreen mode Exit fullscreen mode

Image description

  • Memcached Instance: Launched a separate instance for Memcached, using a user data script to install and configure the service.

Image description

Confirm the service is running

systemctl status memcached
Enter fullscreen mode Exit fullscreen mode
  • RabbitMQ Instance: Launched and configured for message queue handling.

Confirm the service is running

systemctl status rabbitmq-server
Enter fullscreen mode Exit fullscreen mode

Image description

  • Web Application EC2: Launch a Tomcat 10 server instance to serve the web application using Ubuntu AMI. Create EC2 role that grants read and write access to s3 and attach it to the EC2.

create a bucket that will hold the web app artifact

aws s3api create-bucket --bucket fozwebapp-artifact --region eu-west-2 --create-bucket-configuration LocationConstraint=eu-west-2
Enter fullscreen mode Exit fullscreen mode

Run Maven from the directory that holds the source code in your local system

mvn install
Enter fullscreen mode Exit fullscreen mode

Copy the artifact from your local system to the s3 bucket. Then, SSH into the instance, install AWS CLI and copy the artifact from s3 into the EC2 instance.

aws s3 cp target/vprofile-artifact s3://fozwebapp-artifact
Enter fullscreen mode Exit fullscreen mode

Image description

Install AWS CLI and copy artifact from s3 to /tmp/ in EC2

snap install aws-cli --classic
Enter fullscreen mode Exit fullscreen mode
aws s3 cp s3://fozwebapp-artifact/vprofile-v2.war /tmp/
Enter fullscreen mode Exit fullscreen mode

Image description

Now, stop tomcat10 on the EC2, remove the default directory of tomcat10, and replace it with the artifact the restart. Run each of the commands one after the other. Lastly, you can confirm tomcat10 is running

systemctl daemon-reload
systemctl stop tomcat10
rm -rf /var/lib/tomcat10/webapps/ROOT
cp /tmp/vprofile-v2.war /var/lib/tomcat10/webapps/ROOT.war
systemctl start tomcat10
Enter fullscreen mode Exit fullscreen mode

tomcat10 running

3. Configuring Route 53 for DNS

  • Domain Registration and SSL: Registered a domain with Route 53, requested an SSL certificate through ACM, and created a public hosted zone.
  • Public DNS: Added a CNAME record pointing the ALB’s endpoint to the domain for HTTPS access.
  • Private DNS: Created a private hosted zone with records for the backend services’ private IP addresses to allow internal communication by name.

4. Building and Deploying the Application Code

  • S3 Bucket Creation: Created an S3 bucket to store the application artifact.
  • IAM Role: Assigned an IAM role to the web application EC2 instance to allow S3 access.
  • Application Build: Built the application using Maven on the local machine and uploaded the artifact to S3.
  • Code Deployment: SSH’ed into the web application EC2, downloaded the artifact from S3, and deployed it to the Tomcat server.

5. Setting Up the Application Load Balancer

  • Target Group Creation: Configured an ALB target group for the autoscaling EC2 instances, setting health checks on port 8080 to ensure only healthy instances serve traffic.
  • HTTPS Configuration: Configured the ALB to use the SSL certificate from ACM.
  • DNS Mapping: Mapped the domain’s CNAME record in Route 53 to the ALB for secure, HTTPS-enabled access.

6. Connecting to the Web Application and Verifying

  • Access Verification: Accessed the web app through the custom domain and verified load balancer forwarding by inspecting responses from the Tomcat server.
  • Service Checks: SSH’ed into each backend instance to ensure that RabbitMQ, MySQL, and Memcached services were running and accessible by the web application server.

7. Configuring Auto Scaling for the Web Application EC2 Instances

  • Autoscaling Policies: Set up scaling policies to adjust the number of EC2 instances in response to application load.
  • Monitoring and Logging: Enabled CloudWatch metrics to monitor CPU and request metrics, triggering autoscaling when defined thresholds were met.

How the Web Application Works

  1. User Access: Users access the application via the custom domain. The ALB handles the HTTPS requests and forwards them to Tomcat servers running in the autoscaling group.
  2. Backend Processing: When a web request involves queuing or caching, the application server communicates with RabbitMQ and Memcached instances. For user data requests, it connects to MySQL.
  3. Scaling and Availability: The autoscaling group dynamically adjusts based on load, ensuring efficient handling of fluctuating traffic.

Lessons Learned and Final Thoughts

Building this multi-tier web app on AWS provided valuable insights into scalable architecture, automated deployment, and cloud security. Leveraging AWS services, I achieved a setup that balances performance with resilience, laying a foundation for future applications.

Top comments (0)