6.4 KiB
Règle d'Or : Résolution DNS Dynamique dans les Proxies Nginx Docker
1. Contexte du Problème (Cause Racine de l'Erreur 502)
Dans un environnement Docker où les conteneurs sont reliés par un réseau virtuel ( ou réseau nommé partagé comme n8n), chaque conteneur peut obtenir une nouvelle adresse IP interne (ex. 172.27.0.x) à chaque redémarrage, mise à jour ou recréation.
Par défaut dans Nginx, lorsqu'une directive proxy_pass utilise un nom d'hôte statique direct :
# ❌ CONFIGURATION VULNÉRABLE (Résolution DNS statique au boot)
location / {
proxy_pass http://mon-backend:8080;
}
Nginx résout le nom mon-backend une seule fois au démarrage de Nginx et met l'adresse IP en cache de manière permanente en mémoire.
Conséquence :
Dès que mon-backend redémarre ou est mis à jour :
mon-backendreçoit une nouvelle adresse IP Docker (ex.172.27.0.43).- Nginx continue d'envoyer les requêtes vers l'ancienne IP (ex.
172.27.0.37). - Nginx reçoit un
Connection refusedouNo route to hostet renvoie un 502 Bad Gateway. - Le service reste en panne jusqu'à un redémarrage manuel du conteneur Nginx.
2. Le Pattern Requis : resolver 127.0.0.11 + Variable Dynamique
Pour forcer Nginx à réévaluer la résolution DNS selon le TTL Docker sans dépendre d'un cache figé, deux éléments sont indispensables :
- La déclaration explicite du DNS interne Docker (
127.0.0.11) avec un TTL court (ex.valid=10s). - L'assignation de l'URL cible dans une variable intermédiaire (
set $upstream_var ...). Nginx est conçu pour ne pas pré-résoudre les variables au boot, mais au moment du traitement de la requête.
Configuration Standard Recommandée :
server {
listen 80;
server_name service.bolbol.tn;
# 1. DNS interne Docker (127.0.0.11) avec re-résolution toutes les 10s
resolver 127.0.0.11 valid=10s ipv6=off;
# 2. Définition dynamique de l'amont via variable
location / {
set $backend_upstream http://mon-service:8080;
proxy_pass $backend_upstream;
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 Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
chunked_transfer_encoding on;
}
}
3. Périmètre Appliqué sur le NAS (21/08/2026)
| Service Proxy | Fichier de Configuration | Statut |
|---|---|---|
baserow-oauth-proxy |
/volume1/docker/baserow-oauth-stub/nginx.conf |
✅ Corrigé & Testé en direct |
baserow-schema-mcp-proxy |
/volume1/docker/baserow-schema-mcp/nginx.conf |
✅ Corrigé & Validé |
redaction-pro |
/volume1/docker/redaction-pro/nginx.conf |
✅ Corrigé & Validé |
formation-consultant |
/volume1/docker/formation-consultant/nginx.conf |
N/A (fichiers statiques locaux, aucun upstream) |
4. Protocole de Test de Résilience Obligatoire
Lors de la mise en place ou modification d'un reverse proxy Nginx :
- Valider la syntaxe :
docker exec <proxy> nginx -t - Recharger la config :
docker exec <proxy> nginx -s reload - Test d'attribution dynamique : Redémarrer le conteneur backend (
docker restart <backend>) et vérifier aveccurll'accès via le proxy sans toucher au conteneur proxy.
Piege additionnel confirme (02/09/2026) — le prefixe de location n'est PAS tronque avec proxy_pass + variable
Symptome : sur redaction-pro, TOUS les appels API echouaient en 405 Method Not Allowed depuis le navigateur, quel que soit le modele choisi — alors que les memes appels fonctionnaient parfaitement en tapant directement l'URL de Bifrost. Aucun rapport avec la whitelist de modeles (voir common/bifrost-vk-incidents.md, incident du meme jour).
Cause : le pattern documente plus haut (set $upstream ...; proxy_pass $upstream;) resout bien le probleme de cache DNS, MAIS il a un effet de bord non documente jusqu'ici : quand proxy_pass cible une variable, nginx ne tronque plus jamais le prefixe du location — il transmet l'URI ORIGINALE complete au backend, contrairement a un proxy_pass avec une URI litterale qui tronque automatiquement le prefixe du location matche.
Concretement sur redaction-pro :
location /api/ {
set $bifrost_upstream http://bifrost:8080;
proxy_pass $bifrost_upstream/; # BUG : le "/" final est ignore avec une variable
}
Une requete navigateur vers /api/v1/chat/completions etait transmise a Bifrost telle quelle, soit /api/v1/chat/completions — un chemin que Bifrost ne reconnait pas comme route API (son router de secours sert alors sa propre page de dashboard en GET, et renvoie 405 sur tout le reste).
Fix : ajouter un rewrite explicite AVANT le proxy_pass, pour forcer la troncature que la variable empeche :
location /api/ {
set $bifrost_upstream http://bifrost:8080;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass $bifrost_upstream; # sans "/" final, inutile desormais
}
Valider avec docker exec <proxy> nginx -t puis nginx -s reload (pas besoin de redemarrer le conteneur).
Regle generale : des qu'un location a un PREFIXE non-racine (/api/, /mcp/, etc.) ET que le proxy_pass cible une variable (pattern DNS dynamique ci-dessus), un rewrite ... break; de troncature est obligatoire, sinon le backend recoit un chemin errone en silence (pas d'erreur nginx, juste un mauvais routage cote backend).
A verifier (non fait le 02/09, hors perimetre de la tache du jour) : baserow-schema-mcp/nginx.conf utilise location /mcp/ { proxy_pass $mcp_upstream; } — meme pattern a risque, jamais audite sous cet angle precis. A verifier si le backend MCP attend un chemin sans prefixe avant de considerer que c'est fonctionnel par coincidence ou reellement correct.
Tableau perimetre — mise a jour
| Service Proxy | Statut prefixe non-racine |
|---|---|
redaction-pro (/api/) |
✅ Bug trouve et corrige le 02/09/2026 (rewrite ajoute) |
baserow-schema-mcp (/mcp/) |
⚠️ A verifier — meme pattern a risque, non audite |
baserow-oauth-proxy |
OK — utilise $upstream$request_uri (chemin complet volontaire), pas concerne |