KUBERNETES

Requests، Limits و HPA در Kubernetes: تنظیم منابع و مقیاس‌پذیری خودکار

تفاوت requests و limits، کلاس‌های QoS، علت OOMKilled و CPU throttling و راه‌اندازی HorizontalPodAutoscaler با مثال و فرمول محاسبه‌ی تعداد replicas.

مدیریت منابع در Kubernetes دو سؤال را پاسخ می‌دهد: هر Pod چقدر منابع «لازم دارد» تا روی یک نود جا بگیرد، و هر Pod حداکثر چقدر «اجازه‌ی مصرف» دارد. تنظیم نادرست این دو، پشت بسیاری از پادهای OOMKilled، نودهای ناپایدار و کلاسترهایی است که با وجود منابع آزاد، پاد جدید را زمان‌بندی نمی‌کنند. در ادامه، requests و limits، کلاس‌های QoS و راه‌اندازی HPA را با مثال بررسی می‌کنیم.

requests و limits: دو مفهوم متفاوت

requestslimits
معنیمقدار تضمین‌شده؛ مبنای زمان‌بندیسقف مصرف مجاز
استفاده‌کنندهscheduler (انتخاب نود)kubelet / هسته (اعمال محدودیت)
عبور از آن در CPUمشکلی نیست (تا حد limit)کندی (throttling)
عبور از آن در حافظهمشکلی نیست (تا حد limit)کشته‌شدن کانتینر (OOMKilled)
resources:
  requests:
    cpu: 250m          # یک‌چهارم هسته
    memory: 256Mi
  limits:
    cpu: "1"
    memory: 256Mi

واحد CPU بر حسب هسته است (1000m = 1) و حافظه با Mi/Gi (توان ۲) نوشته می‌شود؛ 256M و 256Mi یکی نیستند.

کلاس‌های QoS و ترتیب حذف

Kubernetes بر اساس requests و limits هر Pod یکی از سه کلاس QoS را به آن می‌دهد و هنگام کمبود حافظه در نود، از پایین‌ترین کلاس شروع به حذف می‌کند:

  • Guaranteed: برای همه‌ی کانتینرها requests برابر limits است (هم CPU، هم حافظه). آخرین کاندیدای حذف.
  • Burstable: دست‌کم یک request یا limit تعریف شده ولی شرط Guaranteed برقرار نیست. حالت متداول.
  • BestEffort: هیچ requests و limits نیست. اولین کاندیدای حذف.

کلاس هر Pod را می‌توانید با kubectl get pod <name> -o jsonpath='{.status.qosClass}' ببینید.

چگونه مقدار مناسب را پیدا کنیم؟

مقدارهای حدسی معمولاً یا بسیار کم‌اند (OOMKilled و throttling) یا بسیار زیاد (هدررفت و پادهای Pending). روش عملی:

  1. با مقدارهای محافظه‌کارانه شروع کنید و برنامه را زیر بار واقعی یا آزمایشی اجرا کنید.
  2. مصرف واقعی را بخوانید: kubectl top pod (نیازمند metrics-server) یا نمودارهای Prometheus (container_memory_working_set_bytes و rate(container_cpu_usage_seconds_total[5m])).
  3. requests را نزدیک مصرف معمول (مثلاً صدک ۹۰) و limits حافظه را کمی بالاتر از اوج مصرف بگذارید.
  4. نتیجه را در طول زمان بازبینی کنید؛ مصرف با رشد برنامه عوض می‌شود.
kubectl top pod -n production
kubectl top node

قاعده‌های تجربی

  • حافظه: requests را برابر limits بگذارید. حافظه قابل‌فشرده‌سازی نیست؛ برنامه‌ای که بیش از requests مصرف کند اولین هدف حذف است، و limit بالاتر از request فقط بیشتر نودها را بیش‌ازحد متعهد (overcommit) می‌کند.
  • CPU: requests را واقع‌بینانه بگذارید ولی limit سخت‌گیرانه نگذارید یا آن را خیلی بالاتر بگیرید. CPU قابل‌فشرده‌سازی است و limit پایین فقط throttling ایجاد می‌کند که تأخیر را بالا می‌برد.
  • JVM و Node.js: اندازه‌ی heap را به limit کانتینر وابسته کنید (-XX:MaxRAMPercentage در جاوا، --max-old-space-size در Node.js)؛ در غیر این صورت برنامه فکر می‌کند حافظه‌ی کل نود را دارد.

HorizontalPodAutoscaler

HPA تعداد replicas یک Deployment را بر اساس متریک (معمولاً CPU یا حافظه) خودکار تغییر می‌دهد. فرمول آن:

desiredReplicas = ceil( currentReplicas × currentMetric / targetMetric )

مثال: 4 replica، مصرف فعلی 90% از requests، هدف 60%
→ ceil(4 × 90 / 60) = 6 replica

نکته‌ی کلیدی: درصد مصرف نسبت به requests حساب می‌شود، نه limits. پس بدون requests، HPA کار نمی‌کند.

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 3
  maxReplicas: 12
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60
K8SKubernetes YAML GeneratorDeployment همراه با requests و limits و HPA را با فرم بسازید.

پیش‌نیازها: نصب metrics-server در کلاستر، تعریف requests برای همه‌ی کانتینرهای Pod، و readinessProbe درست؛ بدون آن، Podهای جدید قبل از آماده‌شدن ترافیک می‌گیرند. وضعیت را با این دستورها ببینید:

kubectl get hpa web --watch
kubectl describe hpa web        # رویدادها و دلیل تصمیم‌ها

اشتباه‌های رایج HPA

  • minReplicas: 1 برای سرویس مهم؛ قطع یک Pod یعنی قطع سرویس.
  • هدف CPU خیلی بالا (مثل ۹۰٪)؛ تا HPA واکنش نشان دهد، تأخیر بالا رفته است. ۵۰ تا ۷۰ درصد معمول است.
  • نگه‌داشتن replicas در مانیفست Deployment هم‌زمان با HPA؛ هر kubectl apply تعداد را به مقدار ثابت برمی‌گرداند. فیلد replicas را وقتی HPA دارید حذف کنید.
  • مقیاس‌پذیری بر پایه‌ی CPU برای برنامه‌های I/O‌-محور؛ در این سرویس‌ها CPU بالا نمی‌رود. از متریک سفارشی (مثل طول صف یا تعداد درخواست) استفاده کنید.

LimitRange و ResourceQuota

در کلاستر مشترک، می‌توانید برای هر namespace مقدار پیش‌فرض و سقف تعریف کنید تا هیچ Podی بدون منابع ساخته نشود و هیچ تیمی کل کلاستر را مصرف نکند:

apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
spec:
  limits:
    - type: Container
      defaultRequest: { cpu: 100m, memory: 128Mi }
      default:        { cpu: 500m, memory: 256Mi }
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
spec:
  hard:
    requests.cpu: "8"
    requests.memory: 16Gi
    pods: "40"

جمع‌بندی و مطالعه‌ی بعدی

requests را مبنای زمان‌بندی و HPA بدانید، limits را سقف ایمنی، و مقدارها را از مصرف واقعی بخوانید نه حدس. اگر پادها OOMKilled یا Pending می‌شوند، راهنمای عیب‌یابی با kubectl مسیر بررسی را نشان می‌دهد و برای دیدن نمودار مصرف، راه‌اندازی Prometheus و Grafana را ببینید. مانیفست‌های پایه هم در راهنمای Deployment، Service و Ingress آمده‌اند.

پرسش‌های متداول

اگر requests و limits را ننویسم چه می‌شود؟
Pod در کلاس QoS «BestEffort» قرار می‌گیرد: scheduler مصرف آن را صفر فرض می‌کند، ممکن است روی نودی بنشیند که جا ندارد و هنگام کمبود منابع اولین کاندیدای حذف است. HPA هم برای چنین Podی نمی‌تواند درصد مصرف را حساب کند.
چرا Pod من OOMKilled می‌شود؟
مصرف حافظه‌ی کانتینر از limit بیشتر شده و هسته آن را کشته است. یا limit کم است، یا برنامه نشتی حافظه دارد، یا (در JVM و Node.js) تنظیم heap با limit کانتینر هماهنگ نیست. با kubectl describe pod فیلد Last State: Terminated (OOMKilled) را ببینید.
HPA چقدر طول می‌کشد تا واکنش نشان دهد؟
HPA هر ۱۵ ثانیه (پیش‌فرض) مقدار متریک را بررسی می‌کند، ولی برای جلوگیری از نوسان، کاهش تعداد replicas به‌طور پیش‌فرض ۵ دقیقه پنجره‌ی پایداری دارد. افزایش سریع‌تر از کاهش انجام می‌شود. این رفتارها با فیلد behavior قابل‌تنظیم‌اند.
HPA و VPA را هم‌زمان استفاده کنم؟
برای یک متریک مشترک (مثل CPU) هم‌زمانی آن‌ها با هم تداخل می‌کند. معمولاً HPA برای تعداد replicas و VPA (در حالت توصیه‌گر) برای پیشنهاد requests استفاده می‌شود.