DEV Community

Cover image for How to deploy a high available and auto scale Apache Web Server on EC2 instances using AWS CloudFormation
lucianobm
lucianobm

Posted on

How to deploy a high available and auto scale Apache Web Server on EC2 instances using AWS CloudFormation

Image description

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
Enter fullscreen mode Exit fullscreen mode

  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

CloudFormation service

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”.

Create stack

Enter a stack name, the environment name and the IPv4 CIDR blocks for the VPC and the subnets, and click on “Next”.

Specify stack details

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Check events for stack formation

    aws cloudformation describe-stack-events --stack-name LUIT

    aws cloudformation describe-stack-events --stack-name LUIT2
Enter fullscreen mode Exit fullscreen mode

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.

ALB redirecting traffic to EC2 instance on us-east-1a

ALB redirecting traffic to EC2 instance on us-east-1c


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!

Top comments (0)