وقتی وب‌سروری با چند هزار اتصال همزمان شروع به رد کردن درخواست‌ها می‌کند، مشکل اغلب نه در Nginx است و نه در PHP، بلکه در پیش‌فرض‌های کرنل لینوکس که برای یک لپ‌تاپ یا سرور کم‌بار تنظیم شده‌اند. ابزار sysctl راه رسمی تغییر این پارامترها در لحظه و بدون ریبوت است: اندازهٔ صف اتصالات، محدودهٔ پورت‌ها، بافرهای TCP، الگوریتم کنترل ازدحام و رفتار حافظهٔ مجازی. در این راهنما ابتدا یاد می‌گیرید چگونه گلوگاه را اندازه بگیرید، سپس پارامترهای شبکه، حافظه و امنیت را با مقادیر پیش‌فرض و پیشنهادی مرور می‌کنیم و در پایان یک فایل پیکربندی کامل برای وب‌سرور پرترافیک ارائه می‌دهیم.

sysctl چیست و تنظیمات کرنل کجا ذخیره می‌شوند

کرنل لینوکس صدها پارامتر قابل تنظیم را از طریق شبه‌فایل‌سیستم /proc/sys در معرض دید قرار می‌دهد؛ هر فایل در این مسیر یک پارامتر است و نوشتن در آن، تنظیم را بلافاصله تغییر می‌دهد. sysctl رابط کاربرپسند همین مسیر است: نام نقطه‌دار net.core.somaxconn معادل فایل /proc/sys/net/core/somaxconn است.

اعمال موقت در برابر دائمی

دستور sysctl -w net.core.somaxconn=65535 تنظیم را فقط تا ریبوت بعدی نگه می‌دارد. برای دائمی شدن باید آن را در فایلی داخل /etc/sysctl.d/ بنویسید؛ فایل‌ها به ترتیب نام خوانده می‌شوند و فایل با شمارهٔ بزرگ‌تر، مقادیر قبلی را بازنویسی می‌کند. در Ubuntu 24.04 فایل‌های پیش‌فرض با شماره‌های 10 تا 50 قرار دارند، بنابراین تنظیمات خود را در فایلی مانند 99-web.conf بگذارید. دستور sysctl --system همهٔ این فایل‌ها را به ترتیب اعمال می‌کند و خروجی آن نشان می‌دهد کدام مقدار از کدام فایل آمده است.

پیش از تیونینگ: گلوگاه را اندازه بگیرید

کپی کردن یک فایل sysctl از اینترنت بدون اندازه‌گیری، رایج‌ترین اشتباه است. سه دستور به شما می‌گویند آیا اصلاً مشکلی در سطح کرنل وجود دارد یا نه:

ss -s                                  # تعداد سوکت‌ها به تفکیک وضعیت
nstat -az | grep -Ei 'ListenOverflows|ListenDrops|SyncookiesSent'
cat /proc/net/sockstat                 # حافظهٔ مصرفی TCP
ss -ltn                                # ستون Send-Q = اندازهٔ backlog هر listener

اگر شمارندهٔ TcpExtListenOverflows در حال افزایش است، صف پذیرش اتصالات پر می‌شود و بخش بعدی دقیقاً برای شماست. اگر تعداد سوکت‌های TIME-WAIT به ده‌ها هزار رسیده، محدودهٔ پورت تنگ شده است. اگر هیچ‌کدام دیده نمی‌شود، احتمالاً گلوگاه در PHP-FPM، دیتابیس یا دیسک است و دستکاری sysctl کمکی نخواهد کرد.

تنظیمات شبکه برای تعداد زیاد اتصال

صف‌های پذیرش: somaxconn و tcp_max_syn_backlog

هر سوکت listen دو صف دارد: صف SYN برای اتصالات نیمه‌باز (کنترل‌شده با net.ipv4.tcp_max_syn_backlog) و صف accept برای اتصالات کامل‌شده‌ای که هنوز برنامه آن‌ها را نگرفته است (سقف آن net.core.somaxconn). پیش‌فرض somaxconn از کرنل 5.4 به 4096 رسیده (قبلاً 128 بود) و برای وب‌سرور پرترافیک 65535 منطقی است. نکتهٔ مهم: این فقط سقف است؛ Nginx باید در دستور listen پارامتر backlog=65535 را بدهد، وگرنه مقدار پیش‌فرض خودش (511) اعمال می‌شود.

محدودهٔ پورت و TIME_WAIT

هر اتصال خروجی (مثلاً از Nginx به PHP-FPM روی TCP یا به دیتابیس) یک پورت محلی از محدودهٔ net.ipv4.ip_local_port_range مصرف می‌کند که پیش‌فرض آن 32768 تا 60999 یعنی حدود 28 هزار پورت است. پس از بسته شدن، سوکت به مدت net.ipv4.tcp_fin_timeout (پیش‌فرض 60) در حالت TIME_WAIT می‌ماند. با 500 اتصال خروجی در ثانیه، این محدوده در کمتر از یک دقیقه تمام می‌شود. راه‌حل: محدوده را به 1024 تا 65535 گسترش دهید، net.ipv4.tcp_tw_reuse را روی 1 بگذارید (پیش‌فرض 2 یعنی فقط برای loopback) و fin_timeout را به 15 کاهش دهید. بهترین راه‌حل البته استفاده از Unix socket برای PHP-FPM و keepalive برای upstream است تا اصلاً پورت مصرف نشود.

بافرهای سوکت و صف کارت شبکه

net.core.rmem_max و wmem_max سقف بافر دریافت و ارسال هر سوکت را تعیین می‌کنند (پیش‌فرض حدود 212 کیلوبایت) و net.ipv4.tcp_rmem و tcp_wmem سه مقدار حداقل، پیش‌فرض و حداکثر برای TCP هستند که کرنل به‌صورت خودکار بین آن‌ها تنظیم می‌کند. برای سروری که به کاربران دور (مسیرهای بین‌المللی با تأخیر 100 میلی‌ثانیه) فایل‌های بزرگ می‌دهد، بالا بردن حداکثر به 16 مگابایت throughput را به‌طور محسوس افزایش می‌دهد؛ برای کاربران داخلی با تأخیر پایین، تأثیر کمتر است. net.core.netdev_max_backlog (پیش‌فرض 1000) صف بسته‌های دریافتی قبل از پردازش توسط کرنل است و روی پورت‌های 10 گیگابیت باید به 5000 یا بیشتر برسد.

کنترل ازدحام و تأخیر: BBR و fq

الگوریتم پیش‌فرض CUBIC بر اساس از دست رفتن بسته عمل می‌کند و روی مسیرهای پرتأخیر یا با افت بستهٔ تصادفی (موبایل، مسیرهای بین‌الملل) سرعت را بی‌جهت کم می‌کند. BBR که گوگل توسعه داده، بر اساس پهنای باند و RTT اندازه‌گیری‌شده تصمیم می‌گیرد و در اغلب سرورهای وب، زمان بارگذاری را برای کاربران دور بهبود می‌دهد. BBR نیازمند زمان‌بند صف fq است:

modprobe tcp_bbr
sysctl net.ipv4.tcp_available_congestion_control
# net.ipv4.tcp_available_congestion_control = reno cubic bbr
echo 'net.core.default_qdisc = fq' > /etc/sysctl.d/90-bbr.conf
echo 'net.ipv4.tcp_congestion_control = bbr' >> /etc/sysctl.d/90-bbr.conf
sysctl --system

دو تنظیم مکمل: net.ipv4.tcp_slow_start_after_idle=0 مانع از این می‌شود که اتصال keepalive پس از لحظه‌ای بیکاری دوباره از سرعت صفر شروع کند، و net.ipv4.tcp_mtu_probing=1 مشکلات ناشی از مسدود بودن ICMP در مسیر را خودکار دور می‌زند. کاربران ایرانی که از مسیرهای مختلف داخلی و بین‌المللی به سرور شما می‌رسند، از هر دو بهره می‌برند.

تنظیمات حافظه و فایل‌سیستم

vm.swappiness و صفحات کثیف

برای وب‌سرور، vm.swappiness=10 جلوی Swap شدن پروسه‌های PHP را می‌گیرد. دو پارامتر vm.dirty_ratio (پیش‌فرض 20 درصد) و vm.dirty_background_ratio (پیش‌فرض 10 درصد) تعیین می‌کنند چه درصدی از RAM می‌تواند داده‌های نوشته‌شده اما هنوز روی دیسک ذخیره‌نشده نگه دارد. روی سروری با 64 گیگابایت RAM، پیش‌فرض یعنی تا 12 گیگابایت دادهٔ معلق که هنگام fsync یا کرش، توقف‌های چند ثانیه‌ای و از دست رفتن داده به همراه دارد. مقادیر 10 و 5 برای سرورهای دیتابیس و وب مناسب‌تر است.

fs.file-max، nr_open و inotify

fs.file-max سقف کل descriptorهای باز در سیستم است و در کرنل‌های اخیر به‌طور خودکار بر اساس RAM مقدار بسیار بزرگی می‌گیرد؛ آنچه معمولاً محدود می‌کند، fs.nr_open (پیش‌فرض 1048576، سقف هر پروسه) و ulimit سرویس‌هاست. fs.inotify.max_user_watches را اگر ابزارهای مانیتورینگ فایل یا بیلد دارید به 524288 برسانید. vm.max_map_count (پیش‌فرض 65530) فقط برای Elasticsearch، OpenSearch و برخی اپلیکیشن‌های جاوا باید به 262144 برسد.

تنظیمات امنیتی که باید همراه تیونینگ باشند

هر فایل تیونینگ باید این موارد را هم داشته باشد، وگرنه سرعت بیشتر با سطح حملهٔ بیشتر همراه می‌شود: net.ipv4.tcp_syncookies=1 (پیش‌فرض فعال، در برابر SYN flood وقتی صف SYN پر می‌شود)، net.ipv4.conf.all.rp_filter=1 یا 2 برای رد بسته‌هایی با آدرس مبدأ جعلی، غیرفعال کردن accept_redirects و send_redirects در all و default برای IPv4 و IPv6 (سرور وب هیچ‌وقت نباید مسیر خود را با پیام ICMP تغییر دهد)، accept_source_route=0 و log_martians=1 برای ثبت بسته‌های مشکوک در لاگ کرنل. اگر سرور شما روتر یا Docker host نیست، net.ipv4.ip_forward باید صفر بماند.

نمونهٔ کامل فایل /etc/sysctl.d/99-web.conf

جدول زیر پارامترهای اصلی را با پیش‌فرض و مقدار پیشنهادی برای وب‌سروری با 8 تا 32 گیگابایت RAM که چند هزار اتصال همزمان دارد، خلاصه می‌کند. این مقادیر نقطهٔ شروع هستند و با اندازه‌گیری باید اصلاح شوند.

پارامترپیش‌فرضپیشنهادیاثر
net.core.somaxconn409665535صف accept
net.ipv4.tcp_max_syn_backlogوابسته به RAM65535صف SYN
net.ipv4.ip_local_port_range32768 609991024 65535پورت‌های خروجی
net.ipv4.tcp_tw_reuse21بازاستفادهٔ TIME_WAIT
net.ipv4.tcp_fin_timeout6015عمر TIME_WAIT
net.core.rmem_max / wmem_max21299216777216سقف بافر سوکت
net.ipv4.tcp_congestion_controlcubicbbrکنترل ازدحام
net.ipv4.tcp_keepalive_time7200600پاک‌سازی اتصالات مرده
vm.swappiness6010کاهش Swap
vm.dirty_ratio2010صفحات کثیف
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
vm.swappiness = 10

پس از ذخیره، sysctl --system را اجرا کنید و با sysctl net.ipv4.tcp_congestion_control مطمئن شوید مقدار جدید اعمال شده است. اگر سایت شما روی هاست پرترافیک میزبانی می‌شود، این تنظیمات در سطح سرور توسط ارائه‌دهنده انجام شده و نیازی به دسترسی root ندارید؛ روی سرور مجازی ایران با KVM، همهٔ این پارامترها در اختیار شماست.

اشتباهات رایج و زمان‌هایی که نباید sysctl را دستکاری کرد

  • کپی کردن فایل‌های «بهینه‌سازی» بی‌منبع با ده‌ها پارامتر ناشناخته؛ هر خطی که معنای آن را نمی‌دانید، حذف کنید.
  • بافرهای عظیم روی سرور کم‌حافظه؛ tcp_rmem با حداکثر 64 مگابایت روی 2 گیگابایت RAM، با چند صد اتصال، حافظه را تمام می‌کند.
  • انتظار معجزه؛ اگر PHP-FPM با pm.max_children=20 گلوگاه است، هیچ تنظیم کرنلی کمکی نمی‌کند.

جمع‌بندی

تیونینگ کرنل با sysctl وقتی ارزش دارد که اندازه‌گیری نشان دهد گلوگاه واقعاً در کرنل است: صف‌های پر، پورت‌های تمام‌شده یا بافرهای کوچک برای مسیرهای پرتأخیر. در این صورت ده پارامتر کلیدی شبکه، فعال‌سازی BBR، کاهش swappiness و dirty_ratio و چند تنظیم امنیتی، تفاوت محسوسی در ظرفیت وب‌سرور ایجاد می‌کنند. تغییرات را در /etc/sysctl.d نگه دارید، یکی‌یکی اعمال کنید و نتیجه را با ss و nstat بسنجید.

سوالات متداول

تغییرات sysctl بعد از ریبوت از بین می‌روند؟

اگر با sysctl -w اعمال شده باشند، بله. برای دائمی شدن باید در فایلی داخل /etc/sysctl.d/ نوشته شوند که هنگام بوت توسط systemd-sysctl خوانده می‌شود.

آیا BBR روی هر سروری قابل فعال‌سازی است؟

روی هر کرنل 4.9 به بالا که ماژول tcp_bbr داشته باشد، یعنی عملاً همهٔ توزیع‌های فعلی و سرورهای مجازی KVM. در کانتینرهای OpenVZ یا LXC الگوریتم کنترل ازدحام توسط میزبان تعیین می‌شود و قابل تغییر نیست.

چگونه بفهمم تنظیم somaxconn واقعاً اثر کرده است؟

دستور ss -ltn را اجرا کنید؛ ستون Send-Q برای هر پورت listen، اندازهٔ backlog مؤثر را نشان می‌دهد. اگر هنوز 511 است، Nginx را با backlog=65535 در دستور listen پیکربندی و ری‌استارت کنید.

آیا tcp_tw_reuse امن است؟

بله؛ برخلاف tcp_tw_recycle حذف‌شده، tcp_tw_reuse فقط برای اتصالات خروجی و با بررسی timestamp عمل می‌کند و با NAT مشکلی ایجاد نمی‌کند. مقدار 1 برای سرورهایی که اتصالات خروجی زیاد به دیتابیس یا upstream دارند، استاندارد است.

sysctl روی هاست اشتراکی قابل تغییر است؟

خیر. این پارامترها در سطح کرنل سرور هستند و فقط مدیر سرور با دسترسی root می‌تواند آن‌ها را تغییر دهد.