Salta ai contenuti

Configurazione NGINX

Il provisioning cloud-init distribuisce una configurazione NGINX per CDN edge completamente ottimizzata per le prestazioni. Questa pagina documenta ogni livello di configurazione, dall’ottimizzazione del kernel al comportamento della cache fino all’iniezione degli header dei vendor. Tutte le impostazioni sono state verificate durante un test di carico continuo di 48 ore con un picco di 172.540 req/s.

FileScopo
/etc/sysctl.d/99-cdn-tuning.confOttimizzazione della rete del kernel Linux
/etc/systemd/system/nginx.service.d/override.confLimiti dei descrittori di file per NGINX
/etc/security/limits.d/99-nginx.confLimiti a livello di OS per l’utente www-data
/etc/nginx/nginx.confConfigurazione principale NGINX (worker, buffer, gzip, logging)
/etc/nginx/conf.d/cdn-edge.confConfigurazione proxy CDN (cache, upstream, header)

Applicata tramite /etc/sysctl.d/99-cdn-tuning.conf all’avvio.

ParametroValoreDefaultScopo
net.core.somaxconn655354096Dimensione della coda di backlog in ascolto
net.core.netdev_max_backlog655351000Backlog dei pacchetti in ingresso per CPU
net.ipv4.tcp_max_syn_backlog65535256Coda delle richieste SYN (previene perdite sotto burst)
net.ipv4.tcp_tw_reuse12Riutilizza i socket TIME_WAIT per le connessioni in uscita
net.ipv4.ip_local_port_range1024-6553532768-60999Range delle porte effimere (64K vs 28K)
net.core.rmem_max16 MB212 KBBuffer socket di ricezione massimo
net.core.wmem_max16 MB212 KBBuffer socket di invio massimo
net.ipv4.tcp_rmem4K/87K/16M4K/131K/6MBuffer di ricezione per socket (min/default/max)
net.ipv4.tcp_wmem4K/65K/16M4K/16K/4MBuffer di invio per socket (min/default/max)
net.ipv4.tcp_fin_timeout1560Timeout FIN_WAIT_2 (pulizia più rapida dei socket)
net.ipv4.tcp_keepalive_time3007200Avvia le probe keepalive dopo 5 min di inattività
net.ipv4.tcp_slow_start_after_idle01Mantiene la finestra di congestione attiva sulle connessioni inattive
net.ipv4.tcp_max_tw_buckets2000000~65536Numero massimo di socket TIME_WAIT
fs.file-max2097152variabileLimite di descrittori di file a livello di sistema
vm.swappiness1060Preferisce la RAM allo swap per i dati di cache
worker_processes auto; # 4 worker su D4s_v5 (1 per vCPU)
worker_rlimit_nofile 65535; # Limite descrittori di file per worker
events {
use epoll; # Modello di eventi ottimizzato per Linux
worker_connections 8192; # 4 worker x 8192 = 32.768 connessioni concorrenti max
multi_accept on; # Accetta tutte le connessioni in sospeso per ogni ciclo di eventi
accept_mutex off; # Non necessario con epoll + reuseport
}

L’override di Systemd in /etc/systemd/system/nginx.service.d/override.conf imposta LimitNOFILE=65535 in modo corrispondente.

proxy_buffering on;
proxy_buffer_size 16k; # Buffer degli header (gestisce grandi header CDN)
proxy_buffers 64 16k; # 1 MB per connessione (64 x 16k)
proxy_busy_buffers_size 256k; # Può inviare 256k al client mentre legge ancora

La dimensione del busy buffer di 256k è critica — deve essere superiore alla risposta più grande memorizzata nella cache (Juice Shop è 75 KB). L’impostazione originale a 64k causava serializzazione sotto carico.

gzip on;
gzip_comp_level 4; # Bilanciamento: ~85% della compressione massima a ~40% della CPU
gzip_min_length 256; # Salta le risposte piccole (overhead dell'header gzip)
gzip_vary on; # Vary: Accept-Encoding per una corretta memorizzazione in cache
gzip_proxied any; # Comprime tutte le risposte proxy
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;

Memorizza nella cache i descrittori di file e i metadati per gli oggetti in cache, eliminando le syscall stat() e open() sui file più acceduti.

keepalive_timeout 65;
keepalive_requests 100000; # Era 1000 — causava il riciclo delle connessioni a 90K req/s

A 90K req/s con keepalive_requests 1000, le connessioni venivano riciclate ogni 11 secondi, generando socket TIME_WAIT. L’aumento a 100.000 ha eliminato completamente questo problema.

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;

Il formato di log cdn include $upstream_cache_status (HIT/MISS) e $request_time per l’analisi della latenza. Il logging con buffer (256k/flush ogni 5s) riduce l’overhead di I/O sotto carico elevato.

upstream origin_backend {
server 20.12.78.159:80;
keepalive 256; # Connessioni persistenti per worker verso l'origine
keepalive_timeout 60s;
keepalive_requests 1000;
}

Con 4 worker x 256 keepalive = 1.024 connessioni attive verso l’origine. Combinato con proxy_http_version 1.1 e proxy_set_header Connection "", elimina l’overhead dell’handshake TCP ad ogni cache miss.

proxy_cache_path /var/cache/nginx/cdn
levels=1:2
keys_zone=cdn_cache:32m
max_size=25g
inactive=24h
use_temp_path=off;
ParametroValoreScopo
keys_zone=cdn_cache:32m32 MB di memoria condivisaMemorizza ~256.000 chiavi di cache e metadati
max_size=25gLimite disco di 25 GBUtilizza la maggior parte del disco OS da 30 GB; eviction LRU al limite
inactive=24hTimeout di inattività di 24 oreIl contenuto non acceduto per 24h viene rimosso (diverso dal TTL)
use_temp_path=offScrittura diretta nella dir cacheEvita spostamenti di file tra filesystem diversi
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;
DirettivaValoreScopo
proxy_cache_valid 200 301 302 4hTTL di 4 oreContenuto in cache valido per 4 ore (aumentato per ridurre i cali di throughput durante le ondate di aggiornamento)
proxy_cache_lock onPrevenzione thundering herdSolo 1 richiesta raggiunge l’origine per URL non in cache; le altre attendono il riempimento della cache
proxy_cache_lock_age 3sTimeout del lockSe la prima richiesta non è completata entro 3s, ne viene inoltrata un’altra
proxy_cache_background_update onAggiornamento a latenza zeroServe il contenuto obsoleto immediatamente mentre aggiorna in background
proxy_cache_use_staleResilienzaServe contenuto obsoleto durante errori dell’origine (500/502/503/504) o durante l’aggiornamento
proxy_ignore_headersForza la memorizzazione in cacheIgnora Set-Cookie, Cache-Control, Expires e Vary dell’origine — è il CDN a decidere TTL e comportamento Vary (Juice Shop invia max-age=0 e tripli header Vary che impedivano un’efficace memorizzazione in cache)
proxy_hide_header X-Cache-StatusRimuove l’header dell’origineL’NGINX dell’origine aggiunge il proprio X-Cache-Status — viene rimosso affinché sia visibile solo lo stato di cache del CDN
proxy_hide_header VaryPreviene la frammentazione della cacheL’origine invia più header Vary: Accept-Encoding. La loro rimozione previene la frammentazione della chiave di cache tra le varie permutazioni di Accept-Encoding. gzip_vary on di NGINX aggiunge automaticamente il corretto singolo header Vary
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
proxy_send_timeout 15s;

Concede all’origine più tempo di risposta sotto carico mantenendo un rapido fallimento in caso di problemi di connessione.

server {
listen 80 reuseport; # Il kernel distribuisce le connessioni tra tutti i 4 worker
server_name _;
}

reuseport abilita SO_REUSEPORT — il kernel distribuisce le connessioni in ingresso direttamente ai processi worker, eliminando la contesa sull’accept mutex.

Il simulatore inietta header da tutti e cinque i principali vendor CDN contemporaneamente. Questo consente di configurare F5 XC con l‘“Header IP Client Attendibile” di qualsiasi vendor e di vedere payload di header realistici indipendentemente dal CDN simulato.

HeaderValoreScopo
X-Forwarded-ForCatena IP clientIP inoltrato standard
X-Forwarded-Protohttp o httpsProtocollo originale del client
X-Forwarded-HostNome host originaleHeader Host originale
X-Forwarded-PortPorta del serverPorta originale
X-Real-IPIP del clientIP singolo del client (convenzione nginx)
Via1.1 cdn-simulatorIdentificazione del proxy
ForwardedFormato RFC 7239Header di inoltro standardizzato
CDN-Loopcdn-simulatorRilevamento dei loop
HeaderValoreScopo
True-Client-IPIP del clientIndirizzo IP originale dell’utente finale
X-Akamai-EdgescapeStringa geo compostageoregion, 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-CharacteristicsProprietà del dispositivobrand_name, model_name, is_mobile, is_tablet, is_wireless_device, device_os, device_os_version, resolution_width, resolution_height
X-Akamai-Request-IDUUID della richiestaIdentificatore univoco della richiesta
HeaderValoreScopo
CF-Connecting-IPIP del clientIP reale del client (sempre presente)
CF-IPCountryUSCodice paese a due lettere
cf-ipcitySan JoseCittà del client
cf-ipcontinentNACodice continente
cf-iplatitude / cf-iplongitudeCoordinateGeolocalizzazione
cf-region / cf-region-codeCalifornia / CAInformazioni sulla regione
cf-metro-code807Codice area metropolitana US
cf-postal-code95113Codice postale
cf-timezoneAmerica/Los_AngelesFuso orario IANA
Cf-Ray{request_id}-SJCID ray univoco con codice IATA del POP
CF-Visitor{"scheme":"https"}Informazioni sul protocollo del visitatore
cf-bot-score85Punteggio bot (1=bot, 99=umano)
cf-verified-botfalseFlag bot legittimo noto
cf-ja3-hashe7d705a3286e19ea42f587b344ee6865Fingerprint TLS JA3
cf-ja4t13d1516h2_8daaf6152771_b0da82dd1658Fingerprint TLS JA4
HeaderValoreScopo
CloudFront-Viewer-AddressIP:portaIP del client e porta sorgente
CloudFront-Viewer-CountryUSCodice paese
CloudFront-Viewer-Country-NameUnited StatesNome completo del paese
CloudFront-Viewer-Country-RegionCACodice regione
CloudFront-Viewer-Country-Region-NameCaliforniaNome completo della regione
CloudFront-Viewer-CitySan JoseCittà del client
CloudFront-Viewer-Postal-Code95113Codice postale
CloudFront-Viewer-Latitude / Longitude37.33530 / -121.89300Geolocalizzazione
CloudFront-Viewer-Time-ZoneAmerica/Los_AngelesFuso orario IANA
CloudFront-Viewer-Metro-Code807Codice area metropolitana US
CloudFront-Viewer-ASN7018Numero di Sistema Autonomo
CloudFront-Viewer-Http-Version2.0Versione HTTP del client
CloudFront-Forwarded-ProtohttpsProtocollo originale
CloudFront-Viewer-TLSTLSv1.3:TLS_AES_128_GCM_SHA256:sessionResumedDettagli TLS
CloudFront-Viewer-JA3-Fingerprinte7d705a3286e19ea42f587b344ee6865Fingerprint TLS JA3
CloudFront-Is-Desktop-Viewertrue/falseRilevamento dispositivo
CloudFront-Is-Mobile-Viewertrue/falseRilevamento dispositivo
CloudFront-Is-Tablet-Viewertrue/falseRilevamento dispositivo
CloudFront-Is-SmartTV-ViewerfalseRilevamento dispositivo
X-Amz-Cf-IdID codificatoIdentificatore richiesta CloudFront
HeaderValoreScopo
Fastly-Client-IPIP del clientIP reale del client
Fastly-SSL1La connessione era su TLS
Fastly-Client1Richiesta lato client (non shield)
Fastly-FFcache-sjc3120-SJCIdentificazione del nodo cache
X-Geo-Country-CodeUSPaese (convenzione variabile VCL)
X-Geo-Country-Code3USACodice paese a tre lettere
X-Geo-Country-NameUnited StatesNome completo del paese
X-Geo-CitySan JoseCittà del client
X-Geo-RegionCACodice regione
X-Geo-Continent-CodeNAContinente
X-Geo-Latitude / X-Geo-Longitude37.3353 / -121.8938Geolocalizzazione
X-Geo-Postal-Code95113Codice postale
X-Geo-Metro-Code807Codice area metropolitana US
X-Geo-ASN7018Numero di Sistema Autonomo
X-Geo-Conn-SpeedbroadbandClasse di velocità della connessione
X-Geo-Conn-TypewiredTipo di connessione
HeaderValoreScopo
X-Azure-ClientIPIP del clientIndirizzo IP del client
X-Azure-SocketIPIP del clientIP sorgente del socket TCP
X-Azure-RefStringa di riferimento codificataRiferimento univoco della richiesta per la risoluzione dei problemi
X-Azure-FDIDa0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1Identificatore della risorsa Front Door
X-Azure-RequestChainhops=1Conteggio hop per rilevamento loop

Header di Risposta (Aggiunti alle Risposte al Client)

Sezione intitolata “Header di Risposta (Aggiunti alle Risposte al Client)”
HeaderValoriScopo
X-Cache-StatusHIT, MISS, EXPIRED, STALE, UPDATINGComportamento della cache per questa richiesta
X-CDN-Edgecdn-simulatorIdentifica questo nodo edge
X-CDN-POPSJCCodice IATA del Point of Presence simulato
X-Served-Bycache-sjc3120-SJCNodo cache simulato in formato Fastly
X-Request-IDUUID (per richiesta)Identificatore univoco della richiesta

Il simulatore rileva il tipo di dispositivo dall’header User-Agent utilizzando le direttive map di NGINX:

  • Mobile: Corrisponde a iPhone, Android (non tablet), iPod, BlackBerry, Opera Mini, IEMobile
  • Tablet: Corrisponde a iPad, tablet Android, Kindle, PlayBook
  • Desktop: Default quando non corrisponde né mobile né tablet

Il tipo di dispositivo si riflette in:

  • CloudFront-Is-Desktop-Viewer / CloudFront-Is-Mobile-Viewer / CloudFront-Is-Tablet-Viewer
  • X-Akamai-Device-Characteristics (campi is_mobile, is_tablet, is_wireless_device)
Terminal window
ssh azureuser@<PUBLIC_IP>
# Aggiorna il server upstream
sudo sed -i 's|server .*;|server NEW_HOST:80;|' /etc/nginx/conf.d/cdn-edge.conf
# Svuota la cache e ricarica
sudo rm -rf /var/cache/nginx/cdn/*
sudo nginx -t && sudo systemctl reload nginx

In alternativa, aggiorna origin_host in terraform.tfvars ed esegui terraform apply per riprovisionare.

Terminal window
ssh azureuser@<PUBLIC_IP>
sudo rm -rf /var/cache/nginx/cdn/*
sudo systemctl reload nginx
Terminal window
# Conteggio oggetti e utilizzo disco
sudo find /var/cache/nginx/cdn -type f | wc -l
sudo du -sh /var/cache/nginx/cdn
# Log degli accessi recenti con stato della cache
tail -20 /var/log/nginx/access.log
Terminal window
# Connessioni in tempo reale e stati dei socket
ss -s
# Utilizzo CPU dei worker NGINX
top -bn1 | grep nginx
# Connessioni keepalive upstream
ss -tn state established dst <ORIGIN_IP> | wc -l
# Conteggio socket TIME_WAIT
ss -tn state time-wait | wc -l

Verificati in un test di carico continuo di 48 ore:

MetricaValore
Throughput di picco (in cache)172.540 req/s
Throughput sostenuto (in cache)85.000-103.000 req/s
Connessioni di picco15.000 concorrenti
Rapporto di cache hit100% (a cache riscaldata)
Memoria sotto carico1,2 GB stabile (8% di 16 GB)
CPU al picco100% (4 core - la CPU è il limite)
Errori durante il test di 48h0
Memory leakNessuno rilevato
Connection leakNessuno rilevato