Kubernetes سطح: متوسط

مقیاس‌پذیری خودکار در Kubernetes با HPA؛ از Request و Limit تا متریک‌های سفارشی

آموزش HPA در کوبرنتیز: Request و Limit درست، تنظیم behavior، تست بار و اسکیل بر اساس متریک‌های Prometheus در کلاستر On-Premise.

۵ دقیقه مطالعه

سامانه‌های کیف پول و پرداخت الگوی ترافیک عجیبی دارند: ساعت ۳ صبح تقریباً هیچ، ساعت ۱۲ ظهر ده برابر و روز اول ثبت‌نام دانشگاه پنجاه برابر. اگر برای اوج ترافیک Pod اضافه نگه دارید، بیشتر وقت منابع هدر می‌رود. اگر برای حالت عادی تنظیم کنید، روز اوج سرویس از دسترس خارج می‌شود.

Horizontal Pod Autoscaler (HPA) تعداد Podها را بر اساس بار واقعی کم و زیاد می‌کند. در یکی از پروژه‌های پرداخت دانشگاهی، همین قابلیت ستون اصلی تحمل ترافیک روزهای پرازدحام بود.

HPA چطور تصمیم می‌گیرد؟

فرمول ساده است:

desiredReplicas = ceil( currentReplicas × currentMetric / targetMetric )

اگر ۴ Pod دارید، هدف ۷۰٪ CPU است و مصرف فعلی ۱۰۵٪ است، HPA تعداد را به ceil(4 × 105 / 70) = 6 می‌رساند.

پیش‌نیاز: metrics-server

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl top pods -n production     # if this works, HPA has data

در کلاسترهای kubeadm که گواهی kubelet خودامضا است، ممکن است لازم باشد آرگومان --kubelet-insecure-tls را به metrics-server اضافه کنید؛ راه‌حل درست‌تر، امضای گواهی‌های kubelet با CA کلاستر است.

یک HPA کامل

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: wallet-api
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: wallet-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target: { type: Utilization, averageUtilization: 70 }
    - type: Resource
      resource:
        name: memory
        target: { type: Utilization, averageUtilization: 80 }
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      policies:
        - { type: Percent, value: 100, periodSeconds: 30 }   # can double every 30s
        - { type: Pods, value: 4, periodSeconds: 30 }
      selectPolicy: Max
    scaleDown:
      stabilizationWindowSeconds: 300                        # wait 5 min before shrinking
      policies:
        - { type: Percent, value: 20, periodSeconds: 60 }

بخش behavior مهم‌ترین قسمت این فایل است:

  • Scale Up سریع: وقتی ترافیک ناگهانی می‌آید، هر ثانیه مهم است.
  • Scale Down آهسته: ترافیک معمولاً موجی است. اگر Podها با هر افت کوتاه حذف شوند، چند ثانیه بعد دوباره باید ساخته شوند. به این وضعیت Flapping می‌گویند.

تست بار: قبل از روز واقعی

HPA را تا زیر بار ندیده‌اید، به آن اعتماد نکنید. با ابزارهایی مثل k6 یا hey بار مصنوعی بسازید و رفتارش را تماشا کنید:

# 200 concurrent clients for 5 minutes
hey -z 5m -c 200 https://dev.example.ir/api/wallet/balance

# in another terminal
kubectl get hpa wallet-api -n production -w

سه چیز را اندازه بگیرید: از شروع بار تا Ready شدن Pod جدید چقدر طول کشید؟ در این فاصله زمان پاسخ و درصد خطا چه شد؟ و بعد از قطع بار، Scale Down چقدر طول کشید؟

اگر Ready شدن Pod جدید بیش از یک دقیقه طول می‌کشد، اول همان را بهینه کنید: Image کوچک‌تر، Startup سریع‌تر و Readiness Probe دقیق‌تر.

Scale بر اساس متریک‌های واقعی کسب‌وکار

CPU برای سرویس‌هایی که بیشتر منتظر دیتابیس یا سرویس دیگری هستند، معیار خوبی نیست. ممکن است صف درخواست‌ها پر شده باشد ولی CPU فقط ۲۰٪ باشد. در این حالت بر اساس تعداد درخواست در ثانیه یا طول صف Scale کنید.

با prometheus-adapter، هر متریک Prometheus برای HPA قابل استفاده می‌شود:

# prometheus-adapter rule
rules:
  - seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
    resources:
      overrides:
        namespace: { resource: namespace }
        pod: { resource: pod }
    name:
      as: "http_requests_per_second"
    metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
# in the HPA
metrics:
  - type: Pods
    pods:
      metric: { name: http_requests_per_second }
      target: { type: AverageValue, averageValue: "50" }   # 50 RPS per pod

HPA در کلاستر On-Premise

در ابرهای عمومی، Cluster Autoscaler وقتی جا کم بیاید Node جدید می‌سازد. در کلاستر روی سرورهای داخلی چنین چیزی معمولاً وجود ندارد:

  • اگر HPA بخواهد ۲۰ Pod بسازد و جا فقط برای ۱۲ تا باشد، ۸ Pod در وضعیت Pending می‌مانند.
  • maxReplicas همه‌ی سرویس‌ها را با هم جمع بزنید و با ظرفیت واقعی کلاستر مقایسه کنید.
  • برای Podهای Pending در Prometheus یک Alert بگذارید؛ این اولین نشانه‌ی کمبود ظرفیت است.
  • از PriorityClass استفاده کنید تا در کمبود منابع، سرویس پرداخت به سرویس گزارش‌گیری اولویت داشته باشد.

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

  1. HPA و replicas در Helm هم‌زمان: اگر Helm هر بار replicas: 3 را اعمال کند، با تصمیم HPA می‌جنگد. وقتی HPA فعال است، فیلد replicas را از Deployment حذف کنید.
  2. HPA روی Memory برای برنامه‌های JVM و .NET: این Runtimeها حافظه را آزاد نمی‌کنند. HPA بالا می‌رود و هیچ‌وقت پایین نمی‌آید.
  3. Scale کردن سرویسی که گلوگاهش دیتابیس است: Pod بیشتر یعنی Connection بیشتر به دیتابیس و احتمالاً کندی بیشتر. اول گلوگاه را پیدا کنید.

جمع‌بندی

HPA جادو نیست؛ به Request درست، Probe دقیق، behavior حساب‌شده و ظرفیت کافی در کلاستر نیاز دارد. با این‌ها سرویس‌تان در روز اوج ترافیک بی‌صدا بزرگ می‌شود و نیمه‌شب بی‌صدا کوچک، و هیچ‌کس مجبور نیست بیدار بماند.