Aller au contenu

Configuration NGINX

Le provisionnement cloud-init déploie une configuration NGINX d’edge CDN entièrement optimisée pour les performances. Cette page documente chaque couche de configuration, du réglage du noyau au comportement du cache jusqu’à l’injection des en-têtes fournisseurs. Tous les paramètres ont été vérifiés lors d’un test de charge continu de 48 heures atteignant un pic de 172 540 req/s.

FichierObjectif
/etc/sysctl.d/99-cdn-tuning.confRéglage réseau du noyau Linux
/etc/systemd/system/nginx.service.d/override.confLimites des descripteurs de fichiers pour NGINX
/etc/security/limits.d/99-nginx.confLimites au niveau du système d’exploitation pour l’utilisateur www-data
/etc/nginx/nginx.confConfiguration principale NGINX (workers, tampons, gzip, journalisation)
/etc/nginx/conf.d/cdn-edge.confConfiguration proxy CDN (cache, amont, en-têtes)

Appliqué via /etc/sysctl.d/99-cdn-tuning.conf au démarrage.

ParamètreValeurDéfautObjectif
net.core.somaxconn655354096Taille de la file d’attente du backlog d’écoute
net.core.netdev_max_backlog655351000Backlog de paquets entrants par CPU
net.ipv4.tcp_max_syn_backlog65535256File d’attente des requêtes SYN (évite les pertes en cas de burst)
net.ipv4.tcp_tw_reuse12Réutilisation des sockets TIME_WAIT pour les connexions sortantes
net.ipv4.ip_local_port_range1024-6553532768-60999Plage de ports éphémères (64 K contre 28 K)
net.core.rmem_max16 Mo212 KoTampon de réception maximum du socket
net.core.wmem_max16 Mo212 KoTampon d’envoi maximum du socket
net.ipv4.tcp_rmem4K/87K/16M4K/131K/6MTampon de réception par socket (min/défaut/max)
net.ipv4.tcp_wmem4K/65K/16M4K/16K/4MTampon d’envoi par socket (min/défaut/max)
net.ipv4.tcp_fin_timeout1560Délai d’expiration FIN_WAIT_2 (libération plus rapide des sockets)
net.ipv4.tcp_keepalive_time3007200Démarrage des sondes keepalive après 5 min d’inactivité
net.ipv4.tcp_slow_start_after_idle01Maintien de la fenêtre de congestion active sur les connexions inactives
net.ipv4.tcp_max_tw_buckets2000000~65536Nombre maximum de sockets TIME_WAIT
fs.file-max2097152variableLimite système des descripteurs de fichiers
vm.swappiness1060Préférer la RAM au swap pour les données du cache
worker_processes auto; # 4 workers sur D4s_v5 (1 par vCPU)
worker_rlimit_nofile 65535; # Limite des descripteurs de fichiers par worker
events {
use epoll; # Modèle d'événements optimisé pour Linux
worker_connections 8192; # 4 workers x 8192 = 32 768 connexions simultanées max
multi_accept on; # Accepter toutes les connexions en attente par boucle d'événements
accept_mutex off; # Non nécessaire avec epoll + reuseport
}

La surcharge systemd dans /etc/systemd/system/nginx.service.d/override.conf définit LimitNOFILE=65535 pour correspondre.

proxy_buffering on;
proxy_buffer_size 16k; # Tampon d'en-têtes (gère les grands en-têtes CDN)
proxy_buffers 64 16k; # 1 Mo par connexion (64 x 16k)
proxy_busy_buffers_size 256k; # Peut envoyer 256k au client tout en continuant la lecture

La taille du tampon busy à 256k est critique — elle doit dépasser la plus grande réponse mise en cache (Juice Shop fait 75 Ko). Le paramètre initial de 64k causait une sérialisation sous charge.

gzip on;
gzip_comp_level 4; # Équilibre : ~85% de la compression maximale à ~40% du CPU
gzip_min_length 256; # Ignorer les petites réponses (surcharge d'en-tête gzip)
gzip_vary on; # Vary: Accept-Encoding pour un cache correct
gzip_proxied any; # Compresser toutes les réponses proxifiées
gzip_types text/plain text/css text/javascript text/xml
application/json application/javascript application/xml
application/xml+rss application/atom+xml
application/ld+json application/manifest+json
image/svg+xml;
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

Met en cache les descripteurs de fichiers et les métadonnées des objets mis en cache, éliminant les appels système stat() et open() sur les fichiers fréquemment accédés.

keepalive_timeout 65;
keepalive_requests 100000; # Était 1000 — causait un recyclage des connexions à 90 000 req/s

À 90 000 req/s avec keepalive_requests 1000, les connexions étaient recyclées toutes les 11 secondes, générant des sockets TIME_WAIT. L’augmentation à 100 000 a entièrement éliminé ce problème.

log_format cdn '$remote_addr [$time_local] "$request" $status $body_bytes_sent $upstream_cache_status $request_time';
access_log /var/log/nginx/access.log cdn buffer=256k flush=5s;

Le format de journal cdn inclut $upstream_cache_status (HIT/MISS) et $request_time pour l’analyse de la latence. La journalisation en tampon (256k/flush toutes les 5s) réduit la surcharge d’E/S sous forte charge.

upstream origin_backend {
server 20.12.78.159:80;
keepalive 256; # Connexions persistantes par worker vers l'origine
keepalive_timeout 60s;
keepalive_requests 1000;
}

Avec 4 workers x 256 keepalive = 1 024 connexions actives vers l’origine. Combiné avec proxy_http_version 1.1 et proxy_set_header Connection "", cela élimine la surcharge de la poignée de main TCP à chaque défaut de cache.

proxy_cache_path /var/cache/nginx/cdn
levels=1:2
keys_zone=cdn_cache:32m
max_size=25g
inactive=24h
use_temp_path=off;
ParamètreValeurObjectif
keys_zone=cdn_cache:32m32 Mo de mémoire partagéeStocke ~256 000 clés de cache et métadonnées
max_size=25gLimite disque de 25 GoUtilise la majeure partie du disque OS de 30 Go ; éviction LRU à la limite
inactive=24hDélai d’inactivité de 24 heuresLe contenu non consulté pendant 24h est évincé (différent du TTL)
use_temp_path=offÉcriture directe dans le répertoire du cacheÉvite les déplacements de fichiers entre systèmes de fichiers
proxy_cache cdn_cache;
proxy_cache_valid 200 301 302 4h;
proxy_cache_valid 404 1m;
proxy_cache_key "$scheme$host$request_uri";
proxy_cache_lock on;
proxy_cache_lock_age 3s;
proxy_cache_lock_timeout 3s;
proxy_cache_background_update on;
proxy_cache_use_stale updating error timeout http_500 http_502 http_503 http_504;
proxy_ignore_headers Set-Cookie Cache-Control Expires Vary;
proxy_hide_header X-Cache-Status;
proxy_hide_header Vary;
DirectiveValeurObjectif
proxy_cache_valid 200 301 302 4hTTL de 4 heuresContenu mis en cache valide pendant 4 heures (augmenté pour réduire les baisses de débit lors des vagues de rafraîchissement)
proxy_cache_lock onPrévention du thundering herdUne seule requête est envoyée à l’origine par URL non mise en cache ; les autres attendent le remplissage du cache
proxy_cache_lock_age 3sDélai d’expiration du verrouSi la première requête n’est pas terminée en 3s, une autre est autorisée à passer
proxy_cache_background_update onRafraîchissement sans latenceSert immédiatement le contenu périmé pendant le rafraîchissement en arrière-plan
proxy_cache_use_staleRésilienceSert le contenu périmé lors d’erreurs d’origine (500/502/503/504) ou pendant la mise à jour
proxy_ignore_headersForcer la mise en cacheIgnore les en-têtes Set-Cookie, Cache-Control, Expires, Vary de l’origine — le CDN décide du TTL et du comportement Vary (Juice Shop envoie max-age=0 et trois en-têtes Vary qui empêchaient une mise en cache efficace)
proxy_hide_header X-Cache-StatusSupprimer l’en-tête d’origineNGINX d’origine ajoute son propre X-Cache-Status — le supprimer afin que seul le statut de cache du CDN soit visible
proxy_hide_header VaryPrévenir la fragmentation du cacheL’origine envoie plusieurs en-têtes Vary: Accept-Encoding. Les supprimer évite la fragmentation des clés de cache selon les permutations d’Accept-Encoding. Le paramètre gzip_vary on de NGINX ajoute automatiquement le bon en-tête Vary unique
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
proxy_send_timeout 15s;

Accorde plus de temps de réponse à l’origine sous charge tout en échouant rapidement en cas de problèmes de connexion.

server {
listen 80 reuseport; # Le noyau distribue les connexions sur les 4 workers
server_name _;
}

reuseport active SO_REUSEPORT — le noyau distribue directement les connexions entrantes aux processus workers, éliminant la contention du mutex d’acceptation.

Le simulateur injecte simultanément des en-têtes des cinq principaux fournisseurs CDN. Cela permet de configurer F5 XC avec l’en-tête « Trusted Client IP Header » de n’importe quel fournisseur et de voir des charges d’en-têtes réalistes, quel que soit le CDN simulé.

En-têteValeurObjectif
X-Forwarded-ForChaîne IP clientIP de transfert standard
X-Forwarded-Protohttp ou httpsProtocole client d’origine
X-Forwarded-HostNom d’hôte d’origineEn-tête Host d’origine
X-Forwarded-PortPort serveurPort d’origine
X-Real-IPIP clientIP client unique (convention nginx)
Via1.1 cdn-simulatorIdentification du proxy
ForwardedFormat RFC 7239En-tête de transfert standardisé
CDN-Loopcdn-simulatorDétection de boucle
En-têteValeurObjectif
True-Client-IPIP clientAdresse IP de l’utilisateur final d’origine
X-Akamai-EdgescapeChaîne géographique composéegeoregion, country_code, region_code, city, dma, pmsa, msa, areacode, county, fips, lat, long, timezone, zip, continent, throughput, bw, network, asnum, network_type
X-Akamai-Device-CharacteristicsPropriétés de l’appareilbrand_name, model_name, is_mobile, is_tablet, is_wireless_device, device_os, device_os_version, resolution_width, resolution_height
X-Akamai-Request-IDUUID de la requêteIdentifiant unique de la requête
En-têteValeurObjectif
CF-Connecting-IPIP clientVéritable IP client (toujours présente)
CF-IPCountryUSCode pays à deux lettres
cf-ipcitySan JoseVille du client
cf-ipcontinentNACode continent
cf-iplatitude / cf-iplongitudeCoordonnéesGéolocalisation
cf-region / cf-region-codeCalifornia / CAInformations de région
cf-metro-code807Code de zone métropolitaine US
cf-postal-code95113Code postal
cf-timezoneAmerica/Los_AngelesFuseau horaire IANA
Cf-Ray{request_id}-SJCIdentifiant ray unique avec code IATA du POP
CF-Visitor{"scheme":"https"}Informations sur le protocole du visiteur
cf-bot-score85Score bot (1=bot, 99=humain)
cf-verified-botfalseIndicateur de bot reconnu comme valide
cf-ja3-hashe7d705a3286e19ea42f587b344ee6865Empreinte TLS JA3
cf-ja4t13d1516h2_8daaf6152771_b0da82dd1658Empreinte TLS JA4
En-têteValeurObjectif
CloudFront-Viewer-AddressIP:portIP client et port source
CloudFront-Viewer-CountryUSCode pays
CloudFront-Viewer-Country-NameUnited StatesNom complet du pays
CloudFront-Viewer-Country-RegionCACode de région
CloudFront-Viewer-Country-Region-NameCaliforniaNom complet de la région
CloudFront-Viewer-CitySan JoseVille du client
CloudFront-Viewer-Postal-Code95113Code postal
CloudFront-Viewer-Latitude / Longitude37.33530 / -121.89300Géolocalisation
CloudFront-Viewer-Time-ZoneAmerica/Los_AngelesFuseau horaire IANA
CloudFront-Viewer-Metro-Code807Code de zone métropolitaine US
CloudFront-Viewer-ASN7018Numéro de système autonome
CloudFront-Viewer-Http-Version2.0Version HTTP du client
CloudFront-Forwarded-ProtohttpsProtocole d’origine
CloudFront-Viewer-TLSTLSv1.3:TLS_AES_128_GCM_SHA256:sessionResumedDétails TLS
CloudFront-Viewer-JA3-Fingerprinte7d705a3286e19ea42f587b344ee6865Empreinte TLS JA3
CloudFront-Is-Desktop-Viewertrue/falseDétection d’appareil
CloudFront-Is-Mobile-Viewertrue/falseDétection d’appareil
CloudFront-Is-Tablet-Viewertrue/falseDétection d’appareil
CloudFront-Is-SmartTV-ViewerfalseDétection d’appareil
X-Amz-Cf-IdID encodéIdentifiant de requête CloudFront
En-têteValeurObjectif
Fastly-Client-IPIP clientVéritable IP client
Fastly-SSL1La connexion était sur TLS
Fastly-Client1Requête côté client (pas shield)
Fastly-FFcache-sjc3120-SJCIdentification du nœud de cache
X-Geo-Country-CodeUSPays (convention de variable VCL)
X-Geo-Country-Code3USACode pays à trois lettres
X-Geo-Country-NameUnited StatesNom complet du pays
X-Geo-CitySan JoseVille du client
X-Geo-RegionCACode de région
X-Geo-Continent-CodeNAContinent
X-Geo-Latitude / X-Geo-Longitude37.3353 / -121.8938Géolocalisation
X-Geo-Postal-Code95113Code postal
X-Geo-Metro-Code807Code de zone métropolitaine US
X-Geo-ASN7018Numéro de système autonome
X-Geo-Conn-SpeedbroadbandClasse de vitesse de connexion
X-Geo-Conn-TypewiredType de connexion
En-têteValeurObjectif
X-Azure-ClientIPIP clientAdresse IP du client
X-Azure-SocketIPIP clientIP source du socket TCP
X-Azure-RefChaîne de référence encodéeRéférence unique de requête pour le dépannage
X-Azure-FDIDa0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1Identifiant de ressource Front Door
X-Azure-RequestChainhops=1Compteur de sauts pour la détection de boucle

En-têtes de réponse (ajoutés aux réponses client)

Section intitulée « En-têtes de réponse (ajoutés aux réponses client) »
En-têteValeursObjectif
X-Cache-StatusHIT, MISS, EXPIRED, STALE, UPDATINGComportement du cache pour cette requête
X-CDN-Edgecdn-simulatorIdentifie ce nœud edge
X-CDN-POPSJCCode IATA du point de présence simulé
X-Served-Bycache-sjc3120-SJCNœud de cache simulé au format Fastly
X-Request-IDUUID (par requête)Identifiant unique de la requête

Le simulateur détecte le type d’appareil depuis l’en-tête User-Agent à l’aide de directives map NGINX :

  • Mobile : Correspond à iPhone, Android (hors tablette), iPod, BlackBerry, Opera Mini, IEMobile
  • Tablette : Correspond à iPad, tablette Android, Kindle, PlayBook
  • Bureau : Valeur par défaut lorsque ni mobile ni tablette ne correspond

Le type d’appareil est reflété dans :

  • CloudFront-Is-Desktop-Viewer / CloudFront-Is-Mobile-Viewer / CloudFront-Is-Tablet-Viewer
  • X-Akamai-Device-Characteristics (champs is_mobile, is_tablet, is_wireless_device)
Fenêtre de terminal
ssh azureuser@<PUBLIC_IP>
# Mettre à jour le serveur amont
sudo sed -i 's|server .*;|server NEW_HOST:80;|' /etc/nginx/conf.d/cdn-edge.conf
# Vider le cache et recharger
sudo rm -rf /var/cache/nginx/cdn/*
sudo nginx -t && sudo systemctl reload nginx

Ou mettez à jour origin_host dans terraform.tfvars et exécutez terraform apply pour re-provisionner.

Fenêtre de terminal
ssh azureuser@<PUBLIC_IP>
sudo rm -rf /var/cache/nginx/cdn/*
sudo systemctl reload nginx
Fenêtre de terminal
# Nombre d'objets et utilisation du disque
sudo find /var/cache/nginx/cdn -type f | wc -l
sudo du -sh /var/cache/nginx/cdn
# Journal d'accès récent avec statut du cache
tail -20 /var/log/nginx/access.log
Fenêtre de terminal
# Connexions en temps réel et états des sockets
ss -s
# Utilisation CPU des workers NGINX
top -bn1 | grep nginx
# Connexions keepalive en amont
ss -tn state established dst <ORIGIN_IP> | wc -l
# Nombre de sockets TIME_WAIT
ss -tn state time-wait | wc -l

Vérifié lors d’un test de charge continu de 48 heures :

MétriqueValeur
Débit de pointe (mis en cache)172 540 req/s
Débit soutenu (mis en cache)85 000-103 000 req/s
Connexions de pointe15 000 simultanées
Taux de succès du cache100% (une fois réchauffé)
Mémoire sous charge1,2 Go stable (8% des 16 Go)
CPU au pic100% (4 cœurs - le CPU est le plafond)
Erreurs durant le test de 48h0
Fuites mémoireAucune détectée
Fuites de connexionsAucune détectée