بلاگ/محدود کردن مصرف CPU و RAM سرویس‌ها با systemd و cgroups در سرور لینوکس
به‌روز شده

محدود کردن مصرف CPU و RAM سرویس‌ها با systemd و cgroups در سرور لینوکس

1405/06/040 بازدید
محدود کردن مصرف CPU و RAM سرویس‌ها با systemd و cgroups در سرور لینوکس

محدود کردن مصرف CPU و RAM سرویس‌ها با systemd و cgroups در سرور لینوکس

نمایی مفهومی از تقسیم منابع CPU و حافظه میان سرویس‌های لینوکس روی یک سرور ممکن است وب‌سرور، پایگاه داده، صف پردازش و چند سرویس داخلی هم‌زمان اجرا شوند. اگر یکی از آن‌ها به‌دلیل باگ، افزایش ناگهانی ترافیک یا تنظیم نادرست تمام CPU یا حافظه را مصرف کند، سرویس‌های دیگر هم کند یا متوقف می‌شوند. حتی اتصال SSH ممکن است آن‌قدر کند شود که بررسی مشکل دشوار باشد. در لینوکس می‌توان با control groups یا cgroups منابع پردازش‌ها را کنترل کرد. در توزیع‌هایی که از systemd استفاده می‌کنند، معمولاً لازم نیست cgroupها را دستی بسازیم؛ systemd برای هر سرویس یک cgroup ایجاد می‌کند و گزینه‌هایی مانند CPUQuota، MemoryHigh و MemoryMax را در اختیارمان می‌گذارد. در این راهنما، روش محدود کردن مصرف منابع سرور با cgroups v2 و systemd را بررسی می‌کنیم. دستورها باید با کاربر root یا از طریق sudo اجرا شوند.

cgroups و systemd چگونه با هم کار می‌کنند؟

cgroups قابلیتی در هسته لینوکس است که پردازش‌ها را در گروه‌های سلسله‌مراتبی قرار می‌دهد. هسته می‌تواند مصرف CPU، حافظه و ورودی/خروجی هر گروه را اندازه‌گیری یا محدود کند. برخلاف ابزارهایی مثل nice که فقط اولویت زمان‌بندی CPU را تغییر می‌دهند، cgroups امکان تعریف سقف واقعی یا کنترل فشار منابع را فراهم می‌کند. systemd سرویس‌ها، نشست‌های کاربران و بخش‌هایی از سیستم را در واحدهای جداگانه مدیریت می‌کند. هر فایل .service هنگام اجرا در cgroup مربوط به خودش قرار می‌گیرد. در نتیجه، محدودیت روی تمام پردازش‌های سرویس اعمال می‌شود؛ اگر سرویس چند worker بسازد، آن‌ها نیز در همان سهم منابع قرار می‌گیرند. نسخه دوم cgroups یک ساختار یکپارچه دارد و رفتار کنترل‌کننده‌ها در آن منظم‌تر است. بیشتر توزیع‌های جدید لینوکس از cgroups v2 استفاده می‌کنند، اما بهتر است قبل از اعمال تنظیمات آن را بررسی کنیم.

تشخیص نسخه cgroups روی سرور

برای مشاهده نوع فایل‌سیستم cgroup این دستور را اجرا کنید:

stat -fc %T /sys/fs/cgroup

اگر خروجی cgroup2fs باشد، سرور از cgroups v2 استفاده می‌کند. روش دیگر بررسی mountها است:

findmnt -t cgroup2

برای دیدن سلسله‌مراتب مورد استفاده systemd نیز می‌توان این دستور را اجرا کرد:

systemd-cgls

در خروجی، سرویس‌ها زیر شاخه‌هایی مثل system.slice دیده می‌شوند. برای مثال، nginx.service معمولاً در مسیر زیر قرار دارد:

/system.slice/nginx.service

نسخه systemd را هم بررسی کنید، چون بعضی گزینه‌ها در نسخه‌های قدیمی وجود ندارند:

systemctl --version

دستورهای این مقاله برای systemd و cgroups v2 نوشته شده‌اند. در سیستم‌های قدیمی مبتنی بر cgroups v1 ممکن است نام یا رفتار بعضی کنترل‌ها متفاوت باشد.

تفاوت گزینه‌های اصلی محدودسازی منابع

systemd چند گزینه نزدیک به هم دارد که کاربرد یکسانی ندارند. انتخاب درست آن‌ها مانع از محدودیت بیش از حد یا توقف ناخواسته سرویس می‌شود.

گزینه systemdمنبعرفتار
CPUQuotaCPUسقف قطعی زمان CPU را تعیین می‌کند
CPUWeightCPUسهم نسبی سرویس هنگام رقابت را مشخص می‌کند
MemoryHighRAMآستانه فشار و کندسازی مصرف حافظه است
MemoryMaxRAMسقف قطعی حافظه را تعیین می‌کند
MemorySwapMaxSwapمقدار swap قابل استفاده را محدود می‌کند
TasksMaxپردازش و threadتعداد taskهای سرویس را محدود می‌کند

CPUQuota=50% یعنی سرویس حداکثر معادل نیمی از ظرفیت یک هسته CPU را دریافت می‌کند. روی سروری با چهار هسته، مقدار 200% معادل ظرفیت کامل دو هسته است. این گزینه سرویس را به یک هسته مشخص متصل نمی‌کند؛ فقط مجموع زمان CPU آن را محدود می‌کند. CPUWeight سقف قطعی نیست. اگر سیستم بیکار باشد، سرویس می‌تواند CPU بیشتری بگیرد؛ اما هنگام رقابت، سرویسی با وزن بالاتر سهم بیشتری خواهد داشت. وزن پیش‌فرض معمولاً 100 است و در cgroups v2 می‌توان مقداری بین 1 تا 10000 تعیین کرد. برای حافظه، بهتر است ابتدا MemoryHigh را به‌عنوان آستانه کنترلی تنظیم کنیم و MemoryMax را کمی بالاتر قرار دهیم. عبور از MemoryHigh باعث می‌شود هسته روی تخصیص حافظه فشار وارد کند، اما عبور از MemoryMax ممکن است به اجرای OOM killer در cgroup و خاتمه یک یا چند پردازش سرویس منجر شود.

اعمال محدودیت موقت برای آزمایش

پیش از ویرایش تنظیمات دائمی، می‌توان محدودیت را در زمان اجرا آزمایش کرد. فرض کنیم می‌خواهیم مصرف سرویس myapp.service را به ۸۰ درصد یک هسته و حداکثر یک گیگابایت حافظه محدود کنیم:

sudo systemctl set-property --runtime myapp.service \
  CPUQuota=80% \
  MemoryHigh=768M \
  MemoryMax=1G

گزینه --runtime تنظیمات را در /run نگه می‌دارد؛ بنابراین پس از راه‌اندازی مجدد سرور از بین می‌رود. این رفتار برای آزمودن اثر محدودیت مناسب است. وضعیت مقادیر اعمال‌شده را می‌توان با دستور زیر دید:

systemctl show myapp.service \
  -p CPUQuotaPerSecUSec \
  -p CPUWeight \
  -p MemoryHigh \
  -p MemoryMax \
  -p MemoryCurrent

اگر سرویس زیر بار واقعی پایدار ماند، می‌توان همین محدودیت‌ها را دائمی کرد. برای حذف تنظیمات موقت نیز سرور را راه‌اندازی مجدد کنید یا مقادیر را با systemctl set-property --runtime به حالت مورد نظر برگردانید.

تنظیم دائمی محدودیت CPU و RAM

فایل اصلی unit ممکن است هنگام به‌روزرسانی بسته بازنویسی شود. به همین دلیل، آن را مستقیم ویرایش نکنید. یک drop-in با این دستور بسازید:

sudo systemctl edit myapp.service

سپس تنظیمات زیر را وارد کنید: ```ini