فرض کنید یکی از سرویسهای مبتنی بر کوبرنتیز شما ناگهان کند شده است. کاربران از افزایش تاخیر شکایت میکنند، اما وقتی داشبوردهای مانیتورینگ را بررسی میکنید، همهچیز عادی به نظر میرسد. Podها در وضعیت Running هستند، مصرف CPU روی نود پایین است، Memory تحت فشار نیست و Alert غیرعادی هم دیده نمیشود. با این حال، زمان پاسخدهی سرویس همچنان بیشتر از حد انتظار است. معمولا در چنین شرایطی پیدا کردن منشأ مشکل ساده نیست. یکی از دلایلی که کمتر مورد توجه قرار میگیرد، CPU Throttling ناشی از CPU Limit در کوبرنتیز است. این مسئله ممکن است در بررسی اولیه داشبوردها بهوضوح دیده نشود و از چشم تیم عملیات پنهان بماند.
نکته مهم اینجاست که وجود CPU آزاد روی نود، لزوما به این معنا نیست که همه کانتینرها بدون محدودیت از منابع پردازشی استفاده میکنند. حتی اگر بخشی از ظرفیت پردازشی نود هنوز آزاد باشد، اعمال CPU Limit میتواند باعث CPU Throttling در سرویسهای حساس به تاخیر شود. در ادامه بررسی میکنیم که CPU Request و CPU Limit در کوبرنتیز چه تفاوتی با هم دارند، کوبرنتیز هرکدام را در چه مرحلهای به کار میگیرد و چرا این تنظیم بهظاهر ساده میتواند تاثیر زیادی بر عملکرد سرویسها داشته باشد.
CPU Request و CPU Limit در کوبرنتیز چه تفاوتی دارند؟
CPU Request و CPU Limit دو کاربرد متفاوت دارند. CPU Request مشخص میکند یک پاد برای اجرا به چه میزان CPU نیاز دارد. کوبرنتیز هنگام انتخاب نود (در فرایند Scheduling)، بررسی میکند که با توجه به Request پادهای مستقرشده، کدام نود ظرفیت کافی برای پاد جدید دارد. در این تصمیم، مصرف لحظهای CPU ملاک نیست؛ بنابراین ممکن است CPU یک نود در عمل کممصرف باشد، اما بهدلیل مجموع Requestهای ثبتشده، پاد جدید روی آن اجرا نشود.
در مقابل، CPU Limit سقف مصرف CPU کانتینر را پس از اجرا مشخص میکند. اگر کانتینر به این سقف برسد، کرنل لینوکس دسترسی آن به CPU را موقتاً محدود میکند؛ حتی اگر نود همچنان CPU آزاد داشته باشد.
برای درک بهتر تفاوت Request و Limit، در مثال زیر مقدار منابع موردنیاز کانتینر برای انتخاب نود و همچنین سقف مصرف آن پس از اجرا مشخص شده است:
apiVersion: v1
kind: Pod
metadata:
name: demo-app
spec:
containers:
- name: web
image: nginx
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "512Mi"در این مثال:
- requests.cpu: 500m یعنی پاد برای زمانبندی به نیم هسته CPU نیاز دارد.
- requests.memory: 512Mi یعنی Scheduler باید نودی را انتخاب کند که بتواند حداقل 512Mi حافظه را برای این پاد در نظر بگیرد.
- limits.cpu: 1 یعنی کانتینر در زمان اجرا نمیتواند بیش از یک هسته CPU مصرف کند.
- limits.memory: 512Mi سقف مصرف حافظه را مشخص میکند و عبور از این مقدار میتواند به OOM Kill منجر شود. در OOM Kill، کرنل بهدلیل فشار حافظه یا عبور کانتینر از محدودیت حافظه، یکی از فرایندهای آن را متوقف میکند.
Request در Scheduling چه نقشی دارد؟
یکی از رایجترین سو برداشتها درباره CPU Request در کوبرنتیز این است که آن را معادل مصرف واقعی برنامه در نظر میگیرند؛ درحالیکه Request در اصل یک سیگنال برای زمانبندی (Scheduling در کوبرنتیز) است و قرار نیست گزارشی از مصرف لحظهای کانتینر ارائه دهد. به همین دلیل ممکن است نود از نظر مصرف واقعی CPU تقریبا بیکار به نظر برسد، اما Scheduler همچنان نتواند پاد جدیدی را روی آن قرار دهد.
اگر مجموع CPU Request پادهای مستقر روی یک نود به ظرفیت قابل تخصیص (Allocatable) آن رسیده باشد، کوبرنتیز دیگر پاد جدیدی را روی آن نود قرار نمیدهد؛ حتی اگر نمودارهای مانیتورینگ نشان دهند که مصرف واقعی CPU پایین است. در چنین شرایطی، اگر نود مناسب دیگری هم وجود نداشته باشد، پاد جدید در وضعیت Pending باقی میماند و اجرا نمیشود.
تنظیم نادرست Requestها میتواند روی بهرهوری کل کلاستر هم تاثیر بگذارد. اگر Requestها بیش از نیاز واقعی Workload تعیین شوند، Scheduler باید آن مقدار را در محاسبه ظرفیت قابل زمانبندی نود لحاظ کند. در نتیجه، بخشی از ظرفیت قابل زمانبندی (Schedulable Capacity) برای استقرار پادهای دیگر قابل استفاده نخواهد بود؛ حتی اگر مصرف واقعی CPU پایین باشد. پس بهتر است Request را بر اساس نیاز واقعی Workload تنظیم کنیم، زیرا Scheduler برای استقرار Podها به همین مقدار تکیه میکند و مصرف لحظهای برنامه را معیار قرار نمیدهد.
CPU Limit در کوبرنتیز در پشت صحنه چطور اعمال میشود؟
برخلاف تصور رایج، کوبرنتیز مستقیماً مصرف CPU کانتینر را محدود نمیکند. پس از اینکه Scheduler نود مناسب را برای پاد انتخاب کرد، kubelet که روی آن نود اجرا میشود، مشخصات پاد را دریافت میکند. (عامل اجرایی کوبرنتیز روی هر نود است که پادها و کانتینرهای آن نود را راهاندازی و مدیریت میکند.) سپس kubelet از طریق Container Runtime (مانند containerd)، کانتینر را راهاندازی میکند.
Container Runtime برای هر کانتینر یک cgroup ایجاد کرده و مقادیر Request و Limit را به آنها اعمال میکند. cgroup قابلیتی در لینوکس است که برای مدیریت و محدودکردن منابع پردازشی کانتینرها به کار میرود. از این مرحله به بعد، اعمال محدودیت CPU بر عهده کرنل لینوکس است.
در این فرایند، CPU Request و CPU Limit نقشهای متفاوتی دارند. مقدار CPU Request معمولاً به cpu.weight تبدیل میشود و وزن نسبی کانتینر را هنگام رقابت بر سر CPU مشخص میکند. این مقدار به معنای اختصاص یا رزرو مقدار ثابتی از CPU نیست. در مقابل، CPU Limit به cpu.max تبدیل میشود و سقف سخت مصرف CPU کانتینر را تعیین میکند.
cpu.max = <quota> <period>برای مثال، اگر CPU Limit برابر 200m باشد، مقدار cpu.max بهصورت زیر تنظیم میشود:
cpu.max = 20000 100000در این مثال، کانتینر در هر بازه ۱۰۰،۰۰۰ میکروثانیه (معادل ۱۰۰ میلیثانیه) فقط مجاز است ۲۰،۰۰۰ میکروثانیه (معادل ۲۰ میلیثانیه) از CPU استفاده کند. اگر این سهم را پیش از پایان بازه مصرف کند، کرنل اجرای آن را تا شروع دوره بعدی متوقف میکند. همین توقف موقت را CPU Throttling در کوبرنتیز مینامند. رابطه میان Request و Limit با تنظیمات cgroup را میتوان بهصورت زیر خلاصه کرد:
CPU request → cpu.weight → controls fairness
CPU limit → cpu.max → enforces a hard capنکته مهم این است که CPU Throttling صرفا به معنای «کمی کندتر شدن» کانتینر نیست. اگر سهم CPU مجاز در یک بازه زمانی بهطور کامل مصرف شود، کانتینر تا آغاز بازه بعدی، عملا امکان دریافت CPU بیشتری را نخواهد داشت.
چرا با وجود CPU آزاد روی نود باز هم پاد Throttle میشود؟
شاید در نگاه اول این اتفاق غیرمنطقی به نظر برسد که اگر نود هنوز CPU آزاد دارد، چرا یک پاد باید با CPU Throttling مواجه شود؟ پاسخ این سوال را باید در نحوه اعمال CPU Limit در کوبرنتیز بررسی کنیم.
همانطور که در بخش قبل گفتیم، CPU Limit در سطح cgroup اعمال میشود، نه در سطح کل نود. کرنل این محدودیت را براساس سهمیه زمانی CPU تعیینشده برای همان cgroup اعمال میکند و میزان CPU آزاد نود را در این تصمیم دخالت نمیدهد. به همین دلیل، پایینبودن مصرف CPU در سطح نود لزوماً به این معنا نیست که همه کانتینرها میتوانند از ظرفیت آزاد آن استفاده کنند. ممکن است نود همچنان CPU آزاد داشته باشد، اما کانتینری که سهمیه زمانی CPU خود را مصرف کرده است، تا آغاز دوره بعدی نتواند CPU بیشتری دریافت کند.
همین موضوع ممکن است باعث شود مشکل را بهاشتباه به کمبود منابع نود نسبت دهیم. در واقع، نود با کمبود CPU مواجه نیست؛ بلکه کانتینر سهمیه زمانی CPU خود را در آن دوره مصرف کرده است و کرنل تا آغاز دوره بعدی اجازه استفاده بیشتر از CPU را به آن نمیدهد.
در نتیجه، با وجود ظرفیت پردازشی بلااستفاده روی نود، سرویس ممکن است با افزایش تأخیر، تشکیل صف درخواستها (Queue Buildup) و در نهایت Timeout مواجه شود.
CPU Limit با Memory Limit چه فرقی دارد؟
یکی از اشتباهات رایج درباره CPU Limit در کوبرنتیز این است که آن را با همان منطق Memory Limit در نظر میگیرند؛ درحالیکه این دو از نظر نوع و رفتار هنگام رسیدن به Limit با هم متفاوتاند. جدول زیر این تفاوت را نشان میدهد:
| منبع | رفتار هنگام عبور از Limit | نتیجه محتمل |
| CPU | CPU Throttling | افزایش Latency ،Timeout و کاهش Throughput |
| Memory | OOM Kill | Restart کانتینر و اختلال در سرویس |
این تفاوت در محیطهای عملیاتی اهمیت زیادی دارد. اگر افزایش زمان پاسخگویی مشاهده میکنید، ممکن است ریشه مشکل CPU Throttling باشد. اگر کانتینری بهطور ناگهانی Restart میشود، ابتدا Last Termination Reason را بررسی کنید تا مشخص شود آیا دلیل توقف آن OOMKilled بوده است یا نه. در صورت مشاهده این وضعیت، Memory Limit و میزان واقعی مصرف حافظه را بررسی کنید.
برای سرویسهای حساس به تاخیر چه الگویی بهتر است؟
اگر سرویسی به تاخیر حساس است، هدف فقط محدود کردن مصرف CPU نیست و باید تعادلی بین عملکرد و مدیریت منابع برقرار شود. پیشنهاد میشود که CPU Request بر اساس الگوی واقعی مصرف تنظیم شود و CPU Limit بهصورت پیشفرض اعمال نشود؛ مگر اینکه واقعا به یک سقف سخت برای مصرف CPU نیاز باشد.
همانطور که پیشتر دیدیم، CPU Limit سختگیرانه میتواند حتی با وجود CPU آزاد روی نود باعث Throttling شود. به همین دلیل، برای بسیاری از سرویسهای حساس به تاخیر، تنظیم یک CPU Request متناسب با مصرف واقعی و حذف CPU Limit میتواند به کانتینر اجازه دهد از CPU آزاد نود برای مدیریت Burstهای کوتاهمدت (افزایش ناگهانی و موقت مصرف CPU) استفاده کند. نمونه زیر همین رویکرد را نشان میدهد:
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-api
spec:
replicas: 3
template:
spec:
containers:
- name: checkout-api
image: example.com/checkout-api:1.0.0
resources:
requests:
cpu: "300m"
memory: "512Mi"
limits:
memory: "512Mi"در این مثال، برای هر پاد CPU Request برابر 300m و Memory Request برابر 512Mi تعریف شده است. Memory Limit نیز روی 512Mi قرار دارد، اما هیچگونه CPU Limit تعریف نشده است. حذف CPU Limit به سرویس اجازه میدهد در صورت وجود ظرفیت آزاد روی نود، Burstهای کوتاهمدت مصرف CPU را بدون مواجهه با یک سقف سخت مدیریت کند.
البته این توصیه به این معنا نیست که CPU Limit همیشه باید حذف شود. در برخی Workloadهای حساس به تاخیر، حذف CPU Limit میتواند گزینه مناسبتری باشد؛ اما تنها زمانی که CPU Request باتوجهبه رفتار واقعی برنامه تنظیم شده و الگوی مصرف آن در محیط عملیاتی بررسی شده باشد.
برای عیبیابی Request و Limit چه مواردی را بررسی کنیم؟
اگر احتمال میدهید مشکل به تنظیمات Request یا Limit مربوط باشد، چند بررسی اولیه میتواند سرنخهای مهمی در اختیارتان قرار دهد. ابتدا مصرف فعلی CPU و Memory هر پاد را بررسی کنید:
kubectl top podسپس جزئیات پاد را مشاهده کنید تا مقدار Request ،Limit، کلاس QoS، رویدادها (Events) و آخرین دلیل توقف کانتینر (Last Termination Reason) را ببینید:
kubectl describe pod <pod-name>اگر احتمال میدهید پاد بهدلیل کمبود منابع روی هیچ نودی زمانبندی نشده است، Eventهای مربوط به Scheduling را نیز بررسی کنید:
kubectl describe pod frontend | grep -A 9999999999 Eventsبه این نکته توجه داشته باشید که kubectl top pod فقط مصرف فعلی CPU و Memory را نمایش میدهد و بهتنهایی برای تشخیص CPU Throttling کافی نیست. برای تشخیص این وضعیت باید متریکهای مربوط به CPU Throttling را در ابزار مانیتورینگ بررسی کنید.
💡 کوبرنتیز مدیریتشده همروش، راهی مطمئن برای رشد بیوقفه
راهکار کوبرنتیز مدیریت شده، بر بستر ابر اختصاصی یا به صورت On-Premises
✅ کاهش هزینههای عملیاتی
✅ احراز هویت یکپارچه با اتصال به SSO سازمانی
✅ قابل استقرار روی سرورهای on-premises
چه زمانی CPU Limit نگذاریم؟
CPU Limit در کوبرنتیز ممکن است حتی در صورت وجود CPU آزاد روی نود، باعث CPU Throttling و افزایش تاخیر شود. تعریفنکردن CPU Limit میتواند برای موارد زیر مناسبتر باشد:
- سرویسهای حساس به تاخیر
- Workloadهایی که برای مدیریت Burstهای کوتاهمدت (افزایش ناگهانی و موقت مصرف CPU) باید بتوانند از CPU آزاد نود استفاده کنند
- سرویسهایی که CPU Throttling در آنها باعث افزایش تاخیر، تشکیل صف درخواستها، Timeout یا کاهش Throughput میشود
در این شرایط، CPU Request باید براساس مصرف واقعی Workload تنظیم شود. CPU Limit نیز فقط زمانی تعریف شود که Workload واقعاً به یک سقف سخت برای مصرف CPU نیاز داشته باشد.
چه زمانی CPU Limit در کوبرنتیز همچنان مفید است؟
با اینکه حذف CPU Limit در کوبرنتیز میتواند برای بسیاری از سرویسهای حساس به تأخیر مناسبتر باشد، استفاده از آن در برخی سناریوها همچنان قابل دفاع است. CPU Limit باید زمانی تعریف شود که Workload واقعاً به یک سقف سخت برای مصرف CPU نیاز داشته باشد، نه صرفاً به این دلیل که بهصورت پیشفرض در یک الگوی پیکربندی قرار گرفته است. استفاده از CPU Limit در شرایط زیر میتواند مناسب باشد:
- کلاسترهای Multi-tenant که چند تیم یا چند سرویس از منابع مشترک استفاده میکنند
- Workloadهای غیر قابل اعتماد (Untrusted) که باید مصرف CPU آنها بهطور سختگیرانه کنترل شود
- جابهای پردازش دستهای (Batch Jobs) که به سقف مشخصی برای مصرف CPU نیاز دارند
- محیطهای Benchmark و Performance Test که تکرارپذیری نتایج اهمیت دارد
- سرویسهایی که بر اساس پلنهای ثابت CPU به مشتریان ارائه میشوند
- سناریوهایی که Strict Cost Control در آنها اهمیت دارد و مصرف CPU باید در یک بودجه مشخص باقی بماند
- محیطهایی که سیاستهای پلتفرم، تعریف CPU Limit را الزامی میکنند
در این سناریوها، CPU Limit یک سقف مشخص برای مصرف CPU ایجاد میکند؛ هرچند ممکن است امکان استفاده Workload از CPU آزاد نود را در زمان Burst محدود کند. CPU Limit نیز باید فقط زمانی تعریف شود که Workload واقعاً به یک سقف سخت برای مصرف CPU نیاز داشته باشد.
جمعبندی
CPU Limit در کوبرنتیز همیشه به بهبود عملکرد و پایداری سرویس منجر نمیشود و حتی ممکن است با وجود CPU آزاد روی نود، باعث Throttling و افزایش تأخیر شود. بنابراین، Request و Limit را براساس رفتار واقعی هر Workload تنظیم کنید و CPU Limit را فقط زمانی به کار ببرید که به سقف سخت مصرف نیاز دارید. بررسی مصرف منابع و متریکهای CPU Throttling نیز کمک میکند این تصمیم براساس دادههای واقعی گرفته شود، نه تنظیمات پیشفرض.