Managing high-traffic applications with AWS Elastic Load Balancer and Terraform

🌞 This article, will explain in detail the concepts of scaling infrastructure and the importance of managing Terraform state and Understanding how the different blocks operate and when to use them 🌞

🌐Synopsis:

🖋️Understand the concepts of scaling infrastructure and how the different block operate


🌐Flow Diagram:

ALB


🌐Deploying a Load Balancer:

🖋️At this point, you can deploy your ASG, but you’ll have a small problem: you now have multiple servers, each with its own IP address, but you typically want to give your end users only a single IP to use. One way to solve this problem is to deploy a load balancer to distribute traffic across your servers and to give all your users the IP (actually, the DNS name) of the load balancer. Creating a load balancer that is highly available and scalable is a lot of work. Once again, you can let AWS take care of it for you, this time by using Amazon’s Elastic Load Balancer (ELB) service

🖋️AWS offers three types of load balancers:

  1. Application Load Balancer (ALB) is best suited for load balancing of HTTP and HTTPS traffic. Operates at the application layer (Layer 7) of the Open Systems Interconnection (OSI) model.

  2. Network Load Balancer (NLB) is best suited for load balancing of TCP, UDP, and TLS traffic. Can scale up and down in response to load faster than the ALB (the NLB is designed to scale to tens of millions of requests per second). Operates at the transport layer (Layer 4) of the OSI model.

  3. Classic Load Balancer (CLB) This is the “legacy” load balancer that predates both the ALB and NLB. It can handle HTTP, HTTPS, TCP, and TLS traffic but with far fewer features than either the ALB or NLB. Operates at both the application layer (L7) and transport layer (L4) of the OSI model. Most applications these days should use either the ALB or the NLB.

  • Listener Listens on a specific port (e.g., 80) and protocol (e.g., HTTP).

  • Listener rule Takes requests that come into a listener and sends those that match specific paths (e.g., /foo and /bar) or hostnames (e.g., foo.example.com and bar.example.com) to specific target groups.

  • Target groups One or more servers that receive requests from the load balancer. The target group also performs health checks on these servers and sends requests only to healthy nodes

  • Listener, Listener Rule and TG

    🖋️The first step is to create the ALB itself using the aws_lb resource

  •    resource "aws_lb" "example" {
        name   = "terraform-asg-example"
        load_balancer_type = "application"
        subnets  = data.aws_subnets.default.ids
       }
    

    🖋️The next step is to define a listener for this ALB using the aws_lb_listener resource:

  •    resource "aws_lb_listener" "http" {
        load_balancer_arn = aws_lb.example.arn
        port   = 80
        protocol = "HTTP
       # By default, return a simple 404 page
       default_action {
          type = "fixed-response"
       fixed_response {
            content_type = "text/plain"
            message_body = "404: page not found"
            status_code = 404
          }
        }
      }
    

    🖋️Need to create a new security group specifically for the ALB. This security group should allow incoming requests on port 80 so that you can access the load balancer over HTTP, and allow outgoing requests on all ports so that the load balancer can perform health checks

  •   resource "aws_security_group" "alb" {
        name = "terraform-example-alb"
        # Allow inbound HTTP requests
       ingress {
          from_port   = 80
          to_port    = 80  
          protocol   = "tcp"  
          cidr_blocks = ["0.0.0.0/0"]
        }
        # Allow all outbound requests
       egress {
          from_port  = 0 
          to_port    = 0  
          protocol   = "-1"  
          cidr_blocks = ["0.0.0.0/0"]
        }
       }
    

    🖋️We need to tell the aws_lb resource to use this security group via the

  • security_groups argument

  •   resource "aws_lb" "example" {
        name = "terraform-asg-example"              
        load_balancer_type = "application"
        subnets = data.aws_subnets.default.ids           
        security_groups = [aws_security_group.alb.id]   
      }
    

    🖋️Create a target group for your ASG using the aws_lb_target_group resource:

  •    resource "aws_lb_target_group" "asg" {
        name  = "terraform-asg-example"   
        port = var.server_port    
        protocol = "HTTP"
        vpc_id = data.aws_vpc.default.id   
       health_check {
          path  = "/"              
          protocol  = "HTTP"           
          matcher  = "200"           
          interval = 15            
          timeout  = 3            
          healthy_threshold = 2   
          unhealthy_threshold = 2
        }
       }
    

    🖋️Finally, it’s time to tie all these pieces together by creating listener rules using the aws_lb_listener_rule resource

  •    resource "aws_lb_listener_rule" "asg" {
        listener_arn = aws_lb_listener.http.arn
        priority = 100    
       condition {
       path_pattern {
            values = ["*"]
          }
        }
       action {
          type  = "forward"           
          target_group_arn = aws_lb_target_group.asg.arn
        }
       }
    

    🖋️Output has the DNS name of the ALB

  •    output "alb_dns_name" {
        value = aws_lb.example.dns_name      
        description = "The domain name of the load balancer"
       }
    

  • 🌐What Is Terraform State?

  • 🖋️Every time you run Terraform, it records information about what infrastructure it created in a Terraform state file. By default, when you run Terraform in the folder /foo/bar, Terraform creates the file /foo/bar/terraform.tfstate. This file contains a custom JSON format that records a mapping from the Terraform resources in your configuration files to the representation of those resources in the real world. For example, let’s say your Terraform configuration contained the following

  •   resource "aws_instance" "example" {
        ami = "ami-0fb653ca2d3203ac1"          
        instance_type = "t2.micro"
       }
    

🖋️After running terraform apply, here is a small snippet of the contents of the terraform .tfstate file

 {
 "version": 4,
 "terraform_version": "1.2.3",
 "serial": 1,
 "lineage": "86545604-7463-4aa5-e9e8-a2a221de98d2",
 "outputs": {},
 "resources": [
     {
     "mode": "managed",
     "type": "aws_instance",
     "name": "example",
     "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
     "instances": [
     {
     "schema_version": 1,
     "attributes": {
         "ami": "ami-0fb653ca2d3203ac1",
         "availability_zone": "us-east-2b",
         "id": "i-0bc4bbe5b84387543",
         "instance_state": "running",
         "instance_type": "t2.micro",
         "(...)": "(truncated)"
     }
  }
 ]
 }
 ]
 }

🖋️Using this JSON format, Terraform knows that a resource with type aws_instance and name example corresponds to an EC2 Instance in your AWS account with ID i-0bc4bbe5b84387543. Every time you run Terraform, it can fetch the latest status of this EC2 Instance from AWS and compare that to what’s in your Terraform configurations to determine what changes need to be applied. In other words, the output of the plan command is a diff between the code on your computer and the infrastructure deployed in the real world, as discovered via IDs in the state file


🌐Demo of AWS Application Load Balancer:

🖋️ Creation of security group for ELB and EC2

############ Creating Security Group for EC2 & ELB ############

resource "aws_security_group" "web-server" {
    name        = "web-server"
    description = "Allow incoming HTTP Connections"
    ingress {
        from_port   = 80
        to_port     = 80
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"
        cidr_blocks = ["0.0.0.0/0"]
    }
}

🖋️Creation of two EC2 instances

 ################## Creating 2 EC2 Instances ##################

resource "aws_instance" "web-server" {
    ami             = "ami-01cc34ab2709337aa"
    instance_type   = "t2.micro"
    count           = 2
    key_name        = "terraform-key"
    security_groups = ["${aws_security_group.web-server.name}"]
    user_data = <<-EOF
       #!/bin/bash
       sudo su
        yum update -y
        yum install httpd -y
        systemctl start httpd
        systemctl enable httpd
        echo "<html><h1> Welcome to Terraform Challenge. Happy Learning from 
        $(hostname -f)...</p> </h1></html>" 
           >> /var/www/html/index.html
        EOF
    tags = {
        Name = "instance-${count.index}"
    }
}

🖋️Adding the Default VPC and Subnet

###################### Default VPC and Subnets ######################

data "aws_vpc" "default" {
    default = true
}

data "aws_subnet" "subnet1" {
    vpc_id = data.aws_vpc.default.id
    availability_zone = "us-east-1a"
}

data "aws_subnet" "subnet2" {
    vpc_id = data.aws_vpc.default.id
    availability_zone = "us-east-1b"
}

🖋️Creation of target groups

#################### Creating Target Group ####################

resource "aws_lb_target_group" "target-group" {
    health_check {
        interval            = 10
        path                = "/"
        protocol            = "HTTP"
        timeout             = 5
        healthy_threshold   = 5
        unhealthy_threshold = 2
    }
    name          = "whiz-tg"
    port          = 80
    protocol      = "HTTP"
    target_type   = "instance"
    vpc_id = data.aws_vpc.default.id
}

🖋️Creation of ALB and Listener

############# Creating Application Load Balancer #############

resource "aws_lb" "application-lb" {
    name            = "terr-alb"
    internal        = false
    ip_address_type     = "ipv4"
    load_balancer_type = "application"
    security_groups = [aws_security_group.web-server.id]
    subnets = [
                data.aws_subnet.subnet1.id,
                data.aws_subnet.subnet2.id
                ]
    tags = {
        Name = "terr-alb"
    }
}

######################## Creating Listener ######################

resource "aws_lb_listener" "alb-listener" {
    load_balancer_arn          = aws_lb.application-lb.arn
    port                       = 80
    protocol                   = "HTTP"
    default_action {
        target_group_arn         = aws_lb_target_group.target-group.arn
        type                     = "forward"
    }
}

🖋️Attaching target group to ALB

################ Attaching Target group to ALB ################

resource "aws_lb_target_group_attachment" "ec2_attach" {
    count = length(aws_instance.web-server)
    target_group_arn = aws_lb_target_group.target-group.arn
    target_id        = aws_instance.web-server[count.index].id
}

🖋️Create an Output file

output "elb-dns-name" {
  value = aws_lb.application-lb.dns_name
}

🤩 Terraform output:

🌐Check the HTML page and traffic distribution

🖋️Below are the resources been created

🕵🏻I also want to express that your feedback is always welcome. As I strive to provide accurate information and insights, I acknowledge that there’s always room for improvement. If you notice any mistakes or have suggestions for enhancement, I sincerely invite you to share them with me.

🤩 Thanks for being patient and following me. Keep supporting 🙏

Clap👏 if you liked the blog.

For more exercises — please follow me below ✅!

https://vjraghavanv.hashnode.dev/

#aws #terraform #cloudcomputing #IaC #DevOps #tools #operations #30daytfchallenge #HUG #hashicorp #HUGYDE #IaC #developers #awsugmdu #awsugncr #automatewithraghavan

1
Subscribe to my newsletter

Read articles from vijayaraghavan vashudevan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

vijayaraghavan vashudevan
vijayaraghavan vashudevan

I'm Vijay, a seasoned professional with over 13 years of expertise. Currently, I work as a Quality Automation Specialist at NatWest Group. In addition to my employment, I am an "AWS Community Builder" in the Serverless Category and have served as a volunteer in AWS UG NCR Delhi and AWS UG MDU, a Pynt Ambassador (Pynt is an API Security Testing tool), and a Browserstack Champion. Actively share my knowledge and thoughts on a variety of topics, including AWS, DevOps, and testing, via blog posts on platforms such as dev.to and Medium. I always like participating in intriguing discussions and actively contributing to the community as a speaker at various events. This amazing experience provides me joy and fulfillment! 🙂