9. Load Balancer & Auto Scaling Group
Finally, let's create a webserver cluster that will consist of:
an Application Load Balancer that will forward traffic to
a Target Group (collection of EC2 instances, built from custom AMI) managed by
an Auto Scaling Group
terraform/webserver-cluster/variables.tf
variable "server_port" {
description = "The port the server will use for HTTP requests"
type = number
default = 8080
}
variable "cluster_min_size" {
description = "Minimum number of EC2 instances in Auto Scaling Group"
type = number
default = 3
}
variable "cluster_max_size" {
description = "Maximum number of EC2 instances in Auto Scaling Group"
type = number
default = 6
}
terraform/webserver-cluster/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.62.0"
}
}
required_version = ">= 1.0.8"
}
provider "aws" {
region = "eu-central-1"
}
data "terraform_remote_state" "network" {
backend = "local"
config = {
"path" = "../network/terraform.tfstate"
}
}
data "terraform_remote_state" "iam" {
backend = "local"
config = {
"path" = "../iam/terraform.tfstate"
}
}
data "terraform_remote_state" "secrets" {
backend = "local"
config = {
"path" = "../secrets/terraform.tfstate"
}
}
data "terraform_remote_state" "database" {
backend = "local"
config = {
"path" = "../database/terraform.tfstate"
}
}
data "aws_ami" "node_app" {
filter {
name = "name"
values = ["node-app-*"]
}
owners = ["self"]
most_recent = true
}
locals {
vpc_id = data.terraform_remote_state.network.outputs.vpc_id
iam_instance_profile = data.terraform_remote_state.iam.outputs.ec2_instance_profile_name
}
resource "aws_security_group" "public" {
vpc_id = local.vpc_id
ingress {
description = "Allow HTTP from everywhere"
protocol = "tcp"
from_port = 80
to_port = 80
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "Allow outbound traffic on all ports"
protocol = "-1"
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "private" {
vpc_id = local.vpc_id
ingress {
protocol = "tcp"
from_port = var.server_port
to_port = var.server_port
security_groups = [aws_security_group.public.id]
}
egress {
description = "Allow outbound traffic on all ports"
protocol = "-1"
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_launch_configuration" "webserver" {
image_id = data.aws_ami.node_app.id
instance_type = "t2.micro"
iam_instance_profile = local.iam_instance_profile
security_groups = [aws_security_group.private.id]
user_data = templatefile(
"./user_data.sh",
{
port = var.server_port,
db_secert_arn = data.terraform_remote_state.secrets.outputs.db_secert_arn,
db_endpoint = data.terraform_remote_state.database.outputs.endpoint
}
)
lifecycle {
# reference used in ASG launch configuration will be updated after creating a new resource and destroying this one
create_before_destroy = true
}
}
resource "aws_lb_target_group" "asg" {
name = "webserver-cluster"
port = var.server_port
protocol = "HTTP"
vpc_id = local.vpc_id
health_check {
path = "/"
protocol = "HTTP"
matcher = "200"
interval = 15
timeout = 3
healthy_threshold = 2
unhealthy_threshold = 2
}
}
resource "aws_autoscaling_group" "asg" {
# Explicitly depend on the launch configuration's name so each time it's replaced, this ASG is also replaced
name = aws_launch_configuration.webserver.name
launch_configuration = aws_launch_configuration.webserver.name
vpc_zone_identifier = data.terraform_remote_state.network.outputs.private_subnet_ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
min_size = var.cluster_min_size
max_size = var.cluster_max_size
# Wait for at least this many instances to pass health checks before considering the ASG deployment complete
min_elb_capacity = var.cluster_min_size
# When replacing this ASG, create the replacement first, and only delete the original after
lifecycle {
create_before_destroy = true
}
tag {
key = "Name"
value = "TerraformWorkshopsWebserver"
propagate_at_launch = true
}
}
resource "aws_lb" "alb" {
name = "alb"
load_balancer_type = "application"
subnets = data.terraform_remote_state.network.outputs.public_subnet_ids
security_groups = [aws_security_group.public.id]
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.alb.arn
port = 80
protocol = "HTTP"
default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "404: page not found"
status_code = 404
}
}
}
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
}
}
terraform/webserver-cluster/user_data.sh
#!/bin/bash
su ubuntu -c 'export PORT=${port} SECRET_ID=${db_secert_arn} DB_ENDPOINT=${db_endpoint} && nohup ~/.nvm/versions/node/v16.3.0/bin/node ~/app/index.js &'
terraform/webserver-cluster/outputs.tf
@@ -1,7 +1,4 @@
-output "public_ip_address" {
- value = aws_instance.public.public_ip
-}
-
-output "private_ip_address" {
- value = aws_instance.private.private_ip
+output "alb_dns_name" {
+ value = aws_lb.alb.dns_name
+ description = "The domain name of the load balander"
}
Apply.
Last updated
Was this helpful?