DEV Community

Cover image for Getting Started with AWS and Terraform: Optimizing Web Application Scalability with Auto Scaling and Terraform
Chinmay Tonape
Chinmay Tonape

Posted on • Updated on

Getting Started with AWS and Terraform: Optimizing Web Application Scalability with Auto Scaling and Terraform

In our previous post, we delved into the utilization of the Application Load Balancer (ALB) to enhance the resilience and scalability of a web application by distributing traffic among multiple Availability Zones (AZs) housing EC2 instances.

Building upon that foundation, this post focuses on implementing auto scaling using Terraform to further optimize scalability and ensure the continuous availability of our web application.

Architecture Overview

Before diving into the implementation details, let's take a moment to understand the architecture we'll be working with:

EC2 Auto Scaling Architecture

Step 1: Creating the AMI from Existing Template

Auto scaling requires a pre-built Amazon Machine Image (AMI). To facilitate this, we will use a YAML template provided in an AWS workshop and use cloudformation to create AMI. Download the YAML Template here.

Create CloudFormation Stack

Execute CloudFormation Stack

Running EC2 Instance

Create AMI from above EC2 Instance:

AMI Created

Step 2: Creating VPC and Network Components

Create VPC with IGW, 4 public subnets in separate AZs and route table with association. Please refer to my github repo in resources section below.

Step 3: Creating Linux EC2 Web Server Instances with Auto Scaling Group

Create a launch template using the AMI from Step 1.

####################################################
# Get AMI created earlier
####################################################
data "aws_ami" "amazon-linux-ami" {
  most_recent = true
  owners      = ["self"]
  filter {
    name   = "name"
    values = ["CT_Auto_Scaling_Webhost"]
  }
}
####################################################
# Create Launch Template Resource
####################################################
resource "aws_launch_template" "aws-launch-template" {
  image_id               = data.aws_ami.amazon-linux-ami.id
  instance_type          = var.instance_type
  key_name               = var.instance_key
  vpc_security_group_ids = var.security_group_ec2
  update_default_version = true
  tag_specifications {
    resource_type = "instance"
  }
  monitoring {
    enabled = true
  }
}
Enter fullscreen mode Exit fullscreen mode

Create an auto scaling group with a scaling policy, specifically the TargetTrackingScaling with the metric ASGAverageCPUUtilization. Auto scaling will be triggered if ASGAverageCPUUtilization exceeds 20%.

####################################################
# Create auto scaling group
####################################################
resource "aws_autoscaling_group" "aws-autoscaling-group" {
  #  name                = "${var.project_name}-ASG-Group"
  vpc_zone_identifier = tolist(var.public_subnets)
  desired_capacity    = 1
  max_size            = 4
  min_size            = 1

  launch_template {
    id      = aws_launch_template.aws-launch-template.id
    version = aws_launch_template.aws-launch-template.latest_version
  }
}

####################################################
# Create target tracking scaling policy for average CPU utilization
####################################################
resource "aws_autoscaling_policy" "avg_cpu_scaling_policy" {
  name                   = "avg_cpu_scaling_policy"
  policy_type            = "TargetTrackingScaling"
  autoscaling_group_name = aws_autoscaling_group.aws-autoscaling-group.name
  target_tracking_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ASGAverageCPUUtilization"
    }
    target_value = 20.0
  }
  estimated_instance_warmup = 180
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Creating an Application Load Balancer

Create an ALB with an attachment to auto scaling group

####################################################
# create application load balancer
####################################################
resource "aws_lb" "aws-application_load_balancer" {
  internal           = false
  load_balancer_type = "application"
  security_groups    = [var.security_group_alb[0]]
  //subnets                    = [var.public_subnets[0],var.public_subnets[1] ,var.public_subnets[2],var.public_subnets[3]]
  subnets                    = tolist(var.public_subnets)
  enable_deletion_protection = false
}
####################################################
# create target group for ALB
####################################################
resource "aws_lb_target_group" "alb_target_group" {
  target_type = "instance"
  port        = 80
  protocol    = "HTTP"
  vpc_id      = var.vpc_id

  health_check {
    enabled             = true
    interval            = 300
    path                = "/"
    timeout             = 60
    matcher             = 200
    healthy_threshold   = 5
    unhealthy_threshold = 5
  }

  lifecycle {
    create_before_destroy = true
  }
}

####################################################
# create a listener on port 80 with redirect action
####################################################
resource "aws_lb_listener" "alb_http_listener" {
  load_balancer_arn = aws_lb.aws-application_load_balancer.id
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.alb_target_group.id
  }
}

####################################################
# attach auto scaling group to Application Load Balancer ALB
####################################################
resource "aws_autoscaling_attachment" "asg_attachment_alb" {
  autoscaling_group_name = var.autoscaling_group_id
  lb_target_group_arn    = aws_lb_target_group.alb_target_group.arn
}
Enter fullscreen mode Exit fullscreen mode

Steps to run Terraform

terraform init
terraform plan 
terraform apply -auto-approve
Enter fullscreen mode Exit fullscreen mode

Upon successful completion, Terraform will provide relevant outputs.

Apply complete! Resources: 21 added, 0 changed, 0 destroyed.

Outputs:

alb_dns_name = "tf-lb-20240115192631610100000006-1207062642.us-east-1.elb.amazonaws.com"
Enter fullscreen mode Exit fullscreen mode

Testing the Auto Scaling

Use the ALB DNS name to access the website. As only a single instance is running initially, all requests will be directed to that instance.

Desired EC2 Instances

Desired EC2 - Web page

Scale out: Induce CPU load to stress the CPU, triggering auto scaling to increase the number of instances.

Stress CPU

Scaled out instances

2nd EC2

3rd EC2

4th EC2

Auto Scaling Group activity log to achieve maximum state:

ASG Scale out log

Scale in: Reboot EC2 instance with high CPU utilization to trigger scaling in and achieve the desired value.

Reboot Induced EC2

Instances terminated to achieve desired state:
Scaling out

Only EC2

Auto Scaling Group activity log to achieve desired state:

ASG Scale in log

Cleanup:

Remember to stop AWS components to avoid large bills.

terraform destroy -auto-approve
Enter fullscreen mode Exit fullscreen mode

Congratulations! You have successfully deployed and tested auto scaling for a web server application. In the upcoming module, we will explore Elastic Block Store (EBS) with multi-attach capabilities to further enhance the robustness of our architecture. Happy Coding!

Resources:

Github Link: https://github.com/chinmayto/terraform-aws-linux-webserver-ec2-alb-autoscaling
EC2 Auto Scaling: https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html

Top comments (0)