DEV Community

Cover image for Alta Disponibilidade e Escalabilidade com Load Balancer e Auto Scaling na AWS usando Terraform
Lucas Gabriel
Lucas Gabriel

Posted on

Alta Disponibilidade e Escalabilidade com Load Balancer e Auto Scaling na AWS usando Terraform

Publicar uma aplicação não é uma tarefa simples, dentre as incontáveis etapas que vão desde o projeto de engenharia de software até o desenvolvimento do código e os inúmeros casos de testes, ainda é necessário pensar na disponibilização para o usuário final, e é neste ponto que entra o tema que será abordado nesse post: Alta disponibilidade.
Através da AWS (Amazon Web Services) é possível encontrar variados recursos com os mais diversos casos de uso, nesse post estaremos falando sobre dois deles: o ELB (Elastic Load Balancer) e o ASG (Auto Scaling Group), enquanto também demonstramos a implementação de uma infraestrutura que utiliza esses recursos via Terraform. A seguir, apresentamos o diagrama do projeto proposto:
Image description
Para o provisionamento do ELB e do ASG, precisamos primeiramente criar alguns recursos que serão fundamentais para o funcionamento da infraestrutura, são eles: a criação de uma EC2 (Elastic Compute Cloud), disponibilização de uma VPC (Virtual Private Cloud), e a criação de uma AMI (Amazon Machine Image) que será usada pelo ASG.
Vamos iniciar pelo arquivo main.tf, onde iremos criar uma instância EC2 e uma AMI derivada dessa mesma instância.

provider "aws" {
  region = var.region
}

#Cria a instancia EC2
resource "aws_instance" "terraform" {
  ami                         = var.ami
  instance_type               = var.instance_type
  subnet_id                   = aws_subnet.public_subnet.id
  vpc_security_group_ids      = [aws_security_group.ec2_sg.id]
  associate_public_ip_address = true
  tags = {
    Name = "${var.nome_instancia}"
  }

}

resource "aws_ebs_snapshot" "ec2_snapshot" {
  volume_id = aws_instance.terraform.root_block_device[0].volume_id
}

#Cria uma AMI com base na EC2 criada anteriormente
resource "aws_ami" "ami_app" {
  name                = var.nome-ami
  description         = var.descricao-ami
  root_device_name    = var.root-device-ami
  virtualization_type = var.tipo-virtualizacao

  ebs_block_device {
    device_name = var.root-device-ami
    snapshot_id = aws_ebs_snapshot.ec2_snapshot.id
    volume_size = 10
    delete_on_termination = true
  }

  tags = {
    Name = var.nome-ami
  }
}

#Associa um IP elastico a uma instancia
resource "aws_eip" "eip" {
  instance = aws_instance.terraform.id
}

#Exibe o IP publico associado
output "IP" {
  value = aws_eip.eip.public_ip

}
Enter fullscreen mode Exit fullscreen mode

Logo em seguida criamos o variables.tf,que conterá as seguintes linhas:

variable "region" {
  type        = string
  description = "Regiao"
  default     = "us-east-1"

}

variable "ami" {
  type        = string
  description = "Imagem"
  default     = "ami-053b0d53c279acc90"
}

variable "nome_instancia" {
  type        = string
  description = "Nome da instancia"
  default     = "terraform-app"

}


variable "nome-ami" {
  type        = string
  description = "AMI gerada pelo recurso aws_ami "
  default     = "ami-app-db"
}

variable "descricao-ami" {
  type        = string
  description = "AMI criada a partir da EC2 "
  default     = "AMI criada a partir da EC2"
}

variable "root-device-ami" {
  type        = string
  description = "Disco raiz da AMI"
  default     = "/dev/sda1"
}

variable "tipo-virtualizacao" {
  type        = string
  description = "Tipo de virtualizacao"
  default     = "hvm"
}

variable "instance_type" {
  type        = string
  description = "Tamanho instancia"
  default     = "t2.micro"

}

variable "lb_type" {
  type        = string
  description = "Tipo de lb"
  default     = "application"

}
Enter fullscreen mode Exit fullscreen mode

A seguir, criaremos o arquivovpc.tf, que será a rede responsável por alocar todos os nossos recursos criados, juntamente com seus respectivos grupos de segurança:

#Cria a VPC
resource "aws_vpc" "apptf_vpc" {
  cidr_block = "10.0.0.0/16" # Bloco de endereços IP da VPC

  tags = {
    Name = "tf-vpc"
  }
}

#Cria uma subnet pública
resource "aws_subnet" "public_subnet" {
  vpc_id     = aws_vpc.apptf_vpc.id
  cidr_block = "10.0.1.0/24" # Bloco de endereços IP da subnet
  availability_zone = "us-east-1a"
  tags = {
    Name = "public_subnet_a"
  }
}

#Cria uma subnet pública
resource "aws_subnet" "public_subnet_b" {
  vpc_id     = aws_vpc.apptf_vpc.id
  cidr_block = "10.0.2.0/24" # Bloco de endereços IP da subnet
  availability_zone = "us-east-1b"
  tags = {
    Name = "public_subnet_b"
  }
}

#Cria um grupo de segurança para a subnet pública
resource "aws_security_group" "public_security_group" {
  name_prefix = "public_security_group"
  vpc_id      = aws_vpc.apptf_vpc.id


   ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "public_security_group"
  }
}

#Cria um grupo de segurança para o ELB
resource "aws_security_group" "elb_sg" {
  name        = "elb_sg"
  description = "Acesso a partir do ELB"
  vpc_id      = aws_vpc.apptf_vpc.id

   ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "elb_sg"
  }
}


#Cria um grupo de segurança para o ASG
resource "aws_security_group" "asg_sg" {
  name        = "asg_sg"
  description = "Grupo de Seguranca do ASG"
  vpc_id      = aws_vpc.apptf_vpc.id

    ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "asg_sg"
  }
}

resource "aws_security_group" "ec2_sg" {
  name        = "ec2_sg"
  description = "Acesso"
  vpc_id      = aws_vpc.apptf_vpc.id

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "ec2_sg"
  }
}

#Cria uma subnet privada
resource "aws_subnet" "private_subnet" {
  vpc_id     = aws_vpc.apptf_vpc.id
  cidr_block = "10.0.3.0/24" # Bloco de endereços IP da subnet
  availability_zone = "us-east-1a"
  tags = {
    Name = "private_subnet_a"
  }
}

#Cria uma segunda subnet privada
resource "aws_subnet" "private_subnet_b" {
  vpc_id     = aws_vpc.apptf_vpc.id
  cidr_block = "10.0.4.0/24" # Bloco de endereços IP da subnet
  availability_zone = "us-east-1b"
  tags = {
    Name = "private_subnet_b"
  }
}

# Cria um gateway de internet para a VPC
resource "aws_internet_gateway" "gateway_internet" {
  vpc_id = aws_vpc.apptf_vpc.id

  tags = {
    Name = "gateway_internet"
  }
}

#Cria uma rota para permitir o tráfego da subnet pública para a Internet
resource "aws_route_table" "public_route_table" {
  vpc_id = aws_vpc.apptf_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gateway_internet.id
  }

  tags = {
    Name = "public_route_table"
  }
}

#Associa a subnet pública com a tabela de rotas pública
resource "aws_route_table_association" "public_association" {
  subnet_id      = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_route_table.id
}

#Associa a subnet pública B com a tabela de rotas pública
resource "aws_route_table_association" "public_association_b" {
  subnet_id      = aws_subnet.public_subnet_b.id
  route_table_id = aws_route_table.public_route_table.id
}
Enter fullscreen mode Exit fullscreen mode

Vamos agora à criação das peças centrais deste post: o ELB e o ASG. Começaremos pelo elb.tfque será o responsável por fazer o balanceamento das requisições entre as instancias nas AZs (Availability Zones) configuradas, criaremos então o arquivo elb.tf:

resource "aws_lb" "tf_alb" {
  name               = "apprds-lb-tf"
  internal           = false
  load_balancer_type = var.lb_type
  security_groups    = [aws_security_group.elb_sg.id]
  subnets            = [aws_subnet.public_subnet.id, aws_subnet.public_subnet_b.id]  # IDs das sub-redes

  tags = {
    Name = "app-db-alb"
  }
}

#Cria o target group do ALB
resource "aws_lb_target_group" "alb_target" {
  name     = "target-group"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.apptf_vpc.id 

  health_check {
    path                = "/"
    interval            = 30
    timeout             = 10
    healthy_threshold   = 3
    unhealthy_threshold = 3
  }
}

#Associa a instancia ao target group do ALB
resource "aws_lb_target_group_attachment" "intance_attachment" {
  target_group_arn = aws_lb_target_group.alb_target.arn
  target_id        = aws_instance.terraform.id
  port             = 80
}


#Cria o listener group do ALB
resource "aws_lb_listener" "alb_listener" {
  load_balancer_arn = aws_lb.tf_alb.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    target_group_arn = aws_lb_target_group.alb_target.arn
    type             = "forward"
  }
}

#Exibe o endpoint do LB
output "elb_dns_name" {
  value = aws_lb.tf_alb.dns_name
}
Enter fullscreen mode Exit fullscreen mode

Agora, por fim, prosseguimos com a criação do asg.tf , que basicamente será o responsável pela escalabilidade da aplicação, adicionando e removendo as instancias conforme a necessidade:

#Configuracao do launch template
resource "aws_launch_template" "lt_asg" {
  name_prefix   = "launch-template"
  image_id      = aws_ami.ami_app.id 
  instance_type = var.instance_type 
  vpc_security_group_ids = [aws_security_group.ec2_sg.id]
}

#Cria o recurso do ASG
resource "aws_autoscaling_group" "asg" {
  name             = "asg"
  min_size         = 2  
  max_size         = 4  
  desired_capacity = 2  
  health_check_type = "ELB"
  vpc_zone_identifier = [aws_subnet.public_subnet.id, aws_subnet.public_subnet_b.id] 


  launch_template {
    id      = aws_launch_template.lt_asg.id
    version = "$Latest"
  }

    tag {
    key                 = "Name"
    value               = "asg-terraform"
    propagate_at_launch = true
  }
}

#Cria uma associação do ASG com o ALB
resource "aws_autoscaling_attachment" "attachment_alb_asg" {
  autoscaling_group_name = aws_autoscaling_group.asg.id
  lb_target_group_arn    = aws_lb_target_group.alb_target.arn
}
Enter fullscreen mode Exit fullscreen mode

Após a adição dos recursos acima, agora vamos aplicar os recusos na AWS, iremos então executar alguns comandos, são eles:

terraform init # Usado para inicializar o Terraform
Enter fullscreen mode Exit fullscreen mode
terraform plan # Usado para visualizar as mudanças propostas pela configuração Terraform
Enter fullscreen mode Exit fullscreen mode

E por fim o:

terraform apply # Usado para criar a infraestrutura na AWS
Enter fullscreen mode Exit fullscreen mode

Dessa forma, teremos uma infraestrutura altamente disponível e escalável na AWS, capaz de garantir a continuidade operacional, minimizar interrupções e assegurar o acesso à aplicação, mesmo diante de falhas de hardware ou software.

Principais benefícios da utilização do Load Balancer e do Auto Scaling:

  • Elasticidade: O ASG permite o dimensionamento automático dos recursos provisionados conforme a demanda, aumentando em momentos de picos de tráfego e diminuindo durante períodos de baixa demanda. Isso possibilita a otimização de recursos e a redução de custos.
  • Alta Disponibilidade: Por meio das zonas de disponibilidade aplicadas ao ELB, é possível manter a aplicação operacional mesmo que um data center completo da AWS esteja inacessível.
  • Tolerância a falhas: Em caso de falha de uma instância, o ASG substitui automaticamente por uma nova, garantindo a disponibilidade contínua da aplicação.
  • Distribuição de Tráfego: O ELB realiza a distribuição de tráfego entre as instancias de maneira automática, assim evitando uma possível sobrecarga de um único servidor e uma potencial indisponibilidade da aplicação.

Por fim, conforme apresentado, pudemos acompanhar na prática a implementação de uma infraestrutura altamente disponível na AWS. Nesse contexto, utilizamos o Terraform, que tem se mostrado como uma ferramenta poderosa e cada vez mais essencial para um deploy eficaz e ágil, nesse caso, na nuvem da AWS.

Link do projeto:

Top comments (1)

Collapse
 
abidullah786 profile image
ABIDULLAH786

Welcome to dev.to, Lucas! 👋 Great to see your first post here. Keep up the fantastic work, and don't hesitate to share more of your insights and experiences. We're excited to have you as part of our community! Happy coding! 🚀