Bonnes pratiques d'infrastructure pour micro-services Node.js & Next.js
Ce guide décrit l'infrastructure qui héberge réellement nos services : le bot Kurotsuki Studio, notre API Gateway et nos dashboards. Un seul frontal Nginx, des règles de cache explicites, des sondes qui pilotent le routage et des sauvegardes dont la restauration est prouvée. Public visé : développeurs et administrateurs exploitant des services Node.js sur Linux.
L'objectif n'est pas la sophistication mais la reprise sur panne mesurée en minutes, avec des causes d'incident identifiables dans les journaux et les métriques.
Reverse proxy Nginx : TLS, routage et garde-fous
Placez Nginx en frontal : il termine TLS (configuration moderne, HSTS, redirections HTTP vers HTTPS), route par préfixe (/ vers Next.js, /api/ vers la passerelle, /healthz vers les sondes) et applique des garde-fous globaux — taille maximale de corps, timeouts amont, limitation de débit par IP et mise en tampon des réponses lentes. Activez la compression Brotli/Gzip uniquement pour les contenus textuels et excluez les flux déjà compressés. Chaque service amont déclare une sonde (GET /healthz répondant 200 en moins de 300 ms avec vérification base de données) ; tout amont en échec est temporairement exclu du pool, ce qui transforme un déploiement défectueux en dégradation partielle plutôt qu'en panne totale.
Stratégie de cache : distinguer le statique, le dynamique et l'API
Servez les assets Next.js compilés (/_next/static/) avec un cache long et immuable, les pages statiques avec revalidation courte, et ne mettez jamais en cache par défaut les réponses d'API authentifiées. Pour les endpoints publics coûteux (classements, statistiques), ajoutez un cache applicatif de courte durée (30 à 120 secondes) avec clé incluant la version et la langue, et exposez des en-têtes Cache-Control et ETag explicites. Invalidez par versionnage d'URL plutôt que par purge manuelle : c'est la seule stratégie qui reste fiable à 3 heures du matin pendant un incident.
Monitoring de latence et reprise sur panne
Mesurez la latence côté serveur par route (médiane p50, p95, p99) et côté client (Web Vitals pour Next.js : LCP, INP, CLS), avec des alertes sur le p95 et sur le taux d'erreur 5xx plutôt que sur des moyennes qui masquent les dégradations. Centralisez les journaux structurés (JSON avec requestId, route, statut, durée) et corrélez-les avec les métriques : un pic de p99 sans erreur applicative oriente vers la base ou le réseau, un pic d'erreurs 5xx vers un déploiement récent. Automatisez les sauvegardes quotidiennes de la base et des volumes persistants, chiffrez-les au repos, conservez-les hors site et — point critique — restaurez-les chaque trimestre sur un environnement isolé pour prouver qu'elles fonctionnent. Documentez enfin une procédure de reprise en une page : ordre de redémarrage des services, commandes de rollback, contacts d'astreinte et critères de fin d'incident.
# /etc/nginx/conf.d/platform.conf — frontal TLS + routage + garde-fous (extrait)
upstream nextjs { server 127.0.0.1:3000 max_fails=2 fail_timeout=15s; }
upstream gateway { server 127.0.0.1:4000 max_fails=2 fail_timeout=15s; }
server {
listen 443 ssl http2;
server_name lifestealmc.fr;
client_max_body_size 2m;
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
location /_next/static/ {
proxy_pass http://nextjs;
proxy_cache_valid 200 30d;
add_header Cache-Control "public, max-age=2592000, immutable" always;
}
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://gateway;
proxy_read_timeout 15s;
proxy_set_header X-Request-Id $request_id;
}
location = /healthz { proxy_pass http://gateway; access_log off; }
location / { proxy_pass http://nextjs; }
}
# Sonde applicative minimale (Node.js) — binaire pour le proxy et l'orchestrateur
import http from "node:http";
import { checkDatabase } from "./db.js";
http.createServer(async (req, res) => {
if (req.url !== "/healthz") { res.writeHead(404).end(); return; }
const t0 = Date.now();
try {
await Promise.race([checkDatabase(), new Promise((_, r) => setTimeout(() => r(new Error("timeout")), 250))]);
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, ms: Date.now() - t0 }));
} catch { res.writeHead(503, { "content-type": "application/json" }).end(JSON.stringify({ ok: false })); }
}).listen(4000);
À retenir
Un frontal unique qui termine TLS et applique les garde-fous, un cache différencié par type de contenu, des sondes qui excluent réellement les amonts défaillants, et des sauvegardes restaurées chaque trimestre : c'est ce socle qui permet d'exploiter sereinement les services décrits dans le guide 1 (bots Discord) et de faire respecter les contrôles du guide 2 (webhooks & API). Pour toute question d'exploitation, notre support répond sous quelques jours ouvrés.