跳到內容

NGINX 配置

cloud-init 佈建會部署一套完全效能最佳化的 NGINX CDN 邊緣配置。本頁面記錄了從核心調校、快取行為到供應商標頭注入的每一個配置層級。所有設定均在峰值達 172,540 req/s 的 48 小時連續負載測試下驗證通過。

檔案用途
/etc/sysctl.d/99-cdn-tuning.confLinux 核心網路調校
/etc/systemd/system/nginx.service.d/override.confNGINX 的檔案描述符限制
/etc/security/limits.d/99-nginx.confwww-data 使用者的作業系統層級限制
/etc/nginx/nginx.confNGINX 主配置(workers、buffers、gzip、logging)
/etc/nginx/conf.d/cdn-edge.confCDN 代理配置(快取、上游、標頭)

透過 /etc/sysctl.d/99-cdn-tuning.conf 在開機時套用。

參數預設值用途
net.core.somaxconn655354096監聽佇列大小
net.core.netdev_max_backlog655351000每 CPU 傳入封包佇列
net.ipv4.tcp_max_syn_backlog65535256SYN 請求佇列(防止突發流量下的丟棄)
net.ipv4.tcp_tw_reuse12重用 TIME_WAIT socket 進行連出連線
net.ipv4.ip_local_port_range1024-6553532768-60999臨時埠範圍(64K 對比 28K)
net.core.rmem_max16 MB212 KB最大接收 socket 緩衝區
net.core.wmem_max16 MB212 KB最大傳送 socket 緩衝區
net.ipv4.tcp_rmem4K/87K/16M4K/131K/6M每 socket 接收緩衝區(最小/預設/最大)
net.ipv4.tcp_wmem4K/65K/16M4K/16K/4M每 socket 傳送緩衝區(最小/預設/最大)
net.ipv4.tcp_fin_timeout1560FIN_WAIT_2 逾時(更快的 socket 清理)
net.ipv4.tcp_keepalive_time3007200閒置 5 分鐘後開始 keepalive 探測
net.ipv4.tcp_slow_start_after_idle01在閒置連線上保持壅塞視窗活躍
net.ipv4.tcp_max_tw_buckets2000000~65536最大 TIME_WAIT socket 數
fs.file-max2097152視情況而定系統級檔案描述符限制
vm.swappiness1060快取資料優先使用 RAM 而非 swap
worker_processes auto; # 4 workers on D4s_v5 (1 per vCPU)
worker_rlimit_nofile 65535; # Per-worker file descriptor limit
events {
use epoll; # Linux-optimized event model
worker_connections 8192; # 4 workers x 8192 = 32,768 max concurrent
multi_accept on; # Accept all pending connections per event loop
accept_mutex off; # Not needed with epoll + reuseport
}

位於 /etc/systemd/system/nginx.service.d/override.conf 的 Systemd 覆寫設定 LimitNOFILE=65535 以匹配。

proxy_buffering on;
proxy_buffer_size 16k; # Header buffer (handles large CDN headers)
proxy_buffers 64 16k; # 1 MB per connection (64 x 16k)
proxy_busy_buffers_size 256k; # Can send 256k to client while still reading

256k 的 busy buffer 大小至關重要——它必須超過最大的快取回應(Juice Shop 為 75 KB)。原先的 64k 設定在高負載時導致了序列化問題。

gzip on;
gzip_comp_level 4; # Balance: ~85% of max compression at ~40% CPU
gzip_min_length 256; # Skip tiny responses (gzip header overhead)
gzip_vary on; # Vary: Accept-Encoding for correct caching
gzip_proxied any; # Compress all proxied responses
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;

快取已快取物件的檔案描述符和中繼資料,消除對熱門檔案的 stat()open() 系統呼叫。

keepalive_timeout 65;
keepalive_requests 100000; # Was 1000 — caused connection recycling at 90K req/s

在 90K req/s 且 keepalive_requests 1000 的情況下,連線每 11 秒回收一次,產生 TIME_WAIT socket。將其增加到 100,000 完全消除了此問題。

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;

cdn 日誌格式包含用於延遲分析的 $upstream_cache_status(HIT/MISS)和 $request_time。緩衝日誌記錄(256k/5s 刷新)減少了高負載下的 I/O 開銷。

upstream origin_backend {
server 20.12.78.159:80;
keepalive 256; # Persistent connections per worker to origin
keepalive_timeout 60s;
keepalive_requests 1000;
}

4 個 workers x 256 keepalive = 1,024 個到源站的暖連線。搭配 proxy_http_version 1.1proxy_set_header Connection "",這消除了每次快取未命中時的 TCP 交握開銷。

proxy_cache_path /var/cache/nginx/cdn
levels=1:2
keys_zone=cdn_cache:32m
max_size=25g
inactive=24h
use_temp_path=off;
參數用途
keys_zone=cdn_cache:32m32 MB 共享記憶體儲存約 256,000 個快取鍵和中繼資料
max_size=25g25 GB 磁碟限制使用 30 GB OS 磁碟的大部分空間;達到限制時 LRU 逐出
inactive=24h24 小時非活動逾時24 小時未存取的內容將被逐出(與 TTL 不同)
use_temp_path=off直接寫入快取目錄避免跨檔案系統的檔案搬移
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;
指令用途
proxy_cache_valid 200 301 302 4h4 小時 TTL快取內容有效期 4 小時(增加以減少刷新波所導致的吞吐量下降)
proxy_cache_lock on驚群效應預防每個未快取 URL 僅有 1 個請求到源站;其他請求等待快取填充
proxy_cache_lock_age 3s鎖定逾時如果第一個請求在 3 秒內未完成,允許另一個通過
proxy_cache_background_update on零延遲刷新立即提供過期內容,同時在背景刷新
proxy_cache_use_stale韌性在源站錯誤(500/502/503/504)或更新期間提供過期內容
proxy_ignore_headers強制快取忽略源站的 Set-Cookie、Cache-Control、Expires、Vary——由 CDN 決定 TTL 和 Vary 行為(Juice Shop 會傳送 max-age=0 和三重 Vary 標頭,這阻礙了有效快取)
proxy_hide_header X-Cache-Status移除源站標頭源站 NGINX 會添加自己的 X-Cache-Status——移除它以便只有 CDN 的快取狀態可見
proxy_hide_header Vary防止快取碎片化源站傳送多個 Vary: Accept-Encoding 標頭。移除它們可防止 Accept-Encoding 排列組合造成的快取鍵碎片化。NGINX 的 gzip_vary on 會自動添加正確的單一 Vary 標頭
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
proxy_send_timeout 15s;

在負載下給予源站更多回應時間,同時在連線問題上仍能快速失敗。

server {
listen 80 reuseport; # Kernel distributes connections across all 4 workers
server_name _;
}

reuseport 啟用 SO_REUSEPORT——核心將傳入連線直接分配給 worker 程序,消除 accept mutex 競爭。

模擬器同時注入所有五大 CDN 供應商的標頭。這使得 F5 XC 可以配置任何供應商的「受信任客戶端 IP 標頭」,並且無論模擬的是哪個 CDN,都能看到真實的標頭內容。

標頭用途
X-Forwarded-For客戶端 IP 鏈標準轉發 IP
X-Forwarded-Protohttphttps原始客戶端協定
X-Forwarded-Host原始主機名稱原始 Host 標頭
X-Forwarded-Port伺服器埠原始埠
X-Real-IP客戶端 IP單一客戶端 IP(nginx 慣例)
Via1.1 cdn-simulator代理識別
ForwardedRFC 7239 格式標準化轉發標頭
CDN-Loopcdn-simulator迴圈偵測
標頭用途
True-Client-IP客戶端 IP原始終端使用者 IP 位址
X-Akamai-Edgescape複合地理字串georegion、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-Characteristics裝置屬性brand_name、model_name、is_mobile、is_tablet、is_wireless_device、device_os、device_os_version、resolution_width、resolution_height
X-Akamai-Request-ID請求 UUID唯一請求識別碼
標頭用途
CF-Connecting-IP客戶端 IP真實客戶端 IP(始終存在)
CF-IPCountryUS兩字母國家代碼
cf-ipcitySan Jose客戶端城市
cf-ipcontinentNA大洲代碼
cf-iplatitude / cf-iplongitude座標地理定位
cf-region / cf-region-codeCalifornia / CA地區資訊
cf-metro-code807美國都會區代碼
cf-postal-code95113郵遞區號
cf-timezoneAmerica/Los_AngelesIANA 時區
Cf-Ray{request_id}-SJC唯一 Ray ID 含 POP IATA 代碼
CF-Visitor{"scheme":"https"}訪客協定資訊
cf-bot-score85機器人分數(1=機器人,99=人類)
cf-verified-botfalse已知良好機器人標記
cf-ja3-hashe7d705a3286e19ea42f587b344ee6865JA3 TLS 指紋
cf-ja4t13d1516h2_8daaf6152771_b0da82dd1658JA4 TLS 指紋
標頭用途
CloudFront-Viewer-AddressIP:port客戶端 IP 和來源埠
CloudFront-Viewer-CountryUS國家代碼
CloudFront-Viewer-Country-NameUnited States完整國家名稱
CloudFront-Viewer-Country-RegionCA地區代碼
CloudFront-Viewer-Country-Region-NameCalifornia完整地區名稱
CloudFront-Viewer-CitySan Jose客戶端城市
CloudFront-Viewer-Postal-Code95113郵遞區號
CloudFront-Viewer-Latitude / Longitude37.33530 / -121.89300地理定位
CloudFront-Viewer-Time-ZoneAmerica/Los_AngelesIANA 時區
CloudFront-Viewer-Metro-Code807美國都會區代碼
CloudFront-Viewer-ASN7018自治系統編號
CloudFront-Viewer-Http-Version2.0客戶端 HTTP 版本
CloudFront-Forwarded-Protohttps原始協定
CloudFront-Viewer-TLSTLSv1.3:TLS_AES_128_GCM_SHA256:sessionResumedTLS 詳細資訊
CloudFront-Viewer-JA3-Fingerprinte7d705a3286e19ea42f587b344ee6865JA3 TLS 指紋
CloudFront-Is-Desktop-Viewertrue/false裝置偵測
CloudFront-Is-Mobile-Viewertrue/false裝置偵測
CloudFront-Is-Tablet-Viewertrue/false裝置偵測
CloudFront-Is-SmartTV-Viewerfalse裝置偵測
X-Amz-Cf-Id編碼 IDCloudFront 請求識別碼
標頭用途
Fastly-Client-IP客戶端 IP真實客戶端 IP
Fastly-SSL1連線使用 TLS
Fastly-Client1面向客戶端的請求(非 shield)
Fastly-FFcache-sjc3120-SJC快取節點識別
X-Geo-Country-CodeUS國家(VCL 變數慣例)
X-Geo-Country-Code3USA三字母國家代碼
X-Geo-Country-NameUnited States完整國家名稱
X-Geo-CitySan Jose客戶端城市
X-Geo-RegionCA地區代碼
X-Geo-Continent-CodeNA大洲
X-Geo-Latitude / X-Geo-Longitude37.3353 / -121.8938地理定位
X-Geo-Postal-Code95113郵遞區號
X-Geo-Metro-Code807美國都會區代碼
X-Geo-ASN7018自治系統編號
X-Geo-Conn-Speedbroadband連線速度等級
X-Geo-Conn-Typewired連線類型
標頭用途
X-Azure-ClientIP客戶端 IP客戶端 IP 位址
X-Azure-SocketIP客戶端 IPTCP socket 來源 IP
X-Azure-Ref編碼參考字串用於疑難排解的唯一請求參考
X-Azure-FDIDa0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1Front Door 資源識別碼
X-Azure-RequestChainhops=1迴圈偵測跳數

回應標頭(添加到客戶端回應)

Section titled “回應標頭(添加到客戶端回應)”
標頭用途
X-Cache-StatusHITMISSEXPIREDSTALEUPDATING此請求的快取行為
X-CDN-Edgecdn-simulator識別此邊緣節點
X-CDN-POPSJC模擬的接入點 IATA 代碼
X-Served-Bycache-sjc3120-SJCFastly 格式的模擬快取節點
X-Request-IDUUID(每個請求)唯一請求識別碼

模擬器使用 NGINX map 指令從 User-Agent 標頭偵測裝置類型:

  • 行動裝置:匹配 iPhone、Android(非平板)、iPod、BlackBerry、Opera Mini、IEMobile
  • 平板電腦:匹配 iPad、Android 平板、Kindle、PlayBook
  • 桌面電腦:當行動裝置和平板電腦均未匹配時的預設值

裝置類型反映在:

  • CloudFront-Is-Desktop-Viewer / CloudFront-Is-Mobile-Viewer / CloudFront-Is-Tablet-Viewer
  • X-Akamai-Device-Characteristicsis_mobileis_tabletis_wireless_device 欄位)
Terminal window
ssh azureuser@<PUBLIC_IP>
# Update upstream server
sudo sed -i 's|server .*;|server NEW_HOST:80;|' /etc/nginx/conf.d/cdn-edge.conf
# Clear cache and reload
sudo rm -rf /var/cache/nginx/cdn/*
sudo nginx -t && sudo systemctl reload nginx

或者更新 terraform.tfvars 中的 origin_host 並執行 terraform apply 以重新佈建。

Terminal window
ssh azureuser@<PUBLIC_IP>
sudo rm -rf /var/cache/nginx/cdn/*
sudo systemctl reload nginx
Terminal window
# Object count and disk usage
sudo find /var/cache/nginx/cdn -type f | wc -l
sudo du -sh /var/cache/nginx/cdn
# Recent access log with cache status
tail -20 /var/log/nginx/access.log
Terminal window
# Real-time connections and socket states
ss -s
# NGINX worker CPU usage
top -bn1 | grep nginx
# Upstream keepalive connections
ss -tn state established dst <ORIGIN_IP> | wc -l
# TIME_WAIT socket count
ss -tn state time-wait | wc -l

在 48 小時連續負載測試下驗證:

指標
峰值吞吐量(已快取)172,540 req/s
持續吞吐量(已快取)85,000-103,000 req/s
峰值連線數15,000 併發
快取命中率100%(暖機後)
負載下記憶體1.2 GB 穩定(16 GB 的 8%)
峰值 CPU100%(4 核心——CPU 是瓶頸)
48 小時測試期間錯誤數0
記憶體洩漏未偵測到
連線洩漏未偵測到