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:
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
}
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"
}
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
}
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
}
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
}
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
terraform plan # Usado para visualizar as mudanças propostas pela configuração Terraform
E por fim o:
terraform apply # Usado para criar a infraestrutura na AWS
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:
ELB e ASG
Projeto criado para publicação de um artigo no Medium abordando sobre alta disponibilidade e escalabilidade na AWS.
Alta Disponibilidade e Escalabilidade com Load Balancer e Auto Scaling na AWS usando Terraform
Latest comments (1)
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! 🚀