Skip to content
Learni
Voir tous les tutoriels
DevOps

Comment implémenter un load balancing cloud AWS en 2026

Read in English

Introduction

Le load balancing cloud est un pilier de l'architecture moderne pour garantir la haute disponibilité (HA), la scalabilité horizontale et la résilience des applications. Sur AWS, l'Application Load Balancer (ALB) excelle pour le routage L7 intelligent, les health checks avancés et l'intégration native avec Auto Scaling Groups (ASG).

Ce tutoriel advanced vous guide pas à pas pour implémenter une stack complète avec Terraform : création d'une VPC multi-AZ, subnets publics, ALB avec target groups HTTP/HTTPS, launch template EC2 pour une app Nginx simple, et ASG pour l'auto-scaling. Imaginez comme un orchestre : l'ALB est le chef qui répartit les requêtes vers les musiciens (instances EC2) en temps réel, en évitant les 'mauvais' via health checks.

Pourquoi ce setup ? Il est production-ready, gère >10k req/s, supporte SSL termination et est 100% IaC pour CI/CD. Durée estimée : 30 min. À la fin, vous aurez un LB accessible publiquement, scalable à l'infini. Préparez votre compte AWS ! (142 mots)

Prérequis

  • Compte AWS actif avec permissions IAM : AmazonEC2FullAccess, AmazonVPCFullAccess, AWSElasticLoadBalancingFullAccess, AutoScalingFullAccess.
  • Terraform CLI v1.5+ installé (télécharger).
  • AWS CLI v2 configuré (aws configure) avec clés IAM admin.
  • Éditeur de code (VS Code avec extension Terraform recommandée).
  • Connaissances avancées en networking AWS (VPC, SG, AZ) et IaC.

Initialiser le projet Terraform

terminal
mkdir aws-load-balancer-terraform
cd aws-load-balancer-terraform
git init
echo 'terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.40"
    }
  }
  required_version = ">= 1.5"
}' > versions.tf
terraform init

Ce script crée le répertoire projet, initialise Git pour versionning, définit les versions Terraform/AWS provider dans versions.tf, et lance terraform init pour télécharger les providers. Cela pose les fondations IaC sans erreurs de compatibilité. Évitez les versions wildcards pour la stabilité en prod.

Configurer les providers et variables

Créez maintenant providers.tf et variables.tf. Le provider AWS cible eu-west-1 pour la compliance EU RGPD. Les variables permettent la personnalisation (CIDR, instance type). Utilisez terraform plan après chaque fichier pour valider.

Providers et variables Terraform

providers.tf
terraform {
  backend "s3" {
    bucket         = "your-terraform-state-bucket"
    key            = "aws-alb/terraform.tfstate"
    region         = "eu-west-1"
    dynamodb_table = "terraform-locks"
  }
}

provider "aws" {
  region = var.aws_region
}

data "aws_availability_zones" "available" {
  state = "available"
}

variable "aws_region" {
  description = "Région AWS"
  type        = string
  default     = "eu-west-1"
}

variable "vpc_cidr" {
  description = "CIDR du VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "instance_type" {
  description = "Type d'instance EC2"
  type        = string
  default     = "t3.micro"
}

Ce fichier configure le provider AWS avec backend S3 pour état distant (essentiel en équipe), data source AZ pour multi-zone, et variables par défaut. Le backend DynamoDB gère les locks concurrents. Piège : oubliez le backend local en prod, risque de corruption d'état.

Provisionner la VPC et networking

VPC multi-AZ : 2 subnets publics pour l'ALB (HA). Internet Gateway (IGW) + route table publique. Security Groups : ALB (HTTP/HTTPS inbound), EC2 (ALB + SSH). Exécutez terraform plan pour preview.

Ressources VPC et Security Groups

vpc.tf
resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr
  tags = {
    Name = "alb-vpc"
  }
}

resource "aws_subnet" "public" {
  count                   = 2
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.${50 + count.index}.0/24"
  availability_zone       = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true
  tags = {
    Name = "public-subnet-${count.index + 1}"
  }
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "alb-igw"
  }
}

resource "aws_default_route_table" "public" {
  default_route_table_id = aws_vpc.main.default_route_table_id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
}

resource "aws_security_group" "alb_sg" {
  name_prefix = "alb-sg-"
  vpc_id      = aws_vpc.main.id
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port   = 443
    to_port     = 443
    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"]
  }
  tags = {
    Name = "alb-sg"
  }
}

resource "aws_security_group" "ec2_sg" {
  name_prefix = "ec2-sg-"
  vpc_id      = aws_vpc.main.id
  ingress {
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb_sg.id]
  }
  ingress {
    from_port   = 22
    to_port     = 22
    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"]
  }
  tags = {
    Name = "ec2-sg"
  }
}

Provisionne VPC, 2 subnets publics AZ-isolés, IGW pour accès internet, et SGs restrictifs (least privilege). L'EC2 SG autorise uniquement trafic depuis ALB sur port 80. Piège : sans map_public_ip_on_launch, pas d'IP publique pour targets ; testez avec plan.

Application Load Balancer et Target Group

alb.tf
resource "aws_lb" "main" {
  name               = "alb-loadbalancer"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb_sg.id]
  subnets            = aws_subnet.public[*].id
  tags = {
    Environment = "prod"
  }
}

resource "aws_lb_target_group" "app" {
  name     = "app-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id
  health_check {
    enabled             = true
    healthy_threshold   = 2
    interval            = 30
    matcher             = "200"
    path                = "/"
    port                = "traffic-port"
    protocol            = "HTTP"
    timeout             = 5
    unhealthy_threshold = 2
  }
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = aws_lb.main.arn
  port              = "80"
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}

resource "aws_lb_listener" "https" {
  load_balancer_arn = aws_lb.main.arn
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2016-08"
  certificate_arn   = data.aws_acm_certificate.cert.arn
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.app.arn
  }
}

Crée ALB internet-facing en subnets publics, target group avec health check strict (/ sur HTTP 200), listeners HTTP/HTTPS (ajustez cert ACM). Forward tout trafic au TG. Piège : health check path doit matcher votre app (Nginx root ok) ; sinon, instances marked unhealthy.

Déployer EC2 via Launch Template et ASG

Launch Template : AMI Amazon Linux 2023, user-data installe Nginx + health endpoint. ASG : 2 instances min, scale sur CPU >70%, attache au TG. terraform apply pour déployer ! Accédez à l'ALB DNS après.

Launch Template, ASG et Outputs

ec2.tf
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64-kernel-*"]
  }
}

resource "aws_launch_template" "app" {
  name_prefix   = "app-lt-"
  image_id      = data.aws_ami.amazon_linux.id
  instance_type = var.instance_type
  vpc_security_group_ids = [aws_security_group.ec2_sg.id]
  user_data = base64encode(
    templatefile("${path.module}/user-data.sh", {
      health_path = "/"
    })
  )
  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "alb-app"
    }
  }
}

resource "aws_autoscaling_group" "app" {
  desired_capacity = 2
  max_size         = 4
  min_size         = 2
  vpc_zone_identifier = aws_subnet.public[*].id
  target_group_arns = [aws_lb_target_group.app.arn]
  health_check_grace_period = 300
  health_check_type = "ELB"
  launch_template {
    id      = aws_launch_template.app.id
    version = "$Latest"
  }
  tag {
    key                 = "Name"
    value               = "alb-asg"
    propagate_at_launch = true
  }
}

resource "aws_autoscaling_policy" "scale_up" {
  name                   = "scale-up"
  scaling_adjustment     = 2
  adjustment_type        = "ChangeInCapacity"
  cooldown              = 300
  autoscaling_group_name = aws_autoscaling_group.app.name
}

resource "aws_autoscaling_policy" "scale_down" {
  name                   = "scale-down"
  scaling_adjustment     = -1
  adjustment_type        = "ChangeInCapacity"
  cooldown              = 300
  autoscaling_group_name = aws_autoscaling_group.app.name
}

output "alb_dns_name" {
  description = "DNS de l'ALB"
  value       = aws_lb.main.dns_name
}

output "target_group_arn" {
  value = aws_lb_target_group.app.arn
}

Launch template avec AMI auto-résolue, user-data Nginx (fichier séparé ci-après), ASG multi-AZ avec ELB health, policies scale CPU-based. Outputs pour intégration CI/CD. Piège : grace_period trop court = faux unhealthy ; user-data doit exposer health path.

User-data script pour Nginx

user-data.sh
#!/bin/bash
yum update -y
yum install -y nginx
systemctl enable nginx
systemctl start nginx
cat > /usr/share/nginx/html/index.html <<EOF
<!DOCTYPE html>
<html>
<head><title>AWS ALB Test</title></head>
<body>
<h1>Load Balanced by AWS ALB! ✅</h1>
<p>Instance ID: $(curl -s http://169.254.169.254/latest/meta-data/instance-id)</p>
</body>
</html>
EOF
systemctl reload nginx

Script user-data bootstrap : update, install Nginx, page custom avec Instance ID pour vérifier routing. Health check sur / renvoie 200. Piège : sans reload, changements non appliqués ; testez localement avec bash user-data.sh.

Appliquer et tester le déploiement

terminal
terraform validate
tfenv vars set aws_region eu-west-1  # si tfenv
terraform plan -var="vpc_cidr=10.1.0.0/16"
terraform apply -auto-approve
terraform output alb_dns_name
curl $(terraform output -raw alb_dns_name)
echo 'DNS ALB: $(terraform output -raw alb_dns_name)' > alb-url.txt
# Cleanup
t# erraform destroy -auto-approve

Valide, planifie (optionnel override vars), applique, output DNS, teste curl (doit montrer HTML Nginx). Cleanup destroy. Piège : sans -var-file, defaults utilisés ; surveillez coûts (~0.025$/h ALB + EC2).

Bonnes pratiques

  • Multi-AZ obligatoire : au moins 2 subnets AZ-différents pour 99.99% SLA.
  • Health checks stricts : path app-spécifique, threshold 2-3, interval 30s.
  • SSL termination ALB : offload HTTPS, cert ACM gratuit/auto-renew.
  • Monitoring CloudWatch : alarmes CPU>70%, connexions, latency ; intégrez à Slack.
  • IaC avancé : modules Terraform réutilisables, remote state S3 + locks.

Erreurs courantes à éviter

  • SG trop permissifs : toujours spécifiez source (ALB ID pour EC2) ; audit avec GuardDuty.
  • Pas de grace_period ASG : instances tuées prématurément ; mettez 300s min.
  • Health check fail : vérifiez logs Nginx (/var/log/nginx/error.log) et path matcher (200 seulement).
  • État Terraform perdu : utilisez backend S3 dès le début, jamais local en prod.

Pour aller plus loin