Retour au cours

infra / reseaux-tcp-ip

Load balancing (L4 vs L7)

Leçon 121 exercice

Explication

Ce que vous allez apprendre

  • Comprendre pourquoi répartir la charge améliore à la fois la performance et la fiabilité
  • Distinguer un load balancer L4 (transport) d'un load balancer L7 (application)
  • Choisir un algorithme de répartition adapté (round-robin, least_conn, ip_hash, pondéré)
  • Configurer un reverse proxy nginx avec plusieurs backends et un backup
  • Comprendre le rôle des health checks pour retirer automatiquement un serveur défaillant

Dans quel contexte ?

Une API critique tourne sur trois serveurs identiques derrière un répartiteur nginx. Un soir, l'un des trois serveurs plante silencieusement suite à une fuite mémoire. Sans health check configuré, un tiers des requêtes des utilisateurs continuerait à échouer pendant des heures. Avec un health check actif (proxy_next_upstream et une vérification périodique), le serveur en panne est automatiquement retiré de la rotation dès sa première non-réponse, et les utilisateurs ne voient jamais l'incident.

Répartir la charge pour ne pas dépendre d'un seul serveur

Un seul serveur a une capacité limitée et représente un point de défaillance unique : s'il tombe, tout le service tombe avec lui. Le load balancing (répartition de charge) place un équipement intermédiaire devant plusieurs serveurs identiques, qui répartit les requêtes entre eux. C'est à la fois une solution de performance (répartir le travail) et de fiabilité (continuer à fonctionner même si un serveur tombe).

AlgorithmePrincipeCas d'usage
round-robinTour de rôle égal entre backendsBackends de puissance équivalente
least_connPriorise le backend le moins chargéRequêtes de durée variable
ip_hashUne même IP va toujours au même backendSessions sans cookie partagé
weightedRépartition proportionnelle à un poidsBackends de puissances différentes

Bonne pratique

Configure toujours un backup server et un health check applicatif (curl -fsS .../health), pas seulement une vérification TCP basique : un serveur peut répondre sur son port tout en étant incapable de traiter une vraie requête (base de données injoignable, par exemple).

L4 vs L7 : un compromis entre vitesse et intelligence

Un répartiteur de niveau 4 (transport) décide uniquement à partir de l'IP et du port, sans jamais lire le contenu réel de la requête : c'est rapide, mais aveugle au contenu applicatif. Un répartiteur de niveau 7 (application) lit véritablement le contenu HTTP (l'URL, les en-têtes, les cookies), ce qui permet des décisions bien plus fines — router selon le chemin demandé, garder un utilisateur toujours sur le même serveur — mais au prix d'un traitement plus coûteux.

Pourquoi le choix de l'algorithme compte

Répartir "à tour de rôle" (round-robin) fonctionne bien quand tous les serveurs sont équivalents et que chaque requête coûte à peu près pareil. Mais si les serveurs n'ont pas la même puissance, ou si certaines requêtes sont bien plus lourdes que d'autres, un algorithme plus fin (pondération, moins de connexions actives) évite de surcharger un serveur pendant que les autres restent sous-utilisés.

Le health check : ne pas envoyer de trafic vers un serveur mort

Un load balancer efficace ne se contente pas de répartir : il vérifie en continu que chaque serveur répond correctement, et retire automatiquement de la rotation ceux qui ne répondent plus, avant qu'un utilisateur ne soit impacté par une erreur.

Commandes & code

Load balancing : L4 vs L7

text
# Répartition de charge Layer 4 (transport) : décide au niveau IP/port, ne lit pas le contenu
# -> rapide, peu de CPU, mais ne peut pas router selon une URL ou un header HTTP

# Répartition de charge Layer 7 (application) : lit le contenu HTTP (URL, headers, cookies)
# -> plus lent, mais permet du routage intelligent (par chemin, par domaine, sticky sessions)
nginx
# --- L7 avec nginx : reverse proxy + load balancing HTTP ---
upstream backend_api {
    least_conn;                          # algorithme : moins de connexions actives en premier
    server 10.0.1.10:8000 weight=3;        # poids plus élevé = reçoit plus de trafic
    server 10.0.1.11:8000 weight=1;
    server 10.0.1.12:8000 backup;            # utilisé uniquement si les autres sont down
}

server {
    listen 443 ssl;
    server_name api.exemple.com;

    location / {
        proxy_pass http://backend_api;
        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_next_upstream error timeout http_502 http_503;   # bascule si un backend échoue
    }
}
text
# Algorithmes de répartition courants
round-robin     -> chaque backend reçoit les requêtes à tour de rôle (défaut)
least_conn        -> priorise le backend avec le moins de connexions actives
ip_hash             -> une même IP client va toujours vers le même backend (sticky sans cookie)
weighted              -> un backend plus puissant reçoit une part plus grande du trafic
bash
# --- L4 avec HAProxy : équilibrage TCP brut, sans inspection du contenu ---
cat /etc/haproxy/haproxy.cfg
# frontend tcp_front
#     bind *:5432
#     mode tcp
#     default_backend postgres_pool
#
# backend postgres_pool
#     mode tcp
#     balance leastconn
#     option tcp-check
#     server db1 10.0.2.10:5432 check
#     server db2 10.0.2.11:5432 check backup

sudo systemctl reload haproxy
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock       # stats en temps réel

# Health checks : retirer automatiquement un backend défaillant de la rotation
curl -fsS http://10.0.1.10:8000/health || echo "backend 10.0.1.10 DOWN"

# DNS round-robin : la forme la plus simple de load balancing, sans équipement dédié
dig +short api.exemple.com     # peut retourner plusieurs IP, résolues alternativement par le client

Résumé

  • L4 (HAProxy en mode tcp) route par IP/port sans lire le contenu : rapide, adapté aux bases de données.
  • L7 (nginx, HAProxy en mode http) lit les requêtes HTTP : permet du routage par URL/domaine et des sticky sessions.
  • Les health checks retirent automatiquement un backend en échec de la rotation avant qu'un utilisateur ne soit impacté.

Exercices pratiques

1 disponible
1

Mission : détecter une panne invisible aux health checks basiques

Objectif : Comprendre pourquoi un health check TCP superficiel peut manquer une panne applicative réelle, et choisir un algorithme adapté à des backends inégaux.

Contexte

Une API critique tourne sur trois serveurs derrière nginx. L'un des trois répond toujours sur le port 8000 (un health check TCP basique le voit UP), mais sa connexion à la base de données interne est cassée : il renvoie une erreur 500 à toutes les vraies requêtes.

Résoudre l’exercice →