Building a Highly Available and Secure AWS VPC for Production Workloads


About the project
This document demonstrates how to create a VPC that you can use for servers in a production environment.
To improve resiliency, you deploy the servers in two Availability Zones using an Auto Scaling Group And an Application Load Balancer. For additional security, you deploy the servers in private subnets. The servers receive requests through the load balancer. The servers can connect to the internet by using a NAT gateway. To improve you deploy the NAT gateway in both Availability Zones.
Architecture Overview
The VPC has public subnets and private subnets in two Availability Zones.
Each public subnet contains a NAT gateway and a load balancer node.
The servers run in the private subnets, are launched and terminated using an Auto Scaling Group, and receive traffic from the load balancer.
The servers can connect to the internet by using the NAT gateway.
Architecture Diagram
Walkthrough of VPC Architecture
Creating VPC
Go to the VPC dashboard and click on “Create VPC”.
- Click on “VPC and more” as AWS will create a bunch of resources that are shown in “Preview”, then give a name (i.e. aws-prod-example) and IPv4 CIDR block will be 10.0.0.0/16.
- Select “No IPv6 CIDR” and select Default for Tenancy, then select 2 No. of AZs, 2 No. of public subnets, and 2 No. of private subnets.
- Select 1 per AZ for NAT gateways, we don’t want any VPC endpoints for this, then keep other options as it is and click on “Create VPC”.
- It will take some time to create resources and VPC, in that time keep an eye on what is being created for the VPC.
- VPC is created.
Creating Auto Scaling Group
Go to the “EC2” dashboard and you will find Auto Scaling Groups, click on “Create Auto Scaling Group”.
We will need a launch template to configure further, a launch template defines instance settings (such as AMI, instance type, key pair, security groups, and storage) used to create EC2 instances in an Auto Scaling group. These launch templates can be used again when creating different ASGs.
- Give a name and description for the launch template.
- We will go with “AWS Ubuntu Server AMI”.
- The Instance type will be “t2.micro” and select or create “Key Pair” (if you create a new key pair download it as we need it further).
- Configure the new security group as in the two images below, we will add “SSH on port 22 and TCP on port 8000” for inbound rules, then keep the other configuration as it is for volumes and click “Create launch template”.
- Back to the Auto Scaling Group page, select the newly created launch template, and click on “Next”.
- Select the VPC we created, choose 2 Availability Zones (AZs), and select 2 private subnets, as the EC2 instances will be launched in these private subnets across the two AZs, then click on “Next”.
- We will create a load balancer later and click on “Next” to step 4.
- Select 2 for the desired capacity which means we always maintain 2 EC2 instances keep running in the Auto Scaling Group.
- Min desired capacity will be 1 and Max desired capacity will be 4 that means that if the 2 desired EC2 instances currently running are fully utilized, the Auto Scaling Group will launch additional instances (up to 4) to meet the application's requirements.
- Keep step-5 and step-6 as it is and click “Create Auto Scaling Group”.
- The Auto Scaling Group will be created, and it will launch 2 EC2 instances across 2 Availability Zones (AZs) within the private subnets of the VPC
Creating Bastion Host
We will create a bastion host and use it to SSH into our private subnet instances. A bastion host is a secure, publicly accessible server that provides access to EC2 instances in private subnets, acting as a gateway for SSH or RDP connections.
- In the Network settings, select the VPC and choose a public subnet. Ensure that “Auto-assign public IP” is enabled.
- Create a new security group that allows SSH access over port 22, then click “Launch instance”.
SSH Into Bastion Host and Private Ec2 Instance
Before SSH into the Bastion host, we need to copy the key pair of the private instances to the Bastion host so that we can log in to our private instances using the Bastion host.
- SSH into the Bastion host using the command shown below, then run
ls
to see the key pair file.
- SSH into the private EC2 instance through the bastion host using the command shown below.
- Create a simple Python HTML webpage as below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Private Subnet EC2</title>
</head>
<body>
<h1>AWS VPC Project to Demonstrate App in Private Subnet</h1>
</body>
</html>
Creating Application Load Balancer
Go to the EC2 dashboard and click “Create” for “Application Load Balancer”.
- Give your load balancer a name and select the option for it to be “internet-facing”.
- Select the VPC we created and choose 2 Availability Zones (AZs) with 2 public subnets.
- We can select any security group, but make sure to allow HTTP traffic on port 80 to provide internet access to the ALB.
- Click “Create target group” so that the ALB can route traffic to the private EC2 instances.
- The Target type will be “Instances”.
- Give the target group a name and select HTTP for port 8000, which will serve our webpage configured in the EC2 instance. Then, select the VPC.
- Register our 2 running EC2 instances as targets.
- Click “Create target group”.
- Now, on the load balancer page, select the target group we created.
- Click “Create load balancer”.
- We might encounter an error if the load balancer's security group doesn’t allow traffic over HTTP on port 80. To resolve this, add that rule to the security group of our load balancer.
- Access the webpage using the DNS name provided by the load balancer.
Testing the Application Load Balancer
Now, to test our load balancer, I have created a new target group that allows traffic over HTTP on port 80, with both EC2 instances as targets.
Execute the commands mentioned below to run the Apache server on both EC2 instances using the bastion host and then access the DNS of the load balancer again.
sudo apt update -y sudo apt install -y apache2 sudo systemctl start apache2 sudo systemctl enable apache2 echo "<h1>Hello World from $(hostname -f)</h1>" | sudo tee /var/www/html/index.html
- Refresh the webpage, and you will be able to see the private IP addresses of both instances, as the ALB is routing traffic across both EC2 instances. The load balancer will alternate requests between the instances, showing the corresponding IP address.
Conclusion
In this guide, we successfully built a production-ready AWS VPC architecture with high availability, scalability, and security best practices. By deploying our servers across multiple Availability Zones, utilizing an Auto Scaling Group, and securing our private instances with a bastion host and NAT gateway, we created a resilient infrastructure. The Application Load Balancer efficiently distributed traffic to our private EC2 instances, ensuring seamless access while maintaining security. This setup provides a strong foundation for running applications in a secure and scalable AWS environment, making it ideal for real-world production workloads.
Subscribe to my newsletter
Read articles from Rutvik Mangukiya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
