How to deploy a high available and auto scale Apache Web Server on EC2 instances using AWS CloudFormation
lucianobm
Posted on January 12, 2023
Introduction
This article will guide you deploy a high available and auto scale Apache Web Server on EC2 instances using AWS CloudFormation on both the AWS Console and AWS CLI.
To achieve this, we will create the following resources:
VPC with custom CIDR block
3 Public Subnets in 3 different availability zones
Internet Gateway
Security Group
LaunchTemplate
Auto Scaling Group
Application Load Balancer
Scaling UP and DOWN policy
Let the fun begin!
According to AWS CloudFormation best practices documentation, it is recommended to break the templates into smaller manageable templates. So I splitted the deploy on 2 different template files vpc.yaml and asg_alb.yaml listed below.
Let’s go through each item of the template:
Description is a text string to describe the purpose of the template.
Parameters are used to pass custom values at runtime. It is how we can make templates reusable to customize our stacks when we create them.
On the example below (vpc.yaml), we are including the name of the environment (that will be used later to tag the resources), the IPv4 CIDR of the VPC and subnets.
Parameters:
EnvName:
Description: Name that will be used on resources
Type: String
Default: LUIT
VPCCIDR:
Description: Please enter the IPv4 CIDR for this VPC
Type: String
Default: 10.10.0.0/16
PublicSubnetACIDR:
Description: Please enter the IPv4 CIDR for this Public Subnet A
Type: String
Default: 10.10.1.0/24
PublicSubnetBCIDR:
Description: Please enter the IPv4 CIDR for this Public Subnet B
Type: String
Default: 10.10.2.0/24
PublicSubnetCCIDR:
Description: Please enter the IPv4 CIDR for this Public Subnet C
Type: String
Default: 10.10.3.0/24
- Resources are the only required object of a template. We are including the VPC with the “DnsSupport” and “DnsHostnames” options enabled, an Internet Gateway that will be attached to the new VPC created, the 3 public subnets and the default route to 0.0.0.0/0 using the Internet Gateway created.
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref EnvName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Ref EnvName
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PublicSubnetACIDR
MapPublicIpOnLaunch: true
AvailabilityZone: "us-east-1a"
#AvailabilityZone: !Select [ 0, !GetAZs '' ]
Tags:
- Key: Name
Value: !Sub ${EnvName} Public Subnet A
PublicSubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PublicSubnetBCIDR
MapPublicIpOnLaunch: true
AvailabilityZone: "us-east-1b"
#AvailabilityZone: !Select [ 1, !GetAZs '' ]
Tags:
- Key: Name
Value: !Sub ${EnvName} Public Subnet B
PublicSubnetC:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PublicSubnetCCIDR
MapPublicIpOnLaunch: true
AvailabilityZone: "us-east-1c"
#AvailabilityZone: !Select [ 2, !GetAZs '' ]
Tags:
- Key: Name
Value: !Sub ${EnvName} Public Subnet C
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: !Sub ${EnvName} Public Route Table
VpcId: !Ref VPC
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicRouteTable
PublicSubnetARouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetA
PublicSubnetBRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetB
PublicSubnetCRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnetC
Lastly, we are including the Outputs section. It is optional but important to print the information of some resources that we might need to know or to use on any other stack.
On the example below, we are printing and exporting the ID of created VPC and subnets, and the CIDR of public subnets created.
Outputs:
VPC:
Description: ID of created VPC
Value: !Ref VPC
Export:
Name: VPC
PublicSubnets:
Description: CIDR of public subnets created
Value: !Join [ ",", [ !Ref PublicSubnetACIDR, !Ref PublicSubnetBCIDR, !Ref PublicSubnetCCIDR ] ]
PublicSubnetA:
Description: ID of created public subnet A
Value: !Ref PublicSubnetA
Export:
Name: PublicSubnetA
PublicSubnetB:
Description: ID of created public subnet B
Value: !Ref PublicSubnetB
Export:
Name: PublicSubnetB
PublicSubnetC:
Description: ID of created public subnet C
Value: !Ref PublicSubnetC
Export:
Name: PublicSubnetC
Below is the full vpc.yaml template that we will use to create the stack.
Now let’s go through each item of the asg_alb.yaml template:
We are including the following resources:
Security Group to allow inbound HTTP and SSH traffic from the Internet;
Launch Template with an Amazon Linux AMI, t2.micro instance type, my key pair “luciano” previous created, the security group created above and the UserData to install Apache Web Server;
Application Load Balancer, Target Group and Listener;
Auto Scaling Group with desired capacity of 2, minimum of 2 and maximum of 5.
Scale up and down policies with alarms to scale in case of the utilization of CPU goes above or beyond 50% for 10 minutes.
Also we are including an output section to print the DNS name of the Application Load Balancer.
Below is the full asg_alb.yaml template that we will use to create the stack.
Now let’s deploy the stacks using both the AWS Console and AWS CLI:
Using AWS Console
After logging into your AWS account, search for CloudFormation.
On the CloudFormation dashboard, click on “Create stack” button.
Select “Upload a template file” and click on “Choose file” button to browse the vpc.yaml file on your computer and then click on “Next”.
Enter a stack name, the environment name and the IPv4 CIDR blocks for the VPC and the subnets, and click on “Next”.
On Configure stack options section, we will leave everything as default and click on “Next”.
Review your stack details and click on “Submit” if everything seems correct.
You can repeat this process to create another stack using the asg_alb.yaml file.
Using AWS CLI
To create the stacks using AWS CLI, follow the steps below:
Validate our templates
aws cloudformation validate-template --template-body file://vpc.yaml
aws cloudformation validate-template --template-body file://asg_alb.yaml
Create stacks using our templates
aws cloudformation create-stack --stack-name LUIT --template-body file://vpc.yaml
aws cloudformation create-stack --stack-name LUIT2 --template-body file://asg_alb.yaml
Check if the stack we created via template is completed successfully
aws cloudformation list-stack-resources --stack-name LUIT
aws cloudformation list-stack-resources --stack-name LUIT2
Describe stack and its resources to view its properties
aws cloudformation describe-stacks --stack-name LUIT
aws cloudformation describe-stacks --stack-name LUIT2
aws cloudformation describe-stack-resources --stack-name LUIT
aws cloudformation describe-stack-resources --stack-name LUIT2
Check events for stack formation
aws cloudformation describe-stack-events --stack-name LUIT
aws cloudformation describe-stack-events --stack-name LUIT2
Now you should check if the Status is “CREATE_COMPLETE” and check the resources created.
If everything was created successfully, we will be able to reach the Application Load Balancer DNS on our browser and see the Apache running.
Success!
We have successfully created 2 stacks using AWS CloudFormation to deploy a high available and auto scale Apache Web Server running on EC2 instances.
If this step-by-step guide was helpful to you, click the like button and drop a comment below! I can’t wait to hear from you!
Posted on January 12, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 12, 2023