Compile outputs fun

AWS CloudFormation: 02 - Create an auto scaling group

Published 2 years agoAWS, CloudFormation

This is continued from AWS CloudFormation: 01 - Create a web server EC2 instance .

Background

I want to scale my web server horizontally.

What we need?

  1. A launch template to launch an EC2 instance when needed.
  2. A load balancer to route traffic to multiple EC2 instances.
  3. A auto scaling group to control the amount of running EC2 instances.

How to do it?

This is what we are going to do:

Open 01-webserver-template.yaml from previous post and save as 02-autoscaling-template.yaml .

Network

The load balancer requires multiple subnets, thus we need to create another subnet for our VPC. Modify these resources:

1   PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref VPC
      AvailabilityZone: ap-southeast-1a
Add the AvailabilityZone to the original subnet.

Then add these resources:

1   PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId: !Ref VPC
      AvailabilityZone: ap-southeast-1b
This is the new subnet. The server instances in this subnet will have the IP of 10.0.1.*.
2   PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet2
Associate the route table to the new subnet.

Launch Template

The auto scaling group will need to know how to launch an EC2 instance. This is defined as launch template in AWS. The structure is very similar to how we define the EC2 instance in last port. Modify WebServerInstance into this WebServerLaunchTemplate :

1   WebServerLaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateData:
        ImageId: ami-0d728fd4e52be968f
        InstanceType: t2.micro
The new EC2 instance will use Amazon Linux 2 and t2.micro like the EC2 instance in the previous post.
        NetworkInterfaces:
          - DeviceIndex: "0"
            AssociatePublicIpAddress: true
This is the network interface of the EC2 instance. The auto scaling group will decide the subnet for the EC2 instance so we don't need to specify it here. It will have a public IP.
            Groups:
              - !Ref WebServerSecurityGroup
Associate it to the security group we defined earlier so we only expose the ports we need. Note that the property name here is Groups instead of GroupSet.
        KeyName: !Sub ${SshKeyName}
        UserData: !Base64 |
          #!/usr/bin/env bash
          sudo yum update -y
          sudo amazon-linux-extras install -y nginx1
          sudo systemctl enable nginx
          sudo systemctl start nginx
    DependsOn: InternetRoute
The rest is just like what we did for the EC2 instance in the previous post.

Load Balancer

The load balancer will route the traffic from the Internet to the EC2 instances. Add these resources:

1   WebServerLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      SecurityGroups:
        - !Ref WebServerSecurityGroup
This is the load balancer. It will accept the same web server traffic as the EC2 instances so we just use the same security group as the EC2 instances. It doesn't handle SSH traffic.
      Subnets:
        - !Ref PublicSubnet
        - !Ref PublicSubnet2
It needs more than 1 subnet here to achieve high availability thus we defined 2 subnets for it.
2   WebServerLoadBalancerTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VPC
This is the target group of the load balancer. All the traffic will be routed to the port 80 of the EC2 instances in this target group. The auto scaling group will manage it.
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: "30"
This is the time needed for a EC2 instance to deregister itself from the target group. This is needed so when the auto scaling group terminate the EC2 instance, it still have enough time to process the active request.
3   WebServerLoadBalancerHttpListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref WebServerLoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref WebServerLoadBalancerTargetGroup
This is the listener of the load balancer. It will accept the HTTP request on port 80 and forwards it to the corresponding target group.

Auto Scaling Group

The auto scaling group will change the number of running EC2 instances on the fly. It can do that manually or through some predefined condition (aka auto scaling). Add this to the resources:

1   WebServerAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      LaunchTemplate:
        Version: !Sub ${WebServerLaunchTemplate.LatestVersionNumber}
        LaunchTemplateId: !Ref WebServerLaunchTemplate
This is the auto scaling group. It will use the launch template we defined earlier to launch a new EC2 instance.
      MinSize: 1
      MaxSize: 2
It will ensure the number of running EC2 instances will be between 1 and 2.
      VPCZoneIdentifier:
        - !Ref PublicSubnet
        - !Ref PublicSubnet2
It will launch the EC2 instances in these subnets.
      TargetGroupARNs:
        - !Ref WebServerLoadBalancerTargetGroup
It will register the EC2 instances in this target group so the load balancer can route traffic to them.

Output

There are multiple EC2 instances now so we can't rely on the IP of the EC2 instances to access our web server. We need to use the DNS name of the load balancer. Change the outputs to this:

1   Url:
    Description: URL of the web server instance
    Value: !Sub http://${WebServerLoadBalancer.DNSName}
This will construct a HTTP URL from the DNS name of the load balancer.

You can update the stack created in previous post with this stack. The CloudFormation will figure out the changes and know how to modify the existing resources and provision new resources.

Open the URL in a browser and you should see the NGINX welcome page. It might takes a while for the page to work because it takes time to spin up the EC2 instance.

Next we will do AWS CloudFormation: 03 - Collect logs .