سامانههای کیف پول و پرداخت الگوی ترافیک عجیبی دارند: ساعت ۳ صبح تقریباً هیچ، ساعت ۱۲ ظهر ده برابر و روز اول ثبتنام دانشگاه پنجاه برابر. اگر برای اوج ترافیک 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 استفاده کنید تا در کمبود منابع، سرویس پرداخت به سرویس گزارشگیری اولویت داشته باشد.
اشتباهات رایج
- HPA و
replicasدر Helm همزمان: اگر Helm هر بارreplicas: 3را اعمال کند، با تصمیم HPA میجنگد. وقتی HPA فعال است، فیلدreplicasرا از Deployment حذف کنید. - HPA روی Memory برای برنامههای JVM و .NET: این Runtimeها حافظه را آزاد نمیکنند. HPA بالا میرود و هیچوقت پایین نمیآید.
- Scale کردن سرویسی که گلوگاهش دیتابیس است: Pod بیشتر یعنی Connection بیشتر به دیتابیس و احتمالاً کندی بیشتر. اول گلوگاه را پیدا کنید.
جمعبندی
HPA جادو نیست؛ به Request درست، Probe دقیق، behavior حسابشده و ظرفیت کافی در کلاستر نیاز دارد. با اینها سرویستان در روز اوج ترافیک بیصدا بزرگ میشود و نیمهشب بیصدا کوچک، و هیچکس مجبور نیست بیدار بماند.