ข้ามไปยังเนื้อหา

การกำหนดค่า NGINX

การจัดเตรียมด้วย cloud-init จะนำใช้งานการกำหนดค่า CDN edge ของ NGINX ที่ได้รับการปรับแต่งประสิทธิภาพอย่างสมบูรณ์ หน้านี้จะบันทึกทุกชั้นของการกำหนดค่า ตั้งแต่การปรับจูนเคอร์เนลไปจนถึงพฤติกรรมแคชและการฉีดเฮดเดอร์ของผู้ให้บริการ การตั้งค่าทั้งหมดได้รับการยืนยันภายใต้การทดสอบโหลดต่อเนื่อง 48 ชั่วโมงที่ยอดสูงสุด 172,540 req/s

ไฟล์วัตถุประสงค์
/etc/sysctl.d/99-cdn-tuning.confการปรับจูนเครือข่ายเคอร์เนล Linux
/etc/systemd/system/nginx.service.d/override.confขีดจำกัด file descriptor สำหรับ NGINX
/etc/security/limits.d/99-nginx.confขีดจำกัดระดับ OS สำหรับผู้ใช้ www-data
/etc/nginx/nginx.confการกำหนดค่าหลัก NGINX (workers, buffers, gzip, logging)
/etc/nginx/conf.d/cdn-edge.confการกำหนดค่า CDN proxy (cache, upstream, headers)

นำใช้งานผ่าน /etc/sysctl.d/99-cdn-tuning.conf ตอนบูต

พารามิเตอร์ค่าค่าเริ่มต้นวัตถุประสงค์
net.core.somaxconn655354096ขนาดคิว listen backlog
net.core.netdev_max_backlog655351000backlog ของแพ็กเก็ตขาเข้าต่อ CPU
net.ipv4.tcp_max_syn_backlog65535256คิวคำขอ SYN (ป้องกันการหลุดภายใต้การเพิ่มขึ้นอย่างรวดเร็ว)
net.ipv4.tcp_tw_reuse12นำ TIME_WAIT sockets กลับมาใช้สำหรับการเชื่อมต่อขาออก
net.ipv4.ip_local_port_range1024-6553532768-60999ช่วง ephemeral port (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 (min/default/max)
net.ipv4.tcp_wmem4K/65K/16M4K/16K/4Mบัฟเฟอร์ส่งต่อ socket (min/default/max)
net.ipv4.tcp_fin_timeout1560timeout FIN_WAIT_2 (ทำความสะอาด socket เร็วขึ้น)
net.ipv4.tcp_keepalive_time3007200เริ่ม keepalive probes หลังจาก idle 5 นาที
net.ipv4.tcp_slow_start_after_idle01รักษา congestion window ให้อุ่นบนการเชื่อมต่อที่ idle
net.ipv4.tcp_max_tw_buckets2000000~65536TIME_WAIT sockets สูงสุด
fs.file-max2097152แตกต่างกันขีดจำกัด file descriptor ระดับระบบ
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
}

Systemd override ที่ /etc/systemd/system/nginx.service.d/override.conf ตั้งค่า 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

ขนาด busy buffer 256k มีความสำคัญ — ต้องมากกว่าการตอบสนองที่แคชขนาดใหญ่ที่สุด (Juice Shop คือ 75 KB) การตั้งค่า 64k เดิมทำให้เกิดการ serialization ภายใต้โหลด

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;

แคช file descriptors และ metadata สำหรับออบเจกต์ที่แคช ช่วยขจัด syscalls stat() และ open() บนไฟล์ที่ร้อนแรง

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

ที่ 90K req/s กับ keepalive_requests 1000 การเชื่อมต่อถูก recycle ทุก 11 วินาที สร้าง TIME_WAIT sockets การเพิ่มเป็น 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 สำหรับการวิเคราะห์ latency การบันทึกแบบบัฟเฟอร์ (256k/flush 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 การเชื่อมต่อที่อุ่นแล้วไปยัง origin ร่วมกับ proxy_http_version 1.1 และ proxy_set_header Connection "" ช่วยขจัดค่าใช้จ่ายจาก TCP handshake ในทุก 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;
พารามิเตอร์ค่าวัตถุประสงค์
keys_zone=cdn_cache:32mshared memory 32 MBเก็บ cache keys และ metadata ประมาณ 256,000 รายการ
max_size=25gขีดจำกัดดิสก์ 25 GBใช้ส่วนใหญ่ของดิสก์ OS 30 GB; การขับออกแบบ LRU เมื่อถึงขีดจำกัด
inactive=24htimeout การไม่ใช้งาน 24 ชั่วโมงเนื้อหาที่ไม่ได้เข้าถึงนาน 24 ชั่วโมงจะถูกขับออก (ไม่ใช่ TTL)
use_temp_path=offเขียนตรงไปยัง cache dirหลีกเลี่ยงการย้ายไฟล์ข้าม filesystem
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;
Directiveค่าวัตถุประสงค์
proxy_cache_valid 200 301 302 4hTTL 4 ชั่วโมงเนื้อหาที่แคชถูกต้องนาน 4 ชั่วโมง (เพิ่มขึ้นเพื่อลดการลดลงของ throughput จากคลื่นการรีเฟรช)
proxy_cache_lock onการป้องกัน thundering herdมีเพียง 1 คำขอที่ไปยัง origin ต่อ URL ที่ยังไม่ได้แคช คำขออื่นรอการเติมแคช
proxy_cache_lock_age 3stimeout ของ lockหากคำขอแรกยังไม่เสร็จสิ้นใน 3s อนุญาตให้คำขออื่นผ่านได้
proxy_cache_background_update onการรีเฟรชแบบ zero-latencyให้บริการเนื้อหาที่ล้าสมัยทันทีขณะรีเฟรชในพื้นหลัง
proxy_cache_use_staleความยืดหยุ่นให้บริการเนื้อหาที่ล้าสมัยระหว่าง origin errors (500/502/503/504) หรือขณะอัปเดต
proxy_ignore_headersบังคับการแคชละเว้น Set-Cookie, Cache-Control, Expires, Vary ของ origin — CDN เป็นผู้กำหนด TTL และพฤติกรรม Vary (Juice Shop ส่ง max-age=0 และเฮดเดอร์ Vary สามตัวซึ่งทำให้ไม่สามารถแคชได้อย่างมีประสิทธิภาพ)
proxy_hide_header X-Cache-Statusซ่อนเฮดเดอร์ originNGINX ของ origin เพิ่ม X-Cache-Status ของตัวเอง — ซ่อนมันเพื่อให้มองเห็นเฉพาะสถานะแคชของ CDN
proxy_hide_header Varyป้องกันการแตกกระจายของแคชOrigin ส่งเฮดเดอร์ Vary: Accept-Encoding หลายตัว การซ่อนพวกมันป้องกันการแตกกระจายของ cache key ข้ามวิธีการ Accept-Encoding ต่างๆ gzip_vary on ของ NGINX จะเพิ่มเฮดเดอร์ Vary เดี่ยวที่ถูกต้องโดยอัตโนมัติ
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
proxy_send_timeout 15s;

ให้เวลา origin มากขึ้นในการตอบสนองภายใต้โหลด ขณะยังคงล้มเหลวเร็วบนปัญหาการเชื่อมต่อ

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

reuseport เปิดใช้งาน SO_REUSEPORT — เคอร์เนลกระจายการเชื่อมต่อขาเข้าโดยตรงไปยัง worker processes ขจัดการแย่ชิง accept mutex

ตัวจำลองฉีดเฮดเดอร์จากผู้ให้บริการ CDN หลักทั้งห้ารายพร้อมกัน ซึ่งช่วยให้สามารถกำหนดค่า F5 XC ด้วย “Trusted Client IP Header” ของผู้ให้บริการรายใดก็ได้ และเห็น payload ของเฮดเดอร์ที่สมจริงโดยไม่คำนึงว่ากำลังจำลอง CDN ใดอยู่

เฮดเดอร์ค่าวัตถุประสงค์
X-Forwarded-Forห่วงโซ่ IP ของไคลเอนต์IP ที่ถูกส่งต่อตามมาตรฐาน
X-Forwarded-Protohttp หรือ httpsโปรโตคอลดั้งเดิมของไคลเอนต์
X-Forwarded-Hostชื่อโฮสต์ดั้งเดิมเฮดเดอร์ Host ดั้งเดิม
X-Forwarded-Portพอร์ตของเซิร์ฟเวอร์พอร์ตดั้งเดิม
X-Real-IPIP ของไคลเอนต์IP ไคลเอนต์เดี่ยว (ตามแบบแผน nginx)
Via1.1 cdn-simulatorการระบุตัวตน proxy
Forwardedรูปแบบ RFC 7239เฮดเดอร์การส่งต่อที่ได้มาตรฐาน
CDN-Loopcdn-simulatorการตรวจจับ loop
เฮดเดอร์ค่าวัตถุประสงค์
True-Client-IPIP ของไคลเอนต์ที่อยู่ IP ของผู้ใช้ปลายทางดั้งเดิม
X-Akamai-Edgescapeสตริง geo แบบผสม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-IDRequest UUIDตัวระบุคำขอที่ไม่ซ้ำกัน
เฮดเดอร์ค่าวัตถุประสงค์
CF-Connecting-IPIP ของไคลเอนต์IP ไคลเอนต์ที่แท้จริง (มีเสมอ)
CF-IPCountryUSรหัสประเทศสองตัวอักษร
cf-ipcitySan Joseเมืองของไคลเอนต์
cf-ipcontinentNAรหัสทวีป
cf-iplatitude / cf-iplongitudeพิกัดข้อมูลตำแหน่งทางภูมิศาสตร์
cf-region / cf-region-codeCalifornia / CAข้อมูลภูมิภาค
cf-metro-code807รหัส metro ของสหรัฐอเมริกา
cf-postal-code95113รหัสไปรษณีย์
cf-timezoneAmerica/Los_Angelestimezone IANA
Cf-Ray{request_id}-SJCRay ID ที่ไม่ซ้ำกันพร้อมรหัส IATA ของ POP
CF-Visitor{"scheme":"https"}ข้อมูลโปรโตคอลของผู้เยี่ยมชม
cf-bot-score85คะแนน bot (1=bot, 99=human)
cf-verified-botfalseแฟล็ก bot ที่รู้จักและดี
cf-ja3-hashe7d705a3286e19ea42f587b344ee6865fingerprint TLS แบบ JA3
cf-ja4t13d1516h2_8daaf6152771_b0da82dd1658fingerprint TLS แบบ JA4
เฮดเดอร์ค่าวัตถุประสงค์
CloudFront-Viewer-AddressIP:portIP ไคลเอนต์และพอร์ตต้นทาง
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_Angelestimezone IANA
CloudFront-Viewer-Metro-Code807รหัส metro ของสหรัฐอเมริกา
CloudFront-Viewer-ASN7018หมายเลข Autonomous System
CloudFront-Viewer-Http-Version2.0เวอร์ชัน HTTP ของไคลเอนต์
CloudFront-Forwarded-Protohttpsโปรโตคอลดั้งเดิม
CloudFront-Viewer-TLSTLSv1.3:TLS_AES_128_GCM_SHA256:sessionResumedรายละเอียด TLS
CloudFront-Viewer-JA3-Fingerprinte7d705a3286e19ea42f587b344ee6865fingerprint TLS แบบ JA3
CloudFront-Is-Desktop-Viewertrue/falseการตรวจจับอุปกรณ์
CloudFront-Is-Mobile-Viewertrue/falseการตรวจจับอุปกรณ์
CloudFront-Is-Tablet-Viewertrue/falseการตรวจจับอุปกรณ์
CloudFront-Is-SmartTV-Viewerfalseการตรวจจับอุปกรณ์
X-Amz-Cf-IdID ที่เข้ารหัสตัวระบุคำขอ CloudFront
เฮดเดอร์ค่าวัตถุประสงค์
Fastly-Client-IPIP ของไคลเอนต์IP ไคลเอนต์ที่แท้จริง
Fastly-SSL1การเชื่อมต่อผ่าน TLS
Fastly-Client1คำขอฝั่งไคลเอนต์ (ไม่ใช่ shield)
Fastly-FFcache-sjc3120-SJCการระบุตัวตน cache node
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รหัส metro ของสหรัฐอเมริกา
X-Geo-ASN7018หมายเลข Autonomous System
X-Geo-Conn-Speedbroadbandประเภทความเร็วการเชื่อมต่อ
X-Geo-Conn-Typewiredประเภทการเชื่อมต่อ
เฮดเดอร์ค่าวัตถุประสงค์
X-Azure-ClientIPIP ของไคลเอนต์ที่อยู่ IP ของไคลเอนต์
X-Azure-SocketIPIP ของไคลเอนต์IP ต้นทาง TCP socket
X-Azure-Refสตริง ref ที่เข้ารหัสการอ้างอิงคำขอที่ไม่ซ้ำกันสำหรับการแก้ไขปัญหา
X-Azure-FDIDa0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1ตัวระบุทรัพยากร Front Door
X-Azure-RequestChainhops=1จำนวน hop สำหรับการตรวจจับ loop

เฮดเดอร์การตอบสนอง (เพิ่มในการตอบสนองของไคลเอนต์)

หัวข้อที่มีชื่อว่า “เฮดเดอร์การตอบสนอง (เพิ่มในการตอบสนองของไคลเอนต์)”
เฮดเดอร์ค่าวัตถุประสงค์
X-Cache-StatusHIT, MISS, EXPIRED, STALE, UPDATINGพฤติกรรมแคชสำหรับคำขอนี้
X-CDN-Edgecdn-simulatorระบุตัวตน edge node นี้
X-CDN-POPSJCรหัส IATA ของ Point of Presence ที่จำลอง
X-Served-Bycache-sjc3120-SJCcache node ที่จำลองในรูปแบบ Fastly
X-Request-IDUUID (ต่อคำขอ)ตัวระบุคำขอที่ไม่ซ้ำกัน

ตัวจำลองตรวจจับประเภทอุปกรณ์จากเฮดเดอร์ User-Agent โดยใช้คำสั่ง NGINX map:

  • มือถือ: ตรงกับ iPhone, Android (ที่ไม่ใช่แท็บเล็ต), iPod, BlackBerry, Opera Mini, IEMobile
  • แท็บเล็ต: ตรงกับ iPad, Android tablet, Kindle, PlayBook
  • เดสก์ท็อป: ค่าเริ่มต้นเมื่อไม่ตรงกับมือถือหรือแท็บเล็ต

ประเภทอุปกรณ์ถูกสะท้อนใน:

  • CloudFront-Is-Desktop-Viewer / CloudFront-Is-Mobile-Viewer / CloudFront-Is-Tablet-Viewer
  • X-Akamai-Device-Characteristics (ฟิลด์ is_mobile, is_tablet, is_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

หรืออัปเดต origin_host ใน terraform.tfvars และรัน 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 ชั่วโมง:

ตัวชี้วัดค่า
throughput สูงสุด (แคช)172,540 req/s
throughput ที่ยั่งยืน (แคช)85,000-103,000 req/s
การเชื่อมต่อสูงสุด15,000 concurrent
อัตราการ hit แคช100% (เมื่อ warm แล้ว)
หน่วยความจำภายใต้โหลด1.2 GB เสถียร (8% ของ 16 GB)
CPU ที่ยอดสูงสุด100% (4 cores - CPU คือเพดาน)
ข้อผิดพลาดระหว่างการทดสอบ 48h0
การรั่วไหลของหน่วยความจำไม่พบ
การรั่วไหลของการเชื่อมต่อไม่พบ