Setting up a three-tier architecture on AWS
Tags: aws, architecture, amazon, webservice
Published: 2022-09-18 16:44:39

Today, I am going to demonstrate how to use Virtual Private Cloud (VPC) services to setting up a three-tier architecture on Amazon Web Service (AWS). The architecture diagram below shows the setup, which is mainly divided into three layers.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezn1facl11yr6k1q9iyg.png)

The first layer is the presentation layer. Users can directly access the public subnet through the gateway. The second layer is the logic layer, which mainly deals with business logic. In this layer, I put it on the private subnet to limit the access and put it behind the load balancer, which can be flexibly and horizontally expanded to handle different time periods with different traffic demands. The third layer is the data layer. In this private subnet, the MySQL database is deployed and only allowed access through the second layer. To improve availability, I deploy to two Availability Zones, and the database is backed up to the other Availability Zone, ensuring that in the event of a loss of one Availability Zone, application services can still be maintained.

First, I will create a VPC network, which I call `victorleungtwdemo`. According to the architecture diagram, I choose `172.17.0.0/16` and my large subnet segment has been created successfully. Notice here `/16` subnet has a number of IP addresses 65535. If I set the subnet to `/24`, that is only 256 IP addresses. If I set it to the `/28` subnet, it would be only 16 IP addresses, which would not be enough for future expansion.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8dqv2phs9b7rvyy6l456.png)

Next, I will create the subnets, I am going to create six subnets, the first subnet is called `pub-subnet-1`, and I must choose the previous created VPC, and then choose the right availability zone A. I first choose area A, then choose the right range of IP addresses. The VPC set-up as a `/16` bit subnet and in order to consider scalability, I cannot block the subnet as it would have no scalability, therefore let me set a `/24` subnet instead.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvt8q31i4nqsambct0jf.png)

And so on and so forth, I create the remaining five subnets. You can take a look at the diagram below, I have created six subnets. You can see the public subnet 1, the public subnet 2, the private subnets 1, 2, 3 and 4 as well as the corresponding Availability zones.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kb2swtx2cixsyr2u7gn.png)

Next, I would create the corresponding gateway. I create a new gateway `victorleungtw-igw`.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/grmxhbqqs5lfcu4qtq29.png)

After it is created, I can assign it to my VPC. Which VPC should I choose? Let's choose `victorleungtwdemo` VPC.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shf0fxs1ysx8haqzc22k.png)

Then you can take a look at the routing table. You can see that there is a default routing table after the VPC is created, which is the main routing table. You can click it and see the routing entries. When you create any route in the table, it has a default routing entry, which is also the first entry, that is to say, the subnets I create are all connected by default. You cannot delete or change this route.

Let's create a new routing table, the public `pub-route` can connect data to the public network and choose my `victorleungtwdemo` vpc, I will change the name of the original routing table to `priv-route`. For the subnet of the database, there should be no communication to this subnet from the Internet. Create another routing table, I call it `nat-route`, and I have chosen the right `victorleungtwdemo` VPC as well.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vozc0rpths1nt7zsaiq.png)

At the moment, I already have three routing tables. You can see that it has a default route after creation.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n6hx4cwvdhfi4svdhlfu.png)

Let's specify the routes related to the public routing table `pub-route` and add a route `0.0.0.0/0` to the internet. I refer it to target `victorleungtw-igw` and then click save. In other words, all machines in this subnet will be able to access the public internet.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgs9f1clzuwlnpbat0r4.png)

Next, I am going to associate my public subnets. Which are my public subnets? Add `pub-subnet-1` and `pub-subnet-2`.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/58v0md7h7j3vnelwu4sx.png)

Then I add `nat-route`, and NAT related to `priv-subnet-1` and `priv-subnet-2`.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zo8ci03x61x4r5j7503g.png)

Lastly, I don’t need to worry about the remaining private routing table, since all subnets will go to the default routing table `priv-route`.

Then I will create a Network Address Translation (NAT) gateway. Let's choose a subnet and I choose `priv-subnet-2`. Because NAT must be allocated with Elastic IP (EIP), so I first create an EIP, and then we create a NAT gateway and add it to the routing table.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rld6zmhta4y9wdjdsr58.png)

Which routing table do we have? It is called the NAT routing table, add `0.0.0.0/0` from the nat gateway.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q8xk7ierq4xma3dqabel.png)

So far, I have almost completed the network architecture with about 70 per cent. Next step, let's set up all the related security group settings. The security group must be bound to my VPC. When I create a VPC, there is a default security group. For example, I am going to use security group for my bastion and load balancer. My bastion allows only specific IP to access SSH port 22, and then my load balancer provides HTTP services to the outside world, so I have to open port 80. Then my web service should be allowed to be accessed by the load balancer, and a security group should also be created for my web server. Finally, for my database layer, I do not allow the bastion to access directly, but only the web layer is allowed to access my database.

Create `bastion-sg` which allows for ssh. Since it's for development purpose, I am going to set a wider range to `0.0.0.0/0`, if it's production use, you may want to whitelist to your own IP address only.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/18iwc06he4ep9b8lohsv.png)

Create `elb-sg` which allows HTTP from anywhere.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8pc4i3euuvjm6e7s1gl6.png)

Create `web-sg` which allows traffic from my trusted `elb-sg` and `bastion-sg`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozeubpaszajlvfeu7h7f.png)

Create `db-sg` which mysql 3306 traffic from `web-sg` only.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oh3i36xwtj8fd7faafod.png)

Next, I need to start the relevant machines. Go to EC2, and use AMI Linux. Select, my VPC, let's create the bastion first, and I need the subnet corresponding to the bastion, `pub-subnet-1`. Enable auto-assign public IP and select the `bastion-sg` security group as shown in the diagram below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/glszup9ics8m48oz1vf4.png)

Create `webserver1` in `priv-subnet-1` with security group `web-sg`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jg3qc9ntj4bfy1ajakd5.png)

Create `webserver2` in `priv-subnet-2` with security group `web-sg`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a6zdrp9yruo8chqt5m1z.png)

After creating all the web servers, I have to create an RDS server. Before creating the RDS server, I also need to create an RDS subnet group. Why? RDS is a little different from the EC2 setting we just set. When I create a new RDS, it does not allow me to choose which VPC. So we create another layer on existing subnet. Create `victorleungtw-db` subnet group, then select the subnet `172.17.4.0/24` and `172.17.5.0/24` as shown in the screenshot below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/loqlh4fmhm724mtx828g.png)

Next, create the database server as shown in the diagram below, notice we select the right vpc and the `db-sg` security group.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/anfks0a72qbsqpd19ogd.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dedafhd0bajtw3rluj20.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kth1kk3amekd8q384rof.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7qx3mlgb5z6xgr1l5ib.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwgkn558i316b88ot62p.png)

Coming back to the EC2 instances, only the bastion server got public IP for access, while webserver 1 and 2 have no public IP address. I need to setup the target group for the webservers. Create a new target group `victorleungtw-tg` and add the two web servers as shown in the diagram below:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1oysinfusuwt3c2sv64d.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htyi10qvvdivba0qb11n.png)

Then I would create a load balancer. Select application load balancer with name `victorleungtw-alb` and map to `pub-subnet-1` and `pub-subnet-2`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yuf7j8wxmnxoqiostiyz.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/poxotxdxk9nqi38r2y78.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yrkfrjht5q1drhr7g1ln.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3o9o5gszk8j9l0cww47d.png)

Finally, everything is set up, we can test it out. Open a new terminal, ssh to the bastion host. Then from the bastion host, you could further ssh to webserver 1 and webserver 2 respectively. Start the Nginx server, and open a browser to access the elb url. You can verify by refreshing the page and hitting to the 2 servers with load balancing. From the web server, you can further verify to access the database from the application layer, but not from the bastion server layer.

That's it for setting up a 3 layer architecture on AWS. Let me know if you got any questions, [feel free to connect](https://www.linkedin.com/in/victorleungtw)