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

VPS من در REGXA حتی بدون ترافیک 94٪ CPU Steal نشان می‌داد

در بررسی مشکل شدید کارایی یک VPS مبتنی بر KVM در REGXA، به 92 تا 94 درصد CPU steal، درخواست‌های چندثانیه‌ای localhost، صف‌های رو به رشد اتصال و پاسخ‌های HTTP 504 با بیش از دو دقیقه تأخیر رسیدم. با حذف کامل ترافیک production، CPU steal به 94.17٪ افزایش یافت و REGXA بعداً علت را resource contention در زیرساخت اشتراکی خود اعلام کرد.

REGXAVPSLinuxCPU stealمجازی‌سازی

این متن را به‌عنوان نقد منفی REGXA نمی‌نویسم و قصد ندارم به کسی بگویم از این شرکت VPS بخرد یا نه. این فقط یکی از روزهای عادی من به‌عنوان توسعه‌دهنده است.

یک workload معمولی را به VPS مبتنی بر KVM با 2 vCPU، 2 GB RAM و 60 GB NVMe منتقل کردم. Nginx در حال اجرا بود و backend هم کار می‌کرد، اما ماشین طوری رفتار می‌کرد که انگار به‌شدت overloaded است: درخواست‌ها جمع می‌شدند، عملیات TLS کند می‌شد، اتصال‌ها باز می‌ماندند و بعضی درخواست‌ها بعد از بیش از دو دقیقه با HTTP 504 تمام می‌شدند.

حسابداری CPU مسیر بررسی را عوض کرد

اولین فرض من ساده بود: چیزی داخل VPS دارد CPU را مصرف می‌کند. بعد mpstat را اجرا کردم.

Average CPU steal: 92.58%
CPU 0 steal: 90.62%
CPU 1 steal: 94.57%
CPU user: 4.02%
CPU system: 1.47%
CPU iowait: 0.41%
CPU idle: 0.53%

نمونه‌های جداگانه مرتباً حدود 89 تا 98 درصد steal نشان می‌دادند. CPU pressure هم بسیار بالا بود:

CPU PSI some avg10: 79.47
CPU PSI some avg60: 75.32
CPU PSI some avg300: 76.31
Load average: 5.85 / 5.75 / 5.73

این فقط یک ماشین دو-vCPU بود. Linux پردازش‌های runnable داشت که منتظر CPU بودند، اما عدد اصلی مصرف معمول CPU توسط application نبود؛ %steal بود.

مصرف بالای CPU با CPU steal یکی نیست

اگر application من واقعاً CPU را مصرف می‌کرد، انتظار داشتم user یا system بالا باشد. در عوض VPS تقریباً چنین وضعی داشت:

user: 4%
system: 1%
steal: 93%

CPU steal زمانی است که virtual CPU آماده اجراست اما hypervisor آن را schedule نمی‌کند. بنابراین وقتی می‌گویم CPU time از VPS گرفته می‌شد، منظورم همین معنای فنی در virtualization است. نمی‌توانم ثابت کنم REGXA عمداً core فیزیکی‌ای را که منحصراً متعلق به من بوده به مشتری دیگری داده است، و از metricهای guest هم نمی‌توان intent را نتیجه گرفت. چیزی که می‌توانم ثابت کنم این است که guest مرتباً کار آماده اجرا داشته ولی CPU scheduling time دریافت نمی‌کرده است.

تمام ترافیک production را حذف کردم

هنوز یک ایراد منطقی باقی بود: شاید workload خودم مشکل را ایجاد می‌کرد. بنابراین آن متغیر را حذف کردم. workload فعال را به جای دیگری بردم، ترافیک production را از این VPS قطع کردم، صبر کردم queueها خالی شوند و دوباره اندازه‌گیری کردم.

Average CPU steal: 94.17%
CPU 0 steal: 95.56%
CPU 1 steal: 92.83%
CPU user: 1.95%
CPU system: 0.59%
CPU iowait: 0.59%
CPU idle: 2.18%

نتیجه بدتر شد. نمونه‌های vmstat همچنان 91 تا 98 درصد steal نشان می‌دادند و تا 15 پردازش runnable منتظر CPU بودند. ترکیب کلیدی این بود:

user: 1.95%
system: 0.59%
steal: 94.17%

applicationها تقریباً هیچ کاری انجام نمی‌دادند، اما VM همچنان تقریباً تمام زمان scheduling خود را از دست می‌داد. دیگر نمی‌شد بار عادی production را توضیح قابل‌قبولی دانست.

حتی localhost هم به شکل غیرعادی کند شد

HTTPS را از طریق 127.0.0.1 آزمایش کردم تا DNS عمومی، ISP، فاصله جغرافیایی و مسیر شبکه خارجی از آزمایش حذف شوند.

زیر بار، چهار مورد از ده درخواست HTTPS به localhost در TLS handshake شکست خوردند. درخواست‌های موفق 29.30، 22.77، 12.25، 11.87، 11.12 و 9.40 ثانیه طول کشیدند. بعضی TLS handshakeها به‌تنهایی حدود 9 ثانیه زمان بردند.

بعد از حذف ترافیک production، localhost بهتر شد اما همچنان ناپایدار بود: 0.061، 0.745، 0.830، 0.873، 1.010، 1.117، 1.121، 2.188 و 3.355 ثانیه. همان عملیات محلی ممکن بود یک بار حدود 61 ms و بار بعد بیش از 3.3 ثانیه طول بکشد.

بقیه stack هم پیامدها را نشان می‌داد

در یک مقطع حدود 450 اتصال established، 122 اتصال orphaned، 110 اتصال FIN-WAIT-1 و 33 اتصال CLOSE-WAIT دیدم. listen queue مربوط به backend محلی به حدود 14–15 و صف HTTPS به حدود 40 رسید.

Nginx پاسخ‌های HTTP 504 با زمان‌های 142.857، 138.902، 135.064، 129.819 و 128.657 ثانیه ثبت کرد. بعضی درخواست‌های دیگر حدود 67 تا 130 ثانیه باز ماندند. همچنین secure-connect timeoutهای حدود 35 تا 41 ثانیه، database network timeout و تأخیر در TLS دیدم.

هرکدام از این موارد می‌توانست جداگانه شبیه مشکل Nginx، database، network یا backend باشد. اما افزایش timeout نمی‌تواند CPU timeای را ایجاد کند که hypervisor schedule نمی‌کند.

RAM و disk توضیح مسئله نبودند

VPS هنوز حدود 1.0 تا 1.1 GiB RAM آزاد داشت، swap تقریباً استفاده نمی‌شد، OOM event و فعالیت OOM killer وجود نداشت. فایل‌سیستم فقط حدود 20٪ پر بود و تقریباً 44 GB فضای آزاد داشت. I/O wait در اندازه‌گیری‌های مهم CPU زیر 1٪ ماند.

حافظه تمام نشده بود، disk پر نبود و applicationهای من CPU گمشده را مصرف نمی‌کردند. metric غالب همچنان %steal بود.

یک KVM VPS سالم کاملاً متفاوت بود

همان نوع بررسی را روی KVM VPS دیگری که در همان زمان ترافیک عادی را سرو می‌کرد اجرا کردم:

Average CPU steal: 0.02%
CPU idle: 87.86%
CPU PSI avg10: 0.29
CPU PSI avg60: 0.63
CPU PSI avg300: 0.49
Load average: 0.47 / 0.33 / 0.14

ده درخواست HTTPS محلی آن بین حدود 37 تا 69 ms تکمیل شدند. بعداً روی VPS production دیگری که واقعاً busy بود، 61.71٪ user CPU، 5.08٪ system CPU، 24.09٪ idle و فقط 0.13٪ steal دیدم.

این تفاوتی است که اکنون برایم مهم است. ممکن است VPS به این دلیل busy باشد که نرم‌افزار خودم واقعاً CPU را مصرف می‌کند. این کاملاً متفاوت است با guestی که بیش از 90٪ CPU time اندازه‌گیری‌شده را در انتظار hypervisor می‌گذراند.

REGXA در نهایت resource contention زیرساخت را تأیید کرد

از داخل VM می‌توانستم guest را اندازه‌گیری کنم، اما physical host، تنظیمات scheduler، CPU quota یا VMهای همسایه را نمی‌دیدم. REGXA می‌توانست.

پشتیبانی در نهایت گفت VPS روی shared CPU infrastructure میزبانی می‌شود، CPU resource میان چند VM مشترک است و performance می‌تواند با load روی physical node تغییر کند. همچنین گفتند زیرساخت Frankfurt در آن زمان با تقاضای بالایی روبه‌رو بوده و CPU steal بالای من را صراحتاً ناشی از resource contention on the underlying infrastructure دانستند.

آنها گفتند امکان تغییر CPU quota یا scheduling policy یا اختصاص CPU resource اضافی برای این shared VPS روی زیرساخت فعلی وجود ندارد. راه‌حل فنی پیشنهادی انتقال VPS به location کم‌مصرف‌تر بود.

این توضیح با عباراتی که قبلاً درباره dedicated CPU cores و guaranteed resources دیده بودم به‌سختی قابل جمع بود. من exact host configuration را نمی‌بینم، بنابراین نمی‌توانم بگویم مکانیسم مستقیم CPU overcommitment، quota، scheduler weighting، throttling یا ترکیبی از آنها بوده است. intent را هم نمی‌توانم ثابت کنم. لازم هم نیست: Linux به‌طور پایدار 92 تا 94٪ steal نشان داد و خود REGXA این مقدار بالا را به contention در shared infrastructure نسبت داد.

برای refund هم مجبور شدم دوباره پیگیری کنم

وقتی مشکل زیرساخت روشن شد، نمی‌خواستم VPS را مدام جابه‌جا کنم و nodeهای دیگر را امتحان کنم. می‌خواستم service را لغو کنم و پولم را پس بگیرم.

در ابتدا فقط یک مبلغ جزئی پیشنهاد شد و قرار بود refund به balance حساب REGXA برود، نه به کارت پرداخت. وقتی قصد خروج از سرویس را دارم، credit داخلی provider را معادل refund واقعی نمی‌دانم.

بنابراین به پیگیری ادامه دادم و خواستم 100٪ مبلغ به original payment method برگردد. در نهایت REGXA پذیرفت و کل مبلغ را به همان روش پرداخت اولیه برگرداند و full refund را یک exception توصیف کرد.

اینکه در نهایت کل مبلغ بازگردانده شد را مثبت می‌دانم. اما اینکه پس از تأیید resource contention توسط support باز هم برای حل مالی مجبور به پیگیری مداوم شدم، بخشی از همین تجربه است.

درسی که از این ماجرا نگه داشتم

ساده‌ترین اشتباه این بود که به optimize کردن application ادامه بدهم. می‌توانستم تنظیمات Nginx را تغییر دهم، timeoutها را بالا ببرم، concurrency را کم کنم، retry اضافه کنم، MongoDB را بررسی کنم یا backend code را بازنویسی کنم. شاید بعضی symptomها تغییر می‌کردند، اما سؤال اصلی بی‌پاسخ می‌ماند: چرا CPU steal بالاتر از 90٪ بود؟

اکنون دیگر فقط بررسی نمی‌کنم که SSH کار می‌کند، Nginx بالا می‌آید و health endpoint مقدار 200 می‌دهد. روی VPS جدید %user، %system، %iowait، %idle، %steal، CPU PSI، run queue و localhost latency را هم بررسی می‌کنم.

و اگر چیزی مشکوک باشد، workload را حذف می‌کنم و دوباره اندازه می‌گیرم. در این مورد، واضح‌ترین نتیجه کل incident همین بود:

CPU user: 1.95%
CPU system: 0.59%
CPU steal: 94.17%

نمی‌گویم همه VPSهای REGXA چنین رفتاری دارند. من یک VPS را آزمایش کردم و اتفاقی را که برای همان ماشین افتاد ثبت کردم. اما در آن VPS شواهد به‌طور غیرعادی واضح بودند: applicationهای من تقریباً CPU مصرف نمی‌کردند، در حالی که Linux نشان می‌داد بخش عمده virtual CPU time در انتظار scheduling توسط hypervisor از دست می‌رود. بعداً REGXA دقیقاً همین رفتار را به resource contention در shared infrastructure نسبت داد.

به همین دلیل این تجربه را ثبت می‌کنم. نه به‌عنوان امتیاز یا توصیه، بلکه فقط به‌عنوان یکی از روزهای کاری من به‌عنوان توسعه‌دهنده و metricی که دیگر هرگز روی VPS نادیده نمی‌گیرم.