بازگشت به بلاگ
۹ شهریور ۱۴۰۵Sergei Solod9 دقیقه مطالعه

VPS من در FDCServers بیش از 3 ترابایت ترافیک را بدون مشکل جابه‌جا کرد؛ بعد خواندن از دیسک به 18 ثانیه رسید

VPS من در FDCServers ابتدا ترافیک واقعی را عادی پردازش کرد و بیش از 3 ترابایت داده انتقال داد. بعد، همان بار معمول باعث توقف‌های شدید دیسک مجازی شد: CPU iowait به 100٪ رسید، فشار I/O لینوکس به نزدیکی 100٪ رفت، latency خواندن 18.7 ثانیه شد و flush بیش از 53 ثانیه طول کشید.

FDCServersVPSLinuxورودی‌وخروجی دیسکDevOps

این متن را به‌عنوان نقد منفی FDCServers نمی‌نویسم و قصد ندارم بگویم دیگران باید از آن‌ها VPS بخرند یا نه. من یک سرور، یک workload و یک زنجیره مشخص از مشکلات داشتم. این برای قضاوت درباره کل یک شرکت میزبانی کافی نیست.

این صرفاً یکی از روزهای کاری من به‌عنوان توسعه‌دهنده است. VPS مدتی عادی کار می‌کرد و بیش از 3 ترابایت ترافیک منتقل کرده بود. بعد بار معمول شروع به ایجاد تأخیرهای شدید کرد. پاسخ صفحات گاهی بسیار طول می‌کشید یا کاملاً timeout می‌شد. ابتدا مظنون‌های معمول را بررسی کردم: application، Nginx، حافظه، شبکه، محدودیت connection یا بار زیاد. اما متریک‌های Linux به جای دیگری اشاره می‌کردند.

VPS از ابتدا کند نبود

بین رخدادها ماشین می‌توانست کاملاً سالم به نظر برسد. در یک بازه عادی هیچ process در D-state نبود، CPU iowait حدود 0٪ بود، latency دیسک تقریباً 2–4 ms و I/O PSI تقریباً صفر بود:

D-state processes: 0
CPU iowait:        ~0%
disk latency:      ~2–4 ms
I/O PSI some:      0.04
I/O PSI full:      0.04

اگر فقط در همان لحظه متصل می‌شدم و top، free -h، df -h و systemctl status nginx را می‌دیدم، احتمالاً نتیجه می‌گرفتم VPS سالم است. سپس workload معمول برگشت و وضعیت ماشین به‌شدت تغییر کرد.

خواندن‌های 18.7 ثانیه‌ای مسیر بررسی را عوض کردند

یکی از مهم‌ترین نمونه‌های iostat این بود:

r_await = 18744 ms
f_await = 53561 ms
aqu-sz  = 128.54
util    = 100.10%
read    = 88 KB/s

این اعداد باید کنار هم دیده شوند. میانگین خواندن‌های تکمیل‌شده حدود 18.7 ثانیه بود. latency مربوط به flush تقریباً 53.6 ثانیه بود. میانگین صف I/O از 128 عبور کرده بود، در حالی که throughput مفید خواندن فقط 88 KB/s بود. این صرفاً یک دیسک شلوغ با انتقال حجم زیادی از داده نبود؛ storage path زمان عظیمی را منتظر پایان عملیات می‌ماند.

iowait و PSI فشار I/O در کل سیستم را نشان دادند

در بدترین دوره‌ها، vmstat بین 4 تا 9 process مسدود، CPU iowait حدود 97–100٪ و CPU idle برابر 0٪ نشان می‌داد. iowait به این معنا نیست که application تمام CPU را مصرف می‌کند؛ یعنی کار مفید منتظر تکمیل I/Oهای در حال انتظار است.

Linux Pressure Stall Information تصویر را روشن‌تر کرد:

I/O PSI some avg10 = 99.14
I/O PSI full avg10 = 95.55

در بازتولیدهای بعدی full به حدود 98٪ نزدیک شد. Disk utilization نشان می‌دهد device مشغول است؛ PSI نشان می‌دهد workload واقعاً تا چه اندازه به‌خاطر آن resource متوقف شده است. فشار پایدار I/O در محدوده 95–98٪ یک افت کوچک performance نیست.

D-state و kernel stackها به لایه‌ای پایین‌تر از application اشاره کردند

بعد بررسی کردم چه processهایی مسدود شده‌اند. هم‌زمان jbd2، systemd-journald، workerهای Nginx، processهای cache آن و فعالیت‌های دیگر filesystem را در D-state می‌دیدم. اگر فقط application من گیر کرده بود، application را بررسی می‌کردم. وقتی Nginx، system journal و EXT4 journal هم‌زمان متوقف می‌شوند، storage واضح‌ترین وابستگی مشترک است.

مسیرهای journaling در EXT4 شامل این موارد بودند:

wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction

workerهای Nginx در خواندن معمول فایل منتظر بودند:

folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64

در یک لحظه kernel گزارش کرد یک task مربوط به Nginx بیش از 122 ثانیه blocked بوده است. Nginx همچنان می‌توانست active نمایش داده شود، اما این به معنی توانایی workerها در تکمیل کار مفید نبود. running با سالم بودن یکسان نیست.

پاسخ 3 ms از backend، application را از مسیر filesystem جدا کرد

تمیزترین مقایسه بین Nginx و backend محلی application بود. درخواست‌ها از طریق Nginx با HTTP=000 و timeout اتصال یا TLS شکست می‌خوردند، اما درخواست مستقیم به backend حدود 3 ms طول کشید:

connect = 0.000423 s
TTFB    = 0.003063 s
total   = 0.003139 s

HTTP status دقیق برای این تست مهم نبود. backend اتصال را پذیرفت، درخواست را پردازش کرد و تقریباً بلافاصله پاسخ داد. در همان زمان workerهای Nginx در انتظار خواندن EXT4 دیده می‌شدند. این تست اجرای عادی application را از مسیری که در جلوی آن به filesystem وابسته بود جدا کرد.

مشکل قابل بازتولید بود و دوباره هم می‌توانست ناپدید شود

قبل از یکی از بازتولیدها ماشین عادی به نظر می‌رسید:

HTTP:          200
D-state:       0
CPU iowait:    3%
r_await:       ~1.18 ms
I/O PSI full:  ~2.95%

حدود نیم دقیقه بعد:

D-state:       4
CPU iowait:    91%
CPU idle:      0%
I/O PSI some:  86.11%
I/O PSI full:  78.02%
r_await:       236.50 ms
HTTP:          000

بعدتر CPU iowait به 96–100٪ رسید، I/O PSI full به 98٪ نزدیک شد، صف HTTPS به 512 رسید و HTTP checkها همچنان شکست می‌خوردند. این بسیار مفیدتر از این بود که فقط بگویم VPS کند به نظر می‌رسد.

وقتی workload را حذف می‌کردم، تغییر معکوس می‌توانست سریع رخ دهد:

D-state:      0
CPU iowait:   6%
r_await:      ~0.98 ms
queue depth:  ~0.07
HTTP:         200

به همین دلیل بررسی storage problemهای مقطعی بعد از رخداد دشوار است. provider ممکن است پس از recovery سرور را ببیند و واقعاً latency عادی مشاهده کند، اما این چیزی درباره ده دقیقه قبل توضیح نمی‌دهد. timestamp دقیق UTC برای من ضروری شد.

دو متریک گمراه‌کننده: await=0 و فضای آزاد دیسک

در بعضی intervalهای بد، r_await = 0 می‌دیدم در حالی که iowait بالا بود، processها در D-state بودند، requestها همچنان in-flight بودند و تقریباً هیچ readی کامل نمی‌شد. آمار latency بر I/O تکمیل‌شده تکیه دارد. اگر operation در sampling interval کامل نشود، در میانگین latency عملیات تکمیل‌شده سهمی ندارد. بنابراین صفر همیشه به معنی دیسک فوری نیست.

پر بودن دیسک را هم بررسی کردم. بعدتر filesystem بیشتر از حدی که معمولاً اجازه می‌دهم پر شد، اما همان نوع failure قبلاً وقتی root filesystem فقط حدود 24٪ استفاده شده بود رخ داده بود. آن زمان حدود 1.2 GiB RAM آزاد بود، inode usage نزدیک 5٪ بود و network interface هیچ error یا dropped packet نشان نمی‌داد. پس پر بودن دیسک نمی‌توانست کل incident را توضیح دهد.

چه چیزی را می‌توانستم ثابت کنم و چه چیزی را نه

از داخل VPS می‌توانستم application، Linux VFS، EXT4 و virtual block device را ببینم. بعد از آن infrastructure provider قرار دارد: virtualization، distributed storage، storage network، deviceهای فیزیکی، scheduling و لایه‌های دیگری که از guest قابل بررسی نیستند.

بنابراین نمی‌توانم صادقانه بگویم یک SSD فیزیکی مشخص خراب بوده یا یک storage node، network path یا virtualization component خاص را root cause معرفی کنم.

چیزی که می‌توانم بگویم محدودتر ولی به‌خوبی مستند است: virtual storage path ارائه‌شده به Linux guest من بارها وارد حالتی شد که I/O عادی filesystem چند ثانیه طول می‌کشید یا در زمان معقول کامل نمی‌شد. شواهد از iostat، PSI، D-state، kernel wait stack، انتظارهای EXT4/jbd2، انتظارهای filesystem در Nginx، queue depth و request timing به دست آمد. برای تشخیص مهندسی من کافی بود، اما برای تعیین root cause فیزیکی نه.

چرا بیش از 3 ترابایت ترافیک قبلی با stallهای بعدی تناقض ندارد

ابتدا این موضوع برایم گیج‌کننده بود. اگر storage مشکل داشت، VPS چطور قبلاً چند ترابایت را موفق منتقل کرده بود؟

چون network traffic با physical disk I/O یکسان نیست. یک فایل می‌تواند یک بار از backing storage خوانده شود، در Linux page cache بماند و بعد بارها از RAM تحویل داده شود. بنابراین سه ترابایت انتقال شبکه به معنی سه ترابایت خواندن فیزیکی یکتا از دیسک نیست.

شرایط infrastructure نیز با زمان تغییر می‌کند: cache state، storage load، queueing، host placement و workloadهای دیگر ممکن است تغییر کنند. عملکرد عادی VPS در دیروز تضمین نمی‌کند storage امروز دقیقاً همان رفتار را داشته باشد.

بالاخره انتظار برای root cause عمیق‌تر را کنار گذاشتم

timestamp دقیق، vmstat، iostat، PSI، snapshot از processهای blocked، kernel stack، filesystem wait، queue depth و HTTP timing جمع کردم. diagnostics را برای FDCServers فرستادم و منتظر توضیح عمیق‌تر در سطح infrastructure ماندم.

مدت زیادی صبر کردم. در نهایت دیگر صبر نکردم. از سمت خودم اطلاعات کافی برای تصمیم عملیاتی داشتم: مشکل قابل بازتولید و شدید بود، پایین‌تر از application layer دیده می‌شد و علت فیزیکی خارج از دید VPS من قرار داشت.

درخواست refund کردم و FDCServers پول را برگرداند

خلاصه مشکلات و diagnostics جمع‌آوری‌شده را برای FDCServers فرستادم، درخواست لغو service و refund کردم. پول را برگرداندند.

پس این داستان به دعوای طولانی بر سر پول ختم نشد. برای توضیح فنی قطعی صبر کردم، تصمیم گرفتم دیگر منتظر نمانم، مدارکی را که داشتم فرستادم و پولم را خواستم. FDCServers آن را بازگرداند.

بعد از آن چه چیزی را تغییر دادم

نتیجه مفید این تجربه تعیین خوب یا بد بودن یک شرکت hosting نبود. چیزی که تغییر کرد، روش من برای debug کردن Linux serverهای کند بود.

هنوز از top، free -h و df -h استفاده می‌کنم، اما حالا خیلی زودتر سراغ این‌ها می‌روم:

date -u
uptime
cat /proc/pressure/io
cat /proc/pressure/memory
cat /proc/pressure/cpu
vmstat 1 10
iostat -x 1 10
ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'
ss -lntp
journalctl -k --since '30 min ago' --no-pager

هر جا ممکن باشد مسیرهای request را جداگانه آزمایش می‌کنم: public request، Nginx محلی، backend مستقیم، filesystem و block-device metrics. سؤال دیگر فقط این نیست که چرا server کند است؛ سؤال این است: در کدام لایه کار مفید دیگر کامل نمی‌شود؟

هر وقت ممکن باشد پیش از reboot مدارک را ثبت می‌کنم. reboot می‌تواند service را برگرداند، اما ممکن است همان D-state، PSI، queueها و latencyهایی را هم پاک کند که incident مقطعی را قابل تشخیص می‌کنند.

حرف آخر

من FDCServers VPS نخریدم تا موضوعی برای مقاله hosting داشته باشم. یک server با bandwidth زیاد می‌خواستم. مدتی دقیقاً همین را داشتم: workload واقعی را پردازش کرد و بیش از سه ترابایت منتقل شد.

بعد workload عادی به‌صورت قابل بازتولید iowait تا 100٪، I/O PSI نزدیک 100٪، read تا 18.7 ثانیه، flush latency بالاتر از 53 ثانیه، queueهای بزرگ، Nginx blocked در filesystem read و EXT4/jbd2 در انتظار I/O ایجاد کرد.

هیچ‌وقت نفهمیدم کدام component فیزیکی یا host-side عامل آن بود و لازم نیست وانمود کنم که فهمیدم. لایه‌ای را که failure در آن ظاهر می‌شد مشخص کردم، شواهد کافی برای جدا کردن آن از application problem جمع کردم، انتظار برای توضیح root cause عمیق‌تر را کنار گذاشتم و درخواست پس گرفتن پولم را دادم. FDCServers آن را بازگرداند.

این حکم درباره همه VPSهای FDCServers نیست. فقط یک یادآوری مستند است که service می‌تواند active و process می‌تواند running به نظر برسد، در حالی که ماشین تقریباً تمام زمان مفید خود را منتظر storage می‌ماند.