Skip to content

Load Balancing

Concepts et implémentation du load balancing : HAProxy, Nginx, et architectures haute disponibilité.

Concepts Fondamentaux

Load Balancing Overview

LOAD BALANCING - VUE D'ENSEMBLE
══════════════════════════════════════════════════════════

                         ┌─────────────┐
                         │   Clients   │
                         └──────┬──────┘
                    ┌───────────▼───────────┐
                    │    Load Balancer      │
                    │   (VIP: 10.0.0.100)   │
                    └───────────┬───────────┘
           ┌────────────────────┼────────────────────┐
           │                    │                    │
           ▼                    ▼                    ▼
    ┌─────────────┐      ┌─────────────┐      ┌─────────────┐
    │  Backend 1  │      │  Backend 2  │      │  Backend 3  │
    │ 10.0.0.11   │      │ 10.0.0.12   │      │ 10.0.0.13   │
    └─────────────┘      └─────────────┘      └─────────────┘

Objectifs:
• Distribution de charge entre serveurs
• Haute disponibilité (failover automatique)
• Scalabilité horizontale
• Maintenance sans interruption

Couches OSI

LOAD BALANCING PAR COUCHE
══════════════════════════════════════════════════════════

Layer 4 (Transport - TCP/UDP):
  • Décision basée sur IP:port
  • Très performant (peu d'overhead)
  • Pas d'inspection du contenu
  • Exemples: HAProxy mode tcp, LVS, F5

Layer 7 (Application - HTTP):
  • Décision basée sur le contenu (URL, headers, cookies)
  • Fonctionnalités avancées (routing, rewriting)
  • Plus de latence (parsing HTTP)
  • Exemples: HAProxy mode http, Nginx, Traefik, Envoy

Choix:
  L4 → Performance pure, protocoles non-HTTP
  L7 → Flexibilité, routing intelligent, TLS termination

Algorithmes de Distribution

ALGORITHMES DE LOAD BALANCING
══════════════════════════════════════════════════════════

Round Robin:
  • Chaque serveur à tour de rôle
  • Simple, équitable si serveurs identiques
  • Ne tient pas compte de la charge

Weighted Round Robin:
  • Round robin avec poids par serveur
  • Serveur puissant = poids élevé = plus de requêtes

Least Connections:
  • Envoie vers le serveur avec le moins de connexions actives
  • Bon pour requêtes de durée variable

Weighted Least Connections:
  • Least connections avec poids
  • Combine capacité et charge actuelle

IP Hash:
  • Hash de l'IP client → même serveur
  • Sticky sessions sans cookies
  • Problème si NAT (tous les clients même IP)

URI Hash:
  • Hash de l'URI → même serveur
  • Bon pour cache (même ressource = même serveur)

Least Response Time:
  • Vers le serveur le plus rapide
  • Nécessite des health checks actifs

Random:
  • Distribution aléatoire
  • Simple, pas de state à maintenir

HAProxy

Installation

# Debian/Ubuntu
apt install haproxy

# RHEL/CentOS
dnf install haproxy

# Version récente (PPA)
add-apt-repository ppa:vbernat/haproxy-2.8
apt update && apt install haproxy

# Vérifier
haproxy -v

Configuration de Base

# /etc/haproxy/haproxy.cfg

global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

    # Optimisations
    maxconn 50000
    tune.ssl.default-dh-param 2048

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    option  redispatch
    option  http-server-close
    timeout connect 5s
    timeout client  30s
    timeout server  30s
    retries 3

    # Compression
    compression algo gzip
    compression type text/html text/plain application/json

# Stats page
listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:password

# Frontend HTTP
frontend http_front
    bind *:80
    default_backend http_back

# Backend
backend http_back
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200

    server web1 10.0.0.11:80 check
    server web2 10.0.0.12:80 check
    server web3 10.0.0.13:80 check backup

HTTPS et TLS Termination

frontend https_front
    bind *:443 ssl crt /etc/haproxy/certs/wildcard.pem alpn h2,http/1.1
    bind *:80

    # Redirect HTTP to HTTPS
    http-request redirect scheme https unless { ssl_fc }

    # HSTS
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"

    # Routing basé sur le Host
    acl host_api hdr(host) -i api.example.com
    acl host_web hdr(host) -i www.example.com

    use_backend api_back if host_api
    use_backend web_back if host_web
    default_backend web_back

backend api_back
    balance leastconn
    option httpchk GET /health

    # Headers
    http-request set-header X-Forwarded-Proto https
    http-request set-header X-Real-IP %[src]

    server api1 10.0.0.21:8080 check
    server api2 10.0.0.22:8080 check

backend web_back
    balance roundrobin
    option httpchk GET /

    # Sticky sessions (cookie)
    cookie SERVERID insert indirect nocache

    server web1 10.0.0.11:80 check cookie s1
    server web2 10.0.0.12:80 check cookie s2

Layer 4 (TCP)

# Mode TCP pour base de données, autres protocoles
frontend mysql_front
    bind *:3306
    mode tcp
    option tcplog
    default_backend mysql_back

backend mysql_back
    mode tcp
    balance leastconn
    option mysql-check user haproxy

    server mysql1 10.0.0.31:3306 check
    server mysql2 10.0.0.32:3306 check backup

Health Checks Avancés

backend api_back
    # HTTP health check
    option httpchk
    http-check connect
    http-check send meth GET uri /health ver HTTP/1.1 hdr Host api.example.com
    http-check expect status 200

    # Paramètres de check
    server api1 10.0.0.21:8080 check inter 3s fall 3 rise 2

# TCP health check
backend redis_back
    mode tcp
    option tcp-check
    tcp-check connect
    tcp-check send PING\r\n
    tcp-check expect string +PONG

    server redis1 10.0.0.41:6379 check

ACLs et Routing

frontend http_front
    bind *:80

    # ACLs
    acl is_api path_beg /api
    acl is_static path_end .css .js .png .jpg .gif
    acl is_websocket hdr(Upgrade) -i websocket
    acl is_post method POST
    acl from_internal src 10.0.0.0/8 192.168.0.0/16
    acl is_blocked_ua hdr_sub(User-Agent) -i bot crawler

    # Rate limiting
    stick-table type ip size 100k expire 30s store http_req_rate(10s)
    http-request track-sc0 src
    http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }

    # Routing
    use_backend static_back if is_static
    use_backend api_back if is_api
    use_backend ws_back if is_websocket
    http-request deny if is_blocked_ua

    default_backend web_back

Nginx (Load Balancer)

Configuration de Base

# /etc/nginx/nginx.conf

upstream backend {
    # Algorithme (round-robin par défaut)
    # least_conn;
    # ip_hash;
    # hash $request_uri consistent;

    server 10.0.0.11:80 weight=3;
    server 10.0.0.12:80 weight=2;
    server 10.0.0.13:80 backup;

    # Keepalive connections
    keepalive 32;
}

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";

        # Timeouts
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # Buffers
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
    }

    # Health check endpoint
    location /nginx_status {
        stub_status on;
        allow 127.0.0.1;
        deny all;
    }
}

HTTPS et HTTP/2

upstream backend {
    server 10.0.0.11:80;
    server 10.0.0.12:80;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }
}

Routing Avancé

upstream api {
    server 10.0.0.21:8080;
    server 10.0.0.22:8080;
}

upstream web {
    server 10.0.0.11:80;
    server 10.0.0.12:80;
}

upstream static {
    server 10.0.0.51:80;
}

server {
    listen 80;

    # Static files
    location ~* \.(css|js|png|jpg|gif|ico|woff2)$ {
        proxy_pass http://static;
        proxy_cache static_cache;
        proxy_cache_valid 200 1d;
        add_header X-Cache-Status $upstream_cache_status;
    }

    # API
    location /api/ {
        proxy_pass http://api/;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    # WebSocket
    location /ws {
        proxy_pass http://api;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }

    # Default
    location / {
        proxy_pass http://web;
    }
}

Health Checks (Nginx Plus ou module tiers)

# Nginx Plus ou avec module nginx_upstream_check
upstream backend {
    server 10.0.0.11:80;
    server 10.0.0.12:80;

    # Nginx Plus
    # health_check interval=5s fails=3 passes=2;

    # Passive health check (open source)
    # max_fails et fail_timeout
    server 10.0.0.11:80 max_fails=3 fail_timeout=30s;
}

Traefik

Configuration Docker

# docker-compose.yml
version: "3.8"

services:
  traefik:
    image: traefik:v2.10
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=auth"
      - "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$..."

  webapp:
    image: nginx
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webapp.rule=Host(`www.example.com`)"
      - "traefik.http.routers.webapp.entrypoints=websecure"
      - "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
      - "traefik.http.services.webapp.loadbalancer.server.port=80"
    deploy:
      replicas: 3

Configuration Kubernetes

# IngressRoute Traefik
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: webapp
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`www.example.com`)
      kind: Rule
      services:
        - name: webapp
          port: 80
      middlewares:
        - name: rate-limit
  tls:
    certResolver: letsencrypt
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: rate-limit
spec:
  rateLimit:
    average: 100
    burst: 50

Haute Disponibilité

HAProxy + Keepalived

HAProxy Keepalived HA

ARCHITECTURE HA
══════════════════════════════════════════════════════════

                    VIP: 10.0.0.100
         ┌───────────────┴───────────────┐
         │                               │
    ┌────▼────┐                    ┌────▼────┐
    │ HAProxy │◄──── Keepalived ───│ HAProxy │
    │ Master  │      (VRRP)        │ Backup  │
    │ .10     │                    │ .11     │
    └────┬────┘                    └────┬────┘
         │                               │
         └───────────────┬───────────────┘
         ┌───────────────┼───────────────┐
         ▼               ▼               ▼
    ┌─────────┐    ┌─────────┐    ┌─────────┐
    │Backend 1│    │Backend 2│    │Backend 3│
    └─────────┘    └─────────┘    └─────────┘
# /etc/keepalived/keepalived.conf (Master)
vrrp_script check_haproxy {
    script "/usr/bin/killall -0 haproxy"
    interval 2
    weight 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 101
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass secretpass
    }

    virtual_ipaddress {
        10.0.0.100/24
    }

    track_script {
        check_haproxy
    }
}
# /etc/keepalived/keepalived.conf (Backup)
vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 100
    # ... reste identique
}

Synchronisation de Configuration

# Script de sync HAProxy config
#!/bin/bash
PEERS="haproxy-02 haproxy-03"

rsync -avz /etc/haproxy/haproxy.cfg /tmp/haproxy.cfg.new
haproxy -c -f /tmp/haproxy.cfg.new

if [ $? -eq 0 ]; then
    for peer in $PEERS; do
        rsync -avz /etc/haproxy/haproxy.cfg $peer:/etc/haproxy/
        ssh $peer "systemctl reload haproxy"
    done
    systemctl reload haproxy
fi

Monitoring

Métriques HAProxy

# Stats socket
echo "show stat" | socat stdio /run/haproxy/admin.sock

# Prometheus exporter
# haproxy_exporter --haproxy.scrape-uri="http://localhost:8404/stats;csv"

# Métriques importantes:
# - haproxy_frontend_current_sessions
# - haproxy_backend_current_queue
# - haproxy_server_response_time_average_seconds
# - haproxy_server_http_responses_total{code="5xx"}

Métriques Nginx

# Module stub_status
location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}

# Prometheus: nginx-prometheus-exporter
# Métriques:
# - nginx_http_requests_total
# - nginx_connections_active
# - nginx_connections_accepted

Bonnes Pratiques

Checklist Load Balancing:
  Architecture:
    - [ ] LB en haute disponibilité (2 minimum)
    - [ ] Health checks actifs
    - [ ] Graceful degradation (serveurs backup)
    - [ ] Session persistence si nécessaire

  Performance:
    - [ ] Keep-alive connections vers backends
    - [ ] Connection pooling
    - [ ] Timeouts appropriés
    - [ ] Compression activée

  Sécurité:
    - [ ] TLS termination au LB
    - [ ] Headers de sécurité (HSTS, X-Frame-Options)
    - [ ] Rate limiting
    - [ ] Filtrage d'IP si nécessaire

  Monitoring:
    - [ ] Métriques exposées (Prometheus)
    - [ ] Alertes sur backends down
    - [ ] Logs des requêtes
    - [ ] Dashboard de visualisation

Voir aussi :


Voir aussi