Building a Highly Available, Fault-Tolerant AWS 3-Tier Architecture using CloudFormation Part — 2

Amudha Balamurugan
8 min readApr 17, 2024

--

Check out part — 1 of this article here.

In part — 1, we developed a 3-tier architecture using a CloudFormation template. we also deployed the Data tier using RDS Aurora MySql.

Let us continue to deploy the App Tier and Web Tier.

AppTier Instance Deployment:

This section will create an EC2 instance for our app tier and make all necessary software configurations for it to run. The app tier consists of a Node.js application that will run on port 4000. We will also configure our database with some data and tables.

  1. Create App Tier Instance

Launch Instance → Amazon Linux 2 AMI → t2.micro → select vpc, Private subnets and AppTierSG → and select EC2 Power Role → Go without using keypair

App Tier

Now, check its connection by connecting through the session manager (using EC2 Power role). Switch to ec2-user and type ping 8.8.8.8 to check the connection.

AppTier instance working

You should see a transmission of packets. Stop it by pressing cntrl c.

NOTE: If you can’t reach the internet then you need to double-check your route tables and subnet associations to verify if traffic is being routed to your NAT gateway

2. Configure Database Schema

  1. Start by downloading the MySQL CLI.
sudo yum install mysql -y

2. Initiate your DB connection with your Aurora RDS writer endpoint.

mysql -h CHANGE-TO-YOUR-RDS-ENDPOINT -u CHANGE-TO-USER-NAME -p

3. Create a database called webappdb and check it.

CREATE DATABASE webappdb;
SHOW DATABASES;
Database Schema

4. Create a data table by navigating to the database we created. Create the following transactions table by executing this create table command.

USE webappdb;
CREATE TABLE IF NOT EXISTS transactions(id INT NOT NULL
AUTO_INCREMENT, amount DECIMAL(10,2), description
VARCHAR(100), PRIMARY KEY(id));
SHOW TABLES;

5. Insert data into table for use/testing later.

INSERT INTO transactions (amount,description) VALUES ('400','groceries');
SELECT * FROM transactions;
INSERT INTO transactions (amount,description) VALUES ('100','fruits');
INSERT INTO transactions (amount,description) VALUES ('200','snacks');
SELECT * FROM transactions;
webappdb table

3. Configure Software Stack

Update our database credentials for the app tier. To do this, open the application-code/app-tier/DbConfig.js file from the GitHub repo in your favorite text editor. You’ll see empty strings for the hostname, user, password and database.

DbConfig.js

Fill this in with the credentials you configured for your database, the writer endpoint of your database as the hostname, and webappdb for the database. Save the file.

Upload the app-tier folder to the S3 bucket created earlier.

Go back to your SSM session. Now we need to install all of the necessary components to run our backend application. Start by installing NVM (node version manager).

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
source ~/.bashrc
nvm install 16
nvm use 16
npm install -g pm2

PM2 is a daemon process manager that will keep our node.js app running when we exit the instance or if it is rebooted. Install that as well.

After that, we need to download our code from s3 buckets onto our instance. In the command below, replace BUCKET_NAME with the name of the bucket you uploaded the app-tier folder to:

cd ~/
aws s3 cp s3://BUCKET_NAME/app-tier/ app-tier --recursive

Navigate to app tier, install the dependencies and start the app with pm2

cd ~/app-tier
npm install
pm2 start index.js

Now, pm2 is just making sure our app stays running when we leave the SSM session. However, if the server is interrupted for some reason, we still want the app to start and keep running. This is also important for the AMI we will create:

pm2 startup

After running this you will see a message similar to this.

pm2 save

Test App Tier:

To hit out health check endpoint, copy this command into your SSM terminal. This is our simple health check endpoint that tells us if the app is simply running.

curl http://localhost:4000/health
curl http://localhost:4000/transaction

The response should be like this.

If you see both of these responses, then your networking, security, database and app configurations are correct.

Congrats! Your app layer is fully configured and ready to go.

Internal Load Balancing and Auto Scaling:

As the App tier is functioning as expected, its time to secure and scale our application for high availability and scalability.

This can be achieved by creating a template and a target group from the image of our app instance. Using the template, we can schedule an ASG with desired, minimum and maximum capacity to 2.

Update Config File

Before we create and configure the web instances, open up the application-code/nginx.conf file from the repo we downloaded. Scroll down to line 58 and replace [INTERNAL-LOADBALANCER-DNS] with your internal load balancer’s DNS entry. Then save the file and upload it along with the web tier folder to our s3 bucket.

nginx.config

Web Tier Instance Deployment:

Follow the same instance creation instructions we used for the App Tier Instance Deployment, except the subnet. We will be provisioning this instance in one of our public subnets. Make sure to select the correct network components, security group, and IAM role. This time, auto-assign a public ip on the Configure Instance Details page. Remember to tag the instance with a name so we can identify it more easily.

Connect to Instance:

Follow the same steps you used to connect to the app instance and change the user to ec2-user. Test connectivity here via ping as well since this instance should have internet connectivity:

sudo -su ec2-user 
ping 8.8.8.8

Configure Web Instance:

We now need to install the necessary components to run our front-end application. Again, start by installing NVM and node :

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
source ~/.bashrc
nvm install 16
nvm use 16

Now we need to download our web tier code from our s3 bucket:

cd ~/
aws s3 cp s3://BUCKET_NAME/web-tier/ web-tier --recursive

Navigate to the web-layer folder and create the build folder for the react app so we can serve our code:

cd ~/web-tier
npm install
npm run build

NGINX can be used for different use cases like load balancing, content caching etc, but we will be using it as a web server that we will configure to serve our application on port 80, as well as help direct our API calls to the internal load balancer.

sudo amazon-linux-extras install nginx1 -y

We will now have to configure NGINX. Navigate to the Nginx configuration file with the following commands and list the files in the directory:

cd /etc/nginx
ls

You should see an nginx.conf file. We’re going to delete this file and use the one we uploaded to s3. Replace the bucket name in the command below with the one you created for this workshop:

sudo rm nginx.conf
sudo aws s3 cp s3://BUCKET_NAME/nginx.conf .

Then, restart Nginx with the following command:

sudo service nginx restart

To make sure Nginx has permission to access our files execute this command:

chmod -R 755 /home/ec2-user

And then to make sure the service starts on boot, run this command:

sudo chkconfig nginx on

Now when you plug in the public IP of your web tier instance, you should see your website, which you can find on the Instance details page on the EC2 dashboard. If you have the database connected and working correctly, then you will also see the database working. You’ll be able to add data. Be careful with the delete button, that will clear all the entries in your database.

page1
page2

External Load Balancer and Auto Scaling:

Repeat the same procedure that you did to the App tier to create a load balancer, target groups, and ASG for the Web Tier. Make sure you map the correct VPC, subnets, and security groups to achieve the same.

Check the DNS of the external Load Balancer, if you get the same web page then Congratulate yourself, that you have successfully deployed the AWS 3-tier architecture.

I had issues in getting the result from the external load balancer. I got a 504 connection timeout error. It is just because of the security group's inbound and outbound rules. The internet-facing security group should allow HTTP traffic from anywhere and should pass the traffic to the web tier Security Group.

Clean Up:

Nat Gateway and Aurora Database are the most expensive services used in this project. So please clean up all the resources in the following order to avoid incurring any other charges.

Start with Auto Scaling Group, Application Load Balancer, Target Groups, Launch Template, AMIs, EC2 Instances, Aurora Database, DB Subnet Groups and Security Groups. Then using Cloudformation to delete the stack, it will remove all subnets, route tables, internet gateway, Nat Gateway, subnets, and VPC.

Thank you all for staying with me. I learned a lot in doing this project and thoroughly enjoyed the troubleshooting part. Feel free to share your feedback and applause 👏 as a token of appreciation.

Reference:

GitHub — aws-samples/aws-three-tier-web-architecture-workshop

LUIT3Tier/vpc1.yml at main · DevABM/LUIT3Tier

--

--