Diferentes tipos de balanceadores de carga en AWS

Hello World 🙂
En la entrada de hoy vamos a explorar uno de los servicios más utilizados en AWS: Elastic Load Balancer (ELB). Veremos sus características, los diferentes tipos que existen y las múltiples opciones que nos brinda para gestionar el tráfico en nuestras aplicaciones de forma eficiente.
Según la documentación de AWS, Elastic Load Balancing (ELB) ayuda a distribuir automáticamente el tráfico de aplicaciones entrantes entre múltiples destinos, como instancias EC2, contenedores o direcciones IP, en una o varias zonas de disponibilidad (AZ). ELB garantiza que las cargas de trabajo se repartan equitativamente, mejorando la disponibilidad y el rendimiento de nuestras aplicaciones.
En el pasado, cuando queríamos implementar un balanceador de carga, teníamos que recurrir a diferentes soluciones de software, como NGINX o HAProxy, lo que suponía varios retos, desde estar atentos a las actualizaciones de los servidores donde estaban instalados hasta asegurar la escalabilidad manual. Hoy en día, gracias a AWS, crear un balanceador de carga es extremadamente sencillo, ya que tenemos varias opciones pre configuradas y administradas automáticamente por AWS, lo que nos permite olvidarnos de los desafíos de mantenimiento y centrarnos en nuestras aplicaciones.
AWS ofrece tres tipos principales de balanceadores de carga, y cada uno de ellos está diseñado para satisfacer casos de uso específicos. Vamos a revisar cada uno en detalle:
1. Application Load Balancer (ALB)
El Application Load Balancer está diseñado para dirigir el tráfico de nivel de aplicación (capas 7 del modelo OSI). Este tipo de balanceador es ideal para gestionar tráfico HTTP/HTTPS y está optimizado para enrutamiento avanzado, como solicitudes basadas en contenido (host o ruta) y distribuciones basadas en microservicios.
Características:
- Ideal para aplicaciones web, APIs y microservicios.
- Soporte para WebSockets.
- Distribución del tráfico basado en reglas (por host, ruta o cabeceras).
- Integración con AWS WAF para protección contra amenazas.
Caso de uso: Es ideal para aplicaciones que requieren un enrutamiento más inteligente y complejo a nivel de aplicación, como una web que sirva diferentes versiones en función de la URL o un API con múltiples microservicios detrás.
2. Network Load Balancer (NLB)
El Network Load Balancer está diseñado para gestionar grandes volúmenes de tráfico a nivel de red (capa 4 del modelo OSI), con un enfoque en la baja latencia. Este balanceador es perfecto para aplicaciones que necesitan manejar millones de solicitudes por segundo y requieren conexiones persistentes de baja latencia.
Características:
- Manejo de tráfico TCP/UDP de alta velocidad.
- Muy bajo tiempo de latencia, ideal para aplicaciones en tiempo real.
- Soporte para direcciones IP estáticas y Elastic IPs.
- Capacidad de manejar grandes cargas de tráfico con alta velocidad de procesamiento.
Caso de uso: Aplicaciones que requieren alta disponibilidad y baja latencia en la transferencia de datos a nivel de red, como servicios de juegos online, comunicaciones financieras y otros servicios en tiempo real.
3. Gateway Load Balancer (GWLB)
El Gateway Load Balancer está diseñado específicamente para gestionar la distribución de tráfico hacia dispositivos virtuales de red, como firewalls, sistemas de detección de intrusiones (IDS/IPS) y otros appliances de red. Proporciona la capacidad de escalar estos dispositivos sin complejidad adicional.
Características:
- Enrutamiento de tráfico hacia aplicaciones de seguridad y monitoreo.
- Escalabilidad y administración automática de dispositivos virtuales de red.
- Simplifica el despliegue de aplicaciones de terceros en AWS.
Caso de uso: Ideal cuando necesitamos integrar soluciones de seguridad o monitoreo en nuestra infraestructura, como firewalls virtuales o dispositivos de detección de intrusiones.
Hace un tiempo, AWS ofrecía el Classic Load Balancer, que permitía balancear tráfico tanto en la capa de red como en la capa de aplicación. Sin embargo, este tipo de balanceador ha sido descontinuado, ya que AWS lo ha reemplazado con las opciones más avanzadas mencionadas anteriormente.
En esta entrada, veremos un ejemplo 100% práctico utilizando Terraform para crear un Application Load Balancer (ALB) que distribuirá el tráfico a un grupo de instancias EC2. En cada instancia tendremos un servidor web básico que mostrará el nombre e IP de las instancias, lo que nos permitirá observar en el navegador cómo el ALB balancea las peticiones de forma automática entre las instancias. Así, podrás visualizar en tiempo real el comportamiento del balanceador de carga y cómo distribuye el tráfico, todo implementado y gestionado desde Terraform.
Para lograr este objetivo, crearemos primero una VPC que servirá como base para desplegar todos los recursos necesarios, como las instancias EC2, el ALB, grupos de seguridad (SG), y el grupo de destino (Target Group).
Requisitos:
- Tener una cuenta de AWS.
- Tener instalado Terraform.
El proyecto estará organizado en una única carpeta, dentro de la cual definiremos todos los archivos de Terraform necesarios para crear los recursos.
1- Primero necesitamos exportar nuestras credenciales de AWS en nuestra consola local;
export AWS_ACCESS_KEY_ID="XXXXXXXXXX"
export AWS_SECRET_ACCESS_KEY="XXXXXXXXXX"
- Comprobamos que estemos conectados de forma correcta
2- En el archivo 0-provider.tf, definiremos los proveedores que utilizaremos en este proyecto. Estos proveedores son esenciales, ya que permiten a Terraform interactuar con los distintos servicios de infraestructura en la nube necesarios para desplegar los recursos correctamente.
-
provider "aws" {
region = local.region
default_tags {
tags = local.tags
}
}
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.47"
}
}
}
3- En el archivo 1-locals.tf, definiremos las variables locales del proyecto. Estas variables ayudan a centralizar valores que se utilizarán en múltiples recursos, simplificando la configuración y facilitando el mantenimiento del código.
-
locals {
region = "us-west-1"
tags = {
Environment = "POC",
Terraform ="True"
}
}
4- En este archivo 2-vpc.tf, definiremos todo lo necesario para crear nuestra VPC, que servirá como la base de la infraestructura del proyecto. Aquí, definiremos subredes y demás componentes claves que permitirán una red segura y eficiente para los recursos que desplegaremos más adelante.
-
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "demo-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-1a", "us-west-1b"]
private_subnets = ["10.0.0.0/19", "10.0.32.0/19"]
public_subnets = ["10.0.64.0/19", "10.0.96.0/19"]
enable_dns_hostnames = true
enable_dns_support = true
enable_nat_gateway = true
single_nat_gateway = true
one_nat_gateway_per_az = false
}
5- En el archivo 3-ec2.tf, configuraremos todos los recursos necesarios para crear nuestras instancias EC2. Esto incluirá la creación de la key_pair
para permitir el acceso seguro a las instancias, así como la definición de un data source
que buscará automáticamente el AMI adecuado para desplegar instancias con Ubuntu. Este enfoque nos garantiza utilizar una imagen actualizada.
-
resource "aws_instance" "demo" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
iam_instance_profile = "${aws_iam_instance_profile.demo_profile.name}"
subnet_id = element(module.vpc.private_subnets, count.index % length(module.vpc.private_subnets))
vpc_security_group_ids = [aws_security_group.ec2_sg.id]
key_name = aws_key_pair.terraform_demo.id
user_data = <<-EOF
#!/bin/bash
echo "*** Installing apache2"
sudo apt update -y
sudo apt install apache2 -y
echo "*** Completed Installing apache2"
echo "*** Staring apache2"
sudo systemctl start apache2
echo "*** Enable apache2"
sudo systemctl enable apache2
echo "*** Edit file index.html"
echo "<h1>Hello world from - ${var.instance_names[count.index]}</h1>" | sudo tee /var/www/html/index.html
echo "<p>$(hostname -f)</p>" | sudo tee -a /var/www/html/index.html
EOF
tags = {
"Name" = var.instance_names[count.index]
}
}
resource "aws_key_pair" "terraform_demo" {
key_name = "terraform_demo"
public_key = "${file(var.public_key)}"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-*-20*"]
}
filter {
name = "architecture"
values = ["x86_64"]
}
owners = ["amazon"]
}
6- En el archivo 4-security.tf, configuraremos detalladamente todos los grupos de seguridad necesarios para el proyecto. Estos grupos de seguridad establecerán las reglas de entrada y salida que controlarán el tráfico hacia y desde nuestros recursos, asegurando una protección adecuada en cada nivel. Al definir estas reglas aquí, podremos gestionar de forma centralizada y eficiente los permisos de red requeridos por nuestras instancias y el Application Load Balancer.
-
resource "aws_security_group" "ec2_sg" {
name = "SG-EC2"
description = "SG-EC2"
vpc_id = module.vpc.vpc_id
ingress {
security_groups = [aws_security_group.alb_sg.id]
description = "Acceso al puerto 80 desde el exterior"
from_port = var.puerto_servidor
to_port = var.puerto_servidor
protocol = "TCP"
}
ingress {
cidr_blocks = ["0.0.0.0/0"]
description = "Acceso al puerto 22 desde el exterior"
from_port = var.puerto_ssh
to_port = var.puerto_ssh
protocol = "TCP"
}
egress {
cidr_blocks = ["0.0.0.0/0"]
description = "Salida full internet"
from_port = 0
to_port = 0
protocol = "-1"
}
}
#SG ALB
resource "aws_security_group" "alb_sg" {
name = "SG-ALB"
description = "SG-ALB"
vpc_id = module.vpc.vpc_id
ingress {
cidr_blocks = ["0.0.0.0/0"]
description = "Acceso al puerto 80 desde el exterior"
from_port = var.puerto_lb
to_port = var.puerto_lb
protocol = "TCP"
}
egress {
cidr_blocks = ["0.0.0.0/0"]
description = "Salida full internet"
from_port = 0
to_port = 0
protocol = "-1"
}
}
7- En el archivo 5-alb.tf, configuraremos todos los elementos necesarios para desplegar nuestro Application Load Balancer (ALB), incluyendo el grupo de destino (target group) y el listener. Este archivo permite centralizar la configuración del balanceador de carga, facilitando el enrutamiento de tráfico entrante hacia las instancias EC2 definidas en el grupo de destino. Con estas configuraciones, el ALB podrá distribuir el tráfico de manera eficiente y responder a las solicitudes entrantes de acuerdo con las reglas establecidas en el listener.
-
resource "aws_lb" "demo_lb" {
load_balancer_type = "application"
name = "demo-alb"
security_groups = [aws_security_group.alb_sg.id]
subnets = module.vpc.public_subnets
}
resource "aws_lb_target_group" "demo" {
name = "demo-alb-target-group"
port = 80
vpc_id = module.vpc.vpc_id
protocol = "HTTP"
health_check {
enabled = true
matcher = "200"
path ="/"
port = var.puerto_servidor
protocol = "HTTP"
}
}
resource "aws_lb_target_group_attachment" "demo_1" {
count = var.instance_count
target_group_arn = aws_lb_target_group.demo.arn
target_id = aws_instance.demo[count.index].id
port = var.puerto_servidor
}
resource "aws_lb_listener" "this" {
load_balancer_arn = aws_lb.demo_lb.arn
port = var.puerto_lb
protocol = "HTTP"
default_action {
target_group_arn = aws_lb_target_group.demo.arn
type = "forward"
}
}
8- En el archivo 6-iam.tf, definiremos todos los elementos relacionados con la gestión de identidad y acceso (IAM) en el proyecto. Esto incluirá la creación de roles específicos, la definición de políticas de permisos, y la asociación de estas políticas a los roles correspondientes.
-
resource "aws_iam_role" "demo_role" {
name = "demo_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy" "test_policy" {
name = "test_policy"
role = "${aws_iam_role.demo_role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ec2:*",
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_policy_attachment" "attach_amazon_ssm_full_access-demo_role" {
name = "attach-amazon-ssm-full-access-demo_role"
roles = [aws_iam_role.demo_role.name]
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMFullAccess"
}
resource "aws_iam_policy_attachment" "attach_amazon_ec2_role_for_ssm-demo_role" {
name = "attach-amazon-ec2-role-for-ssm-demo_role"
roles = [aws_iam_role.demo_role.name]
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
}
resource "aws_iam_instance_profile" "demo_profile" {
name = "demo_profile"
role = "${aws_iam_role.demo_role.name}"
}
9- En el archivo 7-variables.tf, definiremos todas las variables que necesitaremos para parametrizar el proyecto. Estas variables permitirán una mayor flexibilidad y reutilización del código, facilitando la personalización de los recursos y haciendo que el despliegue sea más adaptable a diferentes entornos y requisitos específicos.
-
variable "instance_count" {
description = "Cantidad de instancias EC2"
type = number
default = 3
}
variable "puerto_servidor" {
description = "Puerto para las instancias"
type = number
default = 80
#Forma de validar una variable
validation {
condition = var.puerto_servidor > 0 && var.puerto_servidor <= 65535
error_message = "El valor del puerto debe estar comprendido entre 1 y 65535."
}
}
variable "puerto_lb" {
description = "Puerto para el LB"
type = number
default = 80
}
variable "puerto_ssh" {
description = "Puerto SSh para acceder a la instancias"
type = number
default = 22
}
variable "instance_type" {
description = "Tipo de instancia EC2"
type = string
default = "t2.nano"
}
variable "public_key" {
description = "Public key path"
default = "~/.ssh/id_rsa.pub"
}
variable "instance_names" {
description = "A list of instance_names"
type = list(string)
default = []
}
10- En el archivo 8-output.tf, definiremos las salidas que Terraform nos mostrará tras ejecutar terraform apply
. Estas salidas incluirán detalles de los recursos creados, que nos ayudarán a verificar rápidamente el estado del despliegue y a acceder fácilmente a los recursos configurados en el proyecto, en este caso obtendremos el endpoint del ALB y las direcciones ip de las intancias.
-
output "dns_ALB" {
description = "DNS pública del load balancer"
value = "http://${aws_lb.demo_lb.dns_name}:${var.puerto_lb}"
}
output "ip_privada_servidor" {
description = "IP pública IPV4 de los servidores"
value = [for instance in aws_instance.demo : "${instance.private_ip}"]
}
11- En el archivo 9-demo.auto.tfvars, estableceremos los valores de las variables para el entorno que estamos creando. Este archivo es esencial, ya que permite personalizar la configuración según las necesidades específicas de cada ambiente. Ten en cuenta que los valores pueden variar dependiendo de si estamos trabajando en un entorno de desarrollo, pruebas o producción, lo que brinda flexibilidad y adaptabilidad a nuestra infraestructura.
-
puerto_lb = 80
puerto_servidor = 80
puerto_ssh = 22
instance_type = "t2.medium"
instance_count = 4
instance_names = ["servidor-1", "servidor-2", "servidor-3", "servidor-4"]
Con todos los archivos configurados, ya estamos listos para ejecutar Terraform y desplegar la infraestructura.
Ahora, podemos acceder a la URL del ALB y comprobar que el trafico es balanceado entre las instancias.
Tambien podemos ir a la consola de AWS y ver que todos los recursos se hayan creado.
Terraform destroy
- Como siempre, al finalizar tus pruebas, recuerda eliminar todos los recursos creados ejecutando
terraform destroy --auto-approve
. Este paso es crucial para evitar sorpresas desagradables en tu factura a final del mes. No subestimes la importancia de esta tarea, ya que la acumulación de recursos no utilizados puede generar costos innecesarios. Tómate el tiempo para limpiar tu entorno, eso te ahorrará dolores de cabeza en el futuro 🙂
Conclusiones
Hemos llegado al final de esta entrada, donde exploramos los distintos tipos de Elastic Load Balancers (ALB) en AWS, sus características y casos de uso específicos. Además, vimos un ejemplo práctico con Terraform, en el que creamos un ALB y observamos cómo distribuye el tráfico entre varias instancias EC2, un escenario muy común en muchas empresas.
La facilidad de contar con servicios administrados como este en AWS es invaluable, ya que nos permite implementar un balanceador de cargas en cuestión de minutos. En una próxima entrada, profundizaremos en cómo integrar el ALB con un grupo de Auto Scaling, permitiéndonos escalar el número de instancias en función de métricas como el uso de CPU. Esta configuración es altamente demandada, pues asegura que una aplicación reciba los recursos necesarios de manera automática, evitando la degradación del servicio.
¡Nos vemos en la próxima entrada!
Nota: Todo el código lo pueden encontrar en el siguiente repositorio.