跳转到内容

NGINX 配置

cloud-init 配置部署了一套经过全面性能优化的 NGINX CDN 边缘配置。本页面记录了从内核调优到缓存行为再到厂商请求头注入的每一个配置层。所有设置均通过了 48 小时的持续负载测试验证,峰值达到 172,540 请求/秒。

文件用途
/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 主配置(worker、缓冲区、gzip、日志)
/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 套接字
net.ipv4.ip_local_port_range1024-6553532768-60999临时端口范围(64K 对比 28K)
net.core.rmem_max16 MB212 KB最大接收套接字缓冲区
net.core.wmem_max16 MB212 KB最大发送套接字缓冲区
net.ipv4.tcp_rmem4K/87K/16M4K/131K/6M每套接字接收缓冲区(最小/默认/最大)
net.ipv4.tcp_wmem4K/65K/16M4K/16K/4M每套接字发送缓冲区(最小/默认/最大)
net.ipv4.tcp_fin_timeout1560FIN_WAIT_2 超时(更快的套接字清理)
net.ipv4.tcp_keepalive_time3007200空闲 5 分钟后开始 keepalive 探测
net.ipv4.tcp_slow_start_after_idle01在空闲连接上保持拥塞窗口预热
net.ipv4.tcp_max_tw_buckets2000000~65536最大 TIME_WAIT 套接字数
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 请求/秒时使用 keepalive_requests 1000,连接每 11 秒就会回收一次,产生大量 TIME_WAIT 套接字。增加到 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 个 worker 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 操作系统磁盘的大部分空间;达到限制时 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 仅一个请求回源;其他请求等待缓存填充
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 互斥锁竞争。

模拟器同时注入所有五大 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带有 POP IATA 代码的唯一 Ray ID
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 套接字源 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 请求/秒
持续吞吐量(缓存命中)85,000-103,000 请求/秒
峰值连接数15,000 并发
缓存命中率100%(预热后)
负载下内存使用1.2 GB 稳定(16 GB 的 8%)
峰值 CPU 使用100%(4 核——CPU 是瓶颈)
48 小时测试中的错误0
内存泄漏未检测到
连接泄漏未检测到