علت بالا رفتن CPU steal time در سرور مجازی و همسایههای
شاخص CPU steal time نشاندهنده زمان از دست رفته vCPU شماست. با دستور vmstat و بررسی ستون st، تفاوت بین بار کاری سرور خود و تاثیر منفی همسایههای پرمصرف را تشخیص دهید.
معنای واقعی CPU steal time
شاخص CPU steal time در واقع سهم زمانی است که vCPU شما آمادهٔ اجرا بوده و هیچ مانعی برای آن وجود نداشته، اما hypervisor هستهٔ فیزیکی را به یک guest دیگر اختصاص داده است. در این حالت، پردازش در صف انتظار قرار گرفته و هسته در جای دیگری مشغول بوده است. لینوکس این چرخهها را بهطور جداگانه محاسبه کرده و آنها را به عنوان st گزارش میکند؛ این همان روشی است که به شما کمک میکند تفاوت بین «سرور من مشغول است» و «سرور من منتظر نوبت خود است» را تشخیص دهید.
این تفاوت، دلیل اصلی وجود این شمارنده است. زمانی که پردازشهای شما روی CPU صرف میکنند به عنوان us (کاربر) یا sy (سیستم) گزارش میشود. زمانی که یک تسک به دلیل انتظار برای ذخیرهسازی مسدود شده است، به عنوان wa (I/O wait) گزارش میشود. یک vCPU که آمادهٔ اجراست، در صف اجرا قرار دارد، هیچ عملیات I/O معلقی ندارد و با این حال اجرا نمیشود، به عنوان st گزارش میشود. هیچ چیزی در داخل سرور شما نمیتواند این وضعیت را تغییر دهد، زیرا تصمیمگیری برای زمانبندی در لایهای پایینتر از شما، یعنی در سطح host، انجام میشود.
این موضوع مستقیماً از نحوه اشتراکگذاری یک ماشین فیزیکی توسط VPS بین چندین guest ناشی میشود. دلیل معمول این اتفاق، یک همسایه است: guest دیگری روی همان node با بار کاری سنگین در حال اجراست و host هستهها را بین شما تقسیم میکند. دلیل دومی هم وجود دارد که اغلب نادیده گرفته میشود. بسیاری از ارائهدهندگان، سقف استفاده از یک vCPU اشتراکی را به کسری از یک هستهٔ فیزیکی محدود میکنند و در بسیاری از hypervisorها، این محدودیت اعمالشده به عنوان steal در داخل guest محاسبه میشود. بنابراین، مقدار بالای st به شما میگوید که هسته در اختیار شما قرار نگرفته است. این شاخص همیشه به شما نمیگوید که چه کسی آن را اشغال کرده است.
منشأ عدد steal
هستهٔ سیستمعامل شما نمیتواند steal را بهتنهایی اندازهگیری کند، زیرا دیدی نسبت به میزبان (host) ندارد. هایپروایزر این اطلاعات را به آن اعلام میکند. در KVM، میزبان یک شمارنده برای هر vCPU در صفحهای که با مهمان (guest) به اشتراک گذاشته شده مینویسد و اگر هسته با CONFIG_PARAVIRT_TIME_ACCOUNTING کامپایل شده باشد (که تمام هستههای توزیعهای استاندارد چنین هستند)، مهمان آن را جمع میزند. Xen نیز همین اطلاعات را از طریق ناحیه runstate گزارش میدهد. این مجموع دقیقاً در یک نقطه به فضای کاربری (userspace) میرسد:
head -1 /proc/statآن خط cpu شامل ده شمارنده است که بر حسب تیکهای USER_HZ از زمان بوت محاسبه شدهاند و به این ترتیب هستند: user، nice، system، idle، iowait، irq، softirq، steal، guest، guest_nice. مقدار steal هشتمین عدد بعد از برچسب است. تمام ابزارهای زیر، یعنی vmstat، top، mpstat و هر Prometheus exporter، همان فیلد را میخوانند و با مقایسهٔ دو نمونه، آن را به درصد تبدیل میکنند.
یک پیامد از بقیه مهمتر است. اگر هایپروایزر هرگز این شمارنده را صادر نکند، فیلد برای همیشه صفر باقی میماند و تمام ابزارهای مبتنی بر آن، حتی زمانی که میزبان بهشدت تحت فشار است، عدد 0.0 را گزارش میدهند. KVM و Xen این مقدار را صادر میکنند. مهمانها در VMware و Hyper-V معمولاً عدد صفر مطلق را گزارش میدهند. پیش از اعتماد به عدد صفر، پلتفرم خود را بررسی کنید:
systemd-detect-virtاین دستور نام پلتفرم را چاپ میکند، مانند kvm، xen، vmware یا microsoft، و در سیستمهای bare metal مقدار none را نشان میدهد. داخل یک کانتینر، این دستور بهجای آن، runtime مربوطه را گزارش میدهد، مانند lxc، docker یا podman که اطلاعاتی دربارهٔ کانتینر به شما میدهد و نه دربارهٔ ماشین زیرین آن. روی kvm، عدد صفر گواهی واقعی بر این است که میزبان با شما بهخوبی رفتار میکند. در پلتفرمی که هرگز این فیلد را پر نمیکند، صفر هیچ گواهی محسوب نمیشود و در آن صورت، میزان رقابت بر سر منابع باید از طریق زمانسنجیِ کارهای واقعی ارزیابی شود.
چگونه میتوانم CPU steal time را در یک VPS بررسی کنم؟
vmstat از بسته procps میآید. این ابزار در تقریباً تمام ایمیجهای VPS اوبونتو و دبیان موجود است، اما در برخی ایمیجهای حداقلی کانتینر وجود ندارد؛ بنابراین پیش از استفاده، آن را نصب کنید.
sudo apt-get update
sudo apt-get install -y procps
vmstat --version
vmstat 1 5vmstat --version خطی مانند vmstat from procps-ng 4.0.4 چاپ میکند. اگر این خروجی نمایش داده شد، ابزار نصب شده است و شما در حال خواندن شمارندههای واقعی هستید. vmstat 1 5 سپس 5 نمونه با فاصله زمانی یک ثانیه میگیرد.
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
r b swpd free buff cache si so bi bo in cs us sy id wa st gu
1 0 0 3216484 98304 1284360 0 0 4 18 62 110 3 1 96 0 0 0
2 0 0 3216232 98304 1284360 0 0 0 0 248 431 6 2 84 0 8 0ستون st را در بلوک cpu در سمت راست پیدا کنید. نسخههای جدید procps-ng یک ستون gu برای زمان KVM guest بعد از آن چاپ میکنند، بنابراین st اکنون به جای آخرین ستون، دومین ستون از سمت راست است. ستون را بر اساس نام سرتیتر آن بخوانید، زیرا جایگاه آن در نسخههای مختلف تغییر کرده است.
دو عادت باعث میشود خوانش شما دقیق باقی بماند. اولین خط داده، میانگین از زمان بوت است، بنابراین آن را نادیده بگیرید و خطوط بعدی را بخوانید. همچنین یک نمونهگیری، اندازهگیری محسوب نمیشود، زیرا steal به صورت ناگهانی رخ میدهد: vmstat 1 60 را اجرا کنید و یک دقیقه کامل آن را مشاهده کنید تا بتوانید نتیجهگیری کنید.
top همان عدد را در خط خلاصه %Cpu(s)، در فیلد مشخصشده با st گزارش میدهد:
%Cpu(s): 6.2 us, 2.1 sy, 0.0 ni, 83.9 id, 0.0 wa, 0.0 hi, 0.3 si, 7.5 stبرای جزئیات هر هسته، sysstat را اضافه کنید:
sudo apt-get install -y sysstat
mpstat -P ALL 1 5mpstat برای هر CPU یک ردیف با ستون %steal چاپ میکند که نشان میدهد آیا تمام vCPUها تحت تأثیر قرار گرفتهاند یا فقط یکی از آنها. برای تاریخچهای که یک تیکت پشتیبانی به آن نیاز دارد، به جای خواندن از روی صفحه، نمونهها را ذخیره کنید:
date -u | tee -a ~/steal.log
mpstat -P ALL 1 5 | tee -a ~/steal.logآن را از طریق cron در ساعاتی که مشکوک هستید اجرا کنید. این فایل تفاوت بین گفتنِ «دیشب احساس میکردم کند است» به ارائهدهنده خدمات و نشان دادن دقیقِ آن ده دقیقه به آنهاست.
اعداد steal به چه معنا هستند؟
- یک
0.0ثابت. وضعیت سالم است، یا پلتفرم اصلاً steal را گزارش نمیکند. پیش از اطمینان، باsystemd-detect-virtبررسی کنید. - جهشهای چند درصدی که چند ثانیه طول میکشند. در هر نود اشتراکی عادی است. ممکن است بیلد یکی از همسایهها شروع شده باشد یا میزبان در حال اجرای بکآپهای خود باشد.
- 1 تا 5 درصد پایدار در یک پلن اشتراکی. مورد انتظار است. قیمت پرداختی شما دقیقاً بازتابدهنده همین CPU اشتراکی است.
- 5 تا 10 درصد پایدار. کندی محسوسی که میتوانید آن را اندازهگیری کنید. شروع به ثبت شواهد کنید و ساعات مشابه را در طول چند روز با هم مقایسه کنید.
- بالای 10 درصد برای ساعتهای متوالی. نود برای حجم کاری شما بیش از حد شلوغ (oversubscribed) است. این سطحی است که ارسال تیکت پشتیبانی یا جابهجایی سرویس را توجیه میکند.
این بازهها را به عنوان یک راهنمای خواندن در نظر بگیرید، نه یک مشخصات فنی؛ چرا که هیچ ارائهدهندهای در پلنهای اشتراکی، تضمینی برای میزان steal ارائه نمیدهد. آنها را با آنچه اجرا میکنید بسنجید. یک تسک دستهای (batch job) شبانه میتواند 15 درصد steal را تحمل کند و هیچکس متوجه نشود. اما یک سرویس حساس به تأخیر، بسیار پیش از آنکه میانگین اعداد نگرانکننده به نظر برسند، تأثیر آن را در p99 نشان میدهد؛ به همین دلیل است که بارهای کاری حساس به تأخیر مانند رباتهای معاملهگر باید روی هستههای اختصاصی اجرا شوند.
هزینه steal time برای شما چقدر است؟
محاسبه آن کوتاه است. اگر کسری از زمان CPU شما به میزان s گرفته شود، کاری که به مقدار ثابتی از CPU نیاز دارد، 1 / (1 - s) برابر بیشتر در زمان واقعی (wall clock) طول میکشد. برای کاری که به 60 ثانیه زمان CPU نیاز دارد:
The data behind this chart
[
{
"steal_percent": 0,
"wall_clock_seconds": "60.0"
},
{
"steal_percent": 3,
"wall_clock_seconds": "61.9"
},
{
"steal_percent": 8,
"wall_clock_seconds": "65.2"
},
{
"steal_percent": 15,
"wall_clock_seconds": "70.6"
},
{
"steal_percent": 25,
"wall_clock_seconds": "80.0"
},
{
"steal_percent": 40,
"wall_clock_seconds": "100.0"
}
]در نرخ 3 درصد، که یک مقدار معمول در پلنهای اشتراکی است، آن کار به جای 60.0 ثانیه، 61.9 ثانیه طول میکشد. هیچکس برای این مقدار تیکت پشتیبانی باز نمیکند. در نرخ 8 درصد، این زمان 65.2 ثانیه است. در نرخ 40 درصد، همان کار به 100.0 ثانیه زمان نیاز دارد و صفی که قبلاً تخلیه میشد، حالا شروع به رشد میکند.
اینها مقادیر محاسبهشده هستند، نه اندازهگیریشده. این مدل فرض میکند که یک thread قابلاجرا وجود دارد و steal بهطور یکنواخت در طول بازه زمانی پخش شده است. سرویسهای واقعی اغلب عملکردی بدتر از این منحنی نشان میدهند، زیرا یک slice دزدیدهشده (stolen slice) در میانه یک درخواست رخ میدهد و تأخیر آن دوباره توسط تمام مواردی که منتظر آن درخواست هستند، پرداخت میشود. برای بهدست آوردن عدد واقعی خودتان به جای استفاده از فرمول، در یک ساعت خلوت و دوباره در یک ساعت شلوغ، VPS خود را بنچمارک کنید و در هر دو بازه زمانی، مقدار st را ثبت کنید.
آیا این steal است یا مسئلهای دیگر؟
تشخیص steal از سایر علائم ممکن است دشوار باشد. شمارندهها را با هم و در یک خط vmstat بخوانید.
stبالا است در حالی کهrوusپایین باقی میمانند: میزبان (host) هسته پردازشی را در اختیار شما قرار نمیدهد. این همان steal است.rبسیار بالاتر از تعداد vCPU شما است، در حالی کهusبالا وstنزدیک به صفر است: شما حجم کاری بیش از توان پردازندههای خود اجرا میکنید.rرا با خروجیnprocمقایسه کنید. این بیشفروشی (oversubscription) توسط خود شماست، نه یک همسایه.waبالا است در حالی کهstنزدیک به صفر است: وظایف (tasks) در بخش ذخیرهسازی مسدود شدهاند، که مشکلی متفاوت با راهحلی متفاوت است.- میانگین بار (load average) بالا است در حالی که
stوusهر دو پایین هستند: عدد بار، وظایف غیرقابلوقفه (uninterruptible) را نیز میشمارد، بنابراین این وضعیت معمولاً به یک دستگاه گیر کرده یا یک mount شبکه معلق اشاره دارد تا به CPU.
طرحهای قابلانفجار (Burstable plans) نیاز به توجه ویژهای دارند. این طرحها به شما موجودی اعتباری میدهند که در زمان بیکاری افزایش و در زمان فعالیت کاهش مییابد، و هنگامی که تمام شود، ارائهدهنده شما را در نرخ پایه محدود میکند. در برخی پلتفرمها، این محدودیت (throttle) به عنوان steal گزارش میشود. در برخی دیگر، این موضوع از داخل سیستم قابل مشاهده نیست و شما صرفاً چرخههای کمتری در ثانیه دریافت میکنید. پیش از آنکه نتیجه بگیرید همسایهای مقصر است، توضیحات طرح خود را مطالعه کنید.
چرا یک کانتینر steal time را گزارش نمیکند
مقدار steal ویژگی ماشین مجازی است، نه کانتینری که درون آن اجرا میشود. یک کانتینر Docker روی VPS شما از /proc میزبان استفاده میکند، بنابراین مقدار st که درون آن خوانده میشود، همان steal مربوط به VPS است که مورد نیاز شماست. مجازیسازی مبتنی بر کانتینر که بهعنوان VPS فروخته میشود، رفتار متفاوتی دارد. با وجود lxcfs، مقدار /proc/stat درون کانتینر از روی حسابرسی cgroup سنتز میشود و مقدار steal بهطور پیشفرض صفر است. یک پشته مانیتورینگ که فقط از داخل کانتینر داده جمعآوری میکند، ممکن است مقدار صفر و پایداری را نشان دهد، در حالی که ماشین فیزیکی زیرین با کمبود منابع مواجه است.
درون یک کانتینر، شمارندهای که معنای مشابهی دارد، CPU quota throttling است. در cgroup v2:
cat /sys/fs/cgroup/cpu.statمقدار nr_throttled تعداد دورههای اعمال محدودیتی را میشمارد که گروه در آن به سقف سهمیه CPU خود رسیده است و throttled_usec مجموع زمانی است که پردازش در حالت مسدود (frozen) بوده است. افزایش nr_throttled به این معنی است که پردازش شما آماده اجرا بوده اما اجرا نشده است؛ این تجربه مشابه steal است، اما توسط محدودیتی ایجاد شده که خودتان تعیین کردهاید. پیش از مقصر دانستن میزبان، محدودیتهای خود را بررسی کنید، بهویژه اگر سرویسهای خود را در Docker روی یک VPS با محدودیتهای CPU در فایل compose اجرا میکنید. مجازیسازی لایهای یک مکان دیگر برای هدر رفتن زمان اضافه میکند، زیرا یک ماشین مجازی درون VPS شما، علاوه بر تأخیر زمانبندی خودش، متحمل steal مربوط به VPS شما نیز میشود. اگر مجازیسازی تو در تو روی یک VPS اجرا میکنید، این نکته را در نظر داشته باشید.
اقدام در برابر steal مداوم
هیچ تنظیمی در داخل guest نمیتواند مشکل steal را برطرف کند، زیرا زمانبندی (scheduler) که این تصمیم را میگیرد، خارج از guest اجرا میشود. ارتقای هسته (kernel) نیز تغییری ایجاد نمیکند: زمانبندی آگاه به کش که در هسته 7.2 لینوکس اضافه شد، وظایف شما را بین هستههایی که واقعاً به شما اختصاص داده شدهاند جابهجا میکند و نمیتواند چرخههایی را که یک همسایه قبلاً اشغال کرده است، بازیابی کند. چهار اقدام واقعی وجود دارد.
ابتدا شواهد را جمعآوری کنید. زمانها را به UTC ثبت کنید، مدت زمان هر دوره، تکرار آن و اینکه آیا mpstat نشان میدهد که یک vCPU تحت تأثیر قرار گرفته یا همه آنها، مشخص کنید. یک هفته نمونهبرداری ثبتشده، ارزشمندتر از یک اسکرینشات است.
با استفاده از آن دادهها یک تیکت باز کنید. دو سؤال مستقیم بپرسید: آیا این node در این بازههای زمانی بیش از حد اشتراکگذاری (oversubscribed) شده است و آیا میتوان instance من را جابهجا کرد؟ خروجی vmstat و زمانهای دقیق را در تیکت قرار دهید. ارائهدهندگان بر اساس یک بازه زمانی قابل تکرار عمل میکنند و تیکتی که فقط بگوید سرور کند است، با درخواستی برای ارائه جزئیات پاسخ داده میشود. اینکه چه مقدار از این کار را میتوانید به ارائهدهنده بسپارید، یکی از تفاوتهای عملی بین یک VPS مدیریتشده و مدیریتنشده است.
درخواست مهاجرت (migration) کنید. انتقال یک guest به یک node با بار کمتر، کار روتین برای ارائهدهنده است و معمولاً یک reboot کوتاه است. این راهکاری است که هزینهای ندارد و موارد رایجی را که یک node بهطور اتفاقی چندین همسایه سنگین را همزمان میزبانی میکند، حل میکند.
با هزینه کردن، مشکل رقابت بر سر منابع را حذف کنید. یک طرح vCPU اختصاصی، هستههای فیزیکی را برای instance شما رزرو میکند، بنابراین شمارنده روی صفر میماند. این طرح ماهانه هزینه بیشتری دارد و پاسخ صادقانهای برای workloadهایی است که نمیتوانند نوسان را تحمل کنند. اگر این هم کافی نیست، یا میخواهید پهنای باند حافظه را نیز بهطور کامل در اختیار داشته باشید، گام بعدی استفاده از یک سرور اختصاصی بهجای VPS است.
در مدتی که منتظر هر یک از این اقدامات هستید، آسیب ناشی از steal را کاهش دهید. تعداد threadهای کاری کمتری نسبت به vCPUهای خود اجرا کنید، زیرا threadهایی که نمیتوانند هسته دریافت کنند، فقط باعث افزایش context switch میشوند. کارهای دستهای (batch) را به ساعاتی منتقل کنید که node خلوت است؛ چیزی که لاگهای خودتان اکنون به شما میگویند. سپس دوباره با همان دستور و در همان ساعات اندازهگیری کنید تا بتوانید بهجای حدس زدن، بگویید که آیا تغییر اعمالشده مؤثر بوده است یا خیر.
FAQ
مقدار عادی CPU steal time در یک VPS چقدر است؟
در پلنهای اشتراکی، جهشهای کوتاه و مقدار پایدار زیر حدود 5 درصد عادی است، زیرا CPU اشتراکی به این معناست که میزبان، هستههای فیزیکی را بین مهمانها تقسیم میکند. مقادیر دو رقمی پایدار در طول چندین ساعت عادی نیست و ارزش ارسال تیکت را دارد. در پلنهای vCPU اختصاصی، مقدار مورد انتظار 0.0 است، بنابراین هر مقدار دیگری در آنجا یک نقص فنی است که باید گزارش شود. این عدد را با توجه به workload خود بسنجید: یک job دستهای شبانه میتواند steal را جذب کند، اما یک API حساس به تأخیر (latency) نمیتواند.
آیا پلن بزرگتر مشکل steal time بالا را حل میکند؟
بهتنهایی خیر. vCPUهای بیشتر روی همان نود اشتراکی به معنای رقابت vCPUهای مجازی بیشتر برای همان هستههای فیزیکی تحت فشار است و درصد آن میتواند دقیقاً همانجایی که بود باقی بماند. آنچه steal را حذف میکند، تخصیص CPU اختصاصی یا انتقال به یک نود با بار کمتر است. سهم بزرگتر از یک ماشین شلوغ، همچنان سهمی از یک ماشین شلوغ است.
چرا VPS من با وجود کندی واضح، steal time صفر نشان میدهد؟
دو دلیل رایج برای این موضوع وجود دارد. هایپروایزر ممکن است اصلاً این شمارنده را export نکند که در پلتفرمهای VMware و Hyper-V معمول است، بنابراین فیلد در هر شرایطی روی صفر باقی میماند. دستور systemd-detect-virt را اجرا کنید تا ببینید روی چه پلتفرمی هستید. در غیر این صورت، گلوگاه جای دیگری است: برای بررسی انتظار ذخیرهسازی (storage waits) به wa نگاه کنید، برای بررسی overload خودتان r را با nproc مقایسه کنید و برای بررسی محدودیت سهمیه (quota throttling) داخل کانتینرها، /sys/fs/cgroup/cpu.stat را بخوانید.
آیا میتوانم steal time را از داخل سرور خود کاهش دهم؟
شما نمیتوانید زمانبندی (scheduling) میزبان را از داخل مهمان تغییر دهید. فقط میتوانید میزان آسیب آن را کاهش دهید. تعداد worker threadهای کمتری نسبت به vCPUهای خود اجرا کنید تا کار کمتری در صف اجرا (run queue) منتظر هستهای بماند که در دسترس نیست. jobهای دستهای را به ساعاتی منتقل کنید که نود خلوتتر است. نتایج را کش کنید تا درخواستهای کمتری اصلاً به CPU نیاز داشته باشند. تغییراتی که واقعاً steal را حذف میکنند، یعنی مهاجرت به یک نود دیگر یا استفاده از هستههای اختصاصی، در سمت ارائهدهنده خدمات قرار دارند.