HPA — Behavior Policies, Metrics Sources & Debugging
File trước mổ xẻ control loop và thuật toán nền tảng của HPA. File này tập trung vào ba thứ quyết định hành vi HPA ở production: behavior policy (điều khiển tốc độ và độ ổn định của scale), nguồn metric (cái HPA thực sự nhìn vào và pipeline phía sau), và quy trình debug khi mọi thứ không như mong đợi. Đây là phần phân biệt một HPA "chạy được" với một HPA được vận hành có kỷ luật.
Vì sao behavior policy là đòn bẩy quan trọng nhất bạn kiểm soát được
Trên GKE, bạn không sửa được --horizontal-pod-autoscaler-sync-period hay các flag của kube-controller-manager. Đòn bẩy mạnh nhất nằm hoàn toàn trong tay bạn là trường behavior của HPA trong API autoscaling/v2. Nó cho phép định nghĩa riêng biệt cách HPA được phép scale lên và scale xuống — tốc độ tối đa, cửa sổ ổn định, và chính sách chọn khi có nhiều quy tắc.
Mặc định (đã phân tích ở file 1) là scale-up nhanh, scale-down chậm với cửa sổ 300 giây. Mặc định này đúng cho hầu hết web/API service. Nhưng có những lớp workload cần khác đi, và đó là lúc behavior trở nên thiết yếu.
Cấu trúc behavior
Theo tài liệu Kubernetes (Configurable scaling behavior), behavior có hai nhánh scaleUp và scaleDown, mỗi nhánh chứa:
policies: danh sách quy tắc giới hạn tốc độ. Mỗi policy có:type:Pods(thay đổi theo số Pod tuyệt đối) hoặcPercent(thay đổi theo phần trăm số replica hiện tại).value: độ lớn cho phép.periodSeconds: cửa sổ thời gian áp dụng giới hạn đó.
selectPolicy: khi có nhiều policy, chọn cái nào —Max(cho phép thay đổi lớn nhất),Min(nhỏ nhất), hoặcDisabled(vô hiệu hóa scale theo chiều đó).stabilizationWindowSeconds: cửa sổ ổn định cho chiều đó.
Ví dụ: scale-up bùng nổ, scale-down từ tốn
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 100
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100 # cho phép gấp đôi số replica...
periodSeconds: 30 # ...mỗi 30 giây
- type: Pods
value: 10 # hoặc thêm tối đa 10 Pod mỗi 30s
periodSeconds: 30
selectPolicy: Max # chọn quy tắc cho phép tăng nhiều nhất
scaleDown:
stabilizationWindowSeconds: 600 # giữ recommendation cao nhất trong 10 phút
policies:
- type: Percent
value: 10 # mỗi phút chỉ được bớt 10% số replica
periodSeconds: 60
selectPolicy: MaxDiễn giải production:
scaleUpvớiselectPolicy: Maxkết hợp hai policy: hệ thống chọn quy tắc cho phép tăng nhiều hơn. Khi đang ở số replica nhỏ, "gấp đôi" thắng; khi ở số lớn, có thể "+10 Pod" lại nhỏ hơn —Maxđảm bảo phản ứng đủ mạnh ở mọi quy mô.scaleDownvớiPercent 10% / 60sđảm bảo không bao giờ rút công suất quá nhanh: kể cả khi tải về 0, bạn chỉ giảm 10% mỗi phút, cho connection draining và cache có thời gian.
Các pattern behavior theo loại workload
| Loại workload | scaleUp | scaleDown | Lý do |
|---|---|---|---|
| Web/API phục vụ người dùng | Nhanh (Percent 100%, window 0) | Chậm (window 300–600s, Percent 10%/min) | Spike traffic phải bắt kịp; rút chậm tránh flapping |
| Batch/queue worker | Trung bình | Nhanh (window thấp) | Không có người dùng đợi; rút nhanh tiết kiệm tiền |
| Stateful/connection nặng | Trung bình | Rất chậm (Pods 1/period dài) | Mỗi lần giết Pod reset nhiều connection |
| Cost-sensitive, tải đoán được | Trung bình | selectPolicy: Disabled (scale tay xuống) | Tránh mọi scale-down tự động ngoài giờ |
selectPolicy: Disabled cho scaleDown là một công cụ ít người biết: nó vô hiệu hóa hoàn toàn scale-down tự động, hữu ích khi bạn muốn HPA chỉ được scale lên (ví dụ trước một event lớn đã biết) và tự kiểm soát scale xuống.
Ba nguồn metric và pipeline phía sau
HPA scale dựa trên một trong ba loại metric (GKE HPA). Hiểu pipeline phía sau mỗi loại là chìa khóa để debug "vì sao HPA không lấy được metric".
1. Resource metrics (CPU/Memory)
Đây là loại đơn giản nhất: CPU hoặc memory của container, có thể biểu diễn dưới dạng giá trị thô (averageValue) hoặc phần trăm của requests (averageUtilization).
Điểm cực kỳ quan trọng: averageUtilization được tính so với requests, không phải limits và không phải dung lượng node. Nếu requests.cpu đặt sai, averageUtilization vô nghĩa. Một container request 100m nhưng dùng 200m sẽ hiển thị 200% utilization. Đây là một lý do nữa vì sao đặt requests đúng (file 1, file 3 về VPA) là tiền đề của mọi autoscaling đúng.
Pipeline: kubelet → metrics-server (API metrics.k8s.io) → HPA. Trên GKE, metrics-server được Google quản lý và cài sẵn. Nếu ScalingActive=False với FailedGetResourceMetric, nghĩa là metrics-server không trả dữ liệu — kiểm tra metrics-server có chạy không, và container có khai báo requests không (không có requests, utilization không tính được).
2. Custom Metrics
Metric từ bên trong cluster, gắn với một đối tượng Kubernetes — ví dụ requests/giây của một Pod, độ sâu hàng đợi nội bộ, IOPS. Pipeline qua API custom.metrics.k8s.io, cần một adapter đứng giữa nguồn metric và HPA.
Trên GKE, con đường khuyến nghị là Google Cloud Managed Service for Prometheus + Custom Metrics Stackdriver Adapter (hoặc adapter Prometheus). Workload expose metric kiểu Prometheus → Managed Prometheus thu thập → adapter dịch thành custom.metrics.k8s.io → HPA đọc. Đây là pattern để scale theo "đơn vị công việc thật" thay vì CPU.
3. External Metrics
Metric từ ngoài cluster hoàn toàn — ví dụ kích thước hàng đợi Pub/Sub, độ trễ của một dependency, số message trong Cloud Tasks. Pipeline qua API external.metrics.k8s.io.
Đây là loại metric phản ánh đúng nhất "lượng việc đang chờ" cho nhiều kiến trúc event-driven. Tuy nhiên, với scale theo backlog hàng đợi (đặc biệt scale-to-zero), KEDA (file 8) thường là công cụ tốt hơn HPA thuần vì nó đóng gói sẵn hàng chục external scaler và xử lý activation từ 0.
Quy tắc chọn metric: phản ánh đúng đơn vị công việc
Sai lầm thiết kế lớn nhất với HPA là scale theo CPU khi CPU không phải bottleneck. Một API gateway nghẽn vì số connection đồng thời, một worker nghẽn vì độ sâu hàng đợi, một service nghẽn vì độ trễ downstream — trong cả ba, CPU có thể thấp trong khi service đã quá tải. HPA theo CPU sẽ không bao giờ scale đúng.
Nguyên tắc: chọn metric tỷ lệ tuyến tính với lượng việc và với độ đau của người dùng. Với web service đo bằng RPS hoặc concurrent requests; với worker đo bằng queue depth; với pipeline đo bằng lag. CPU chỉ là proxy tốt khi workload thực sự CPU-bound.
Ví dụ cụ thể: scale theo custom metric từ Managed Prometheus
Một HPA scale theo RPS (requests/giây) từ một metric Prometheus, qua pipeline Managed Prometheus + adapter:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-rps
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 3
maxReplicas: 50
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second # metric do adapter expose
target:
type: AverageValue
averageValue: "100" # mỗi Pod xử lý ~100 RPSỞ đây type: Pods với AverageValue: 100 nghĩa là HPA cố giữ ~100 RPS mỗi Pod. Khi tổng RPS tăng, HPA thêm Pod để giữ trung bình. Đây là scale theo đơn vị công việc thật — vượt xa độ chính xác của scale theo CPU cho một web service. Pipeline: ứng dụng expose /metrics kiểu Prometheus → Managed Service for Prometheus thu thập → Custom Metrics Adapter dịch thành custom.metrics.k8s.io → HPA đọc. Khi ScalingActive=False với reason liên quan custom metric, kiểm tra adapter có chạy và metric có tồn tại trong Managed Prometheus không.
Traffic-based autoscaling (Gateway API)
Từ GKE 1.31+, HPA hỗ trợ scale dựa trên traffic đo tại load balancer qua Gateway API — một dạng external metric đặc biệt. Thay vì suy ra tải từ CPU hay metric trong Pod, HPA scale theo lượng traffic load balancer thực sự gửi tới backend service. Ưu điểm: tín hiệu nằm ở đúng điểm vào của hệ thống, phản ánh tải trước cả khi nó tác động tới CPU Pod. Hữu ích cho service mà việc warm-up tốn thời gian (scale dựa trên traffic đang tới giúp chuẩn bị sớm).
Performance HPA Profile: scale tới hàng nghìn HPA object
Mỗi 15 giây, HPA controller phải tính lại cho mọi HPA object trong cluster. Ở quy mô lớn (hàng nghìn HPA), việc giữ chu kỳ trong 15 giây trở thành thách thức — nếu controller không kịp, recommendation trở nên cũ và phản ứng chậm lại.
GKE giải quyết bằng Performance HPA Profile. Theo tài liệu (GKE HPA):
- Khả dụng từ GKE 1.31+, hỗ trợ tới 1.000 HPA object mà vẫn giữ chu kỳ tính lại trong 15 giây.
- Từ GKE 1.33+, nâng lên tới 5.000 HPA object.
- Profile được bật mặc định trên cluster đủ điều kiện (Autopilot v1.32+, Standard v1.33+).
Bật/tắt thủ công:
# Bật
gcloud container clusters update CLUSTER_NAME \
--location=LOCATION \
--hpa-profile=performance
# Tắt
gcloud container clusters update CLUSTER_NAME \
--location=LOCATION \
--hpa-profile=noneYêu cầu: control plane GKE 1.31+, API autoscaling được bật, và node service account cần role roles/autoscaling.metricsWriter để định tuyến metric. Với GKE 1.31 cụ thể, cần bật thu thập system metric.
Hệ quả kiến trúc: nếu bạn vận hành một platform multi-tenant với hàng trăm namespace, mỗi namespace nhiều HPA, Performance HPA Profile là điều kiện để autoscaling không bị "lag" tập thể. Đừng đợi tới khi recommendation trở nên cũ mới phát hiện ra.
Xung đột HPA + VPA: vì sao không được dùng chung trên CPU/Memory
Đây là một trong những anti-pattern nguy hiểm và phổ biến nhất. Tài liệu GKE nói thẳng: "Đừng dùng Horizontal Pod Autoscaler cùng với Vertical Pod Autoscaler trên CPU hoặc memory" (GKE HPA).
Cơ chế xung đột (vòng phản hồi dương không hội tụ):
- Tải tăng → CPU utilization mỗi Pod tăng.
- HPA thấy CPU% cao → thêm replica.
- VPA thấy CPU usage cao → tăng
requests.cpucủa Pod. - Nhưng
averageUtilizationcủa HPA tính so vớirequests. VPA vừa tăngrequests→ utilization% giảm dù usage tuyệt đối không đổi. - HPA thấy utilization% giảm → bớt replica.
- Tải dồn lại lên ít Pod hơn → utilization tăng lại → quay về bước 2.
Hai loop ra quyết định ngược nhau trên cùng một tín hiệu mà không biết về nhau, tạo dao động. Các cách xử lý đúng:
- Tách metric: HPA scale theo một metric (ví dụ custom metric RPS), VPA right-size CPU/memory. Không trùng tín hiệu.
- Multidimensional Pod Autoscaling (file 4): một controller duy nhất làm HPA theo CPU + VPA theo memory, được thiết kế để không tự đánh nhau.
- VPA
Offdùng làm công cụ recommendation: để VPA tính rarequestsđúng, áp thủ công định kỳ, còn HPA chạy scale-out như bình thường.
Tương tác HPA với rolling update của Deployment
Một edge case tinh tế: HPA điều khiển spec.replicas của Deployment, nhưng trong một rolling update, Deployment controller cũng đang thao tác với replica (tạo ReplicaSet mới, thu nhỏ ReplicaSet cũ). Hai bên cùng động vào cùng đối tượng.
Trong thực tế chúng phối hợp ổn vì HPA điều khiển tổng số replica mong muốn của Deployment, còn Deployment controller phân bổ con số đó giữa ReplicaSet cũ và mới theo maxSurge/maxUnavailable. Nhưng có vài hệ quả cần biết:
- Trong lúc rolling update, Pod mới chưa Ready sẽ được HPA áp dampening (giả định 0% khi scale-up, file 1). Nghĩa là giữa một deploy, metric trung bình có thể bị nhiễu bởi Pod đang khởi động, và HPA có thể tạm thời tính ra số cao hơn. Đây là hành vi đúng nhưng cần nhận biết khi đọc biểu đồ replica trong lúc deploy.
- Đừng đặt
spec.replicascứng trong manifest của Deployment khi đã có HPA. Nếu CI/CD apply lại manifest vớireplicas: 3, nó sẽ ghi đè giá trị HPA đang giữ, gây tụt replica đột ngột rồi HPA phải scale lại. Bỏreplicaskhỏi manifest (hoặc dùng server-side apply bỏ qua trường đó) khi HPA quản lý nó.
Độ trễ và độ tươi của metric: nguồn lỗi ẩn
Một lớp vấn đề HPA mà ít người để ý là độ tươi của metric (metric freshness). HPA tính trên metric mới nhất nó nhận được; nếu pipeline metric có độ trễ (ingestion lag của Cloud Monitoring, scrape interval của Prometheus), HPA đang ra quyết định dựa trên dữ liệu của vài chục giây trước.
Hệ quả cụ thể:
- Phản ứng chậm hơn lý thuyết: chu kỳ HPA là 15s, nhưng nếu metric trễ 30–60s thì độ trễ phản ứng thực tế là tổng của cả hai. Với external metric (Pub/Sub, custom), độ trễ pipeline thường lớn hơn nhiều resource metric.
- Metric "cũ" bị xử lý đặc biệt: HPA có ngưỡng tuổi metric; sample quá cũ bị coi như thiếu metric (áp dampening bảo thủ, file 1). Trong log atomic recommendation, trường tuổi sample (
metric ... age) cho biết metric có đang tươi không — một sample tuổi cao là cờ đỏ chỉ ra pipeline metric chậm, không phải HPA chậm.
Bài học vận hành: khi tối ưu tốc độ phản ứng của HPA, đừng chỉ nhìn behavior — kiểm tra cả độ trễ end-to-end của pipeline metric. Với scale theo external metric cần phản ứng nhanh (queue tăng đột ngột), KEDA (file 8) với pollingInterval riêng đôi khi cho độ trễ thấp và kiểm soát tốt hơn HPA external metric.
Quy trình debug HPA ở production
Gộp lại thành một runbook. Khi HPA "không hành xử như mong đợi":
kubectl describe hpa X→ đọc ba condition:ScalingActive=False? → pipeline metric hỏng. Không phải lỗi HPA. Kiểm tra metrics-server / Custom Metrics Adapter / External Metrics source.ScalingLimited=True, TooManyReplicas? → HPA muốn nhiều hơnmaxReplicas. Vấn đề là trần hoặc capacity, không phải logic HPA. Cân nhắc nângmaxReplicasvà kiểm tra Cluster Autoscaler (file 5).AbleToScale=False? → vấn đề truy cập scale target hoặc đang trong backoff.
- Xác nhận metric thật:
kubectl get hpa Xxem cột target hiện tại vs mục tiêu. Nếu<unknown>→ metric không lấy được. - Đọc log
hpa-controller(file 1): final recommendation cho biết quyết định cuối vànormalization(stabilization đang giữ gì); atomic recommendation cho biết metric nào chi phối. - Kiểm tra
behavior: scale-down "không xảy ra" thường là do stabilization window dài hoặcselectPolicy: Disabled. Scale-up "quá rụt rè" do policy giới hạn tốc độ. - Kiểm tra
requests: nếuaverageUtilizationra số vô lý, gốc rễ làrequestssai chứ không phải HPA. - Loại trừ xung đột VPA: có VPA nào đang chạy
Auto/Recreatetrên cùng workload và cùng CPU/memory không?
Real-world: chuẩn HPA cho một platform multi-tenant
Trên một platform phục vụ hàng chục đội, để mỗi đội tự cấu hình HPA tùy ý dẫn tới hỗn loạn: kẻ đặt target 90% (scale quá trễ, hay outage), người đặt minReplicas: 1 (không HA), người để mặc định behavior cho stateful service (scale-down reset connection). Platform team nên áp một chuẩn HPA qua policy (Gatekeeper/Kyverno) và template:
minReplicas >= 2bắt buộc cho mọi service phục vụ traffic (HA + độ phân giải replica đủ mịn).- Target utilization trong dải an toàn (ví dụ 50–70% cho CPU), có tính tới tolerance 10% và độ trễ scale-up.
behaviortemplate theo lớp workload (web/batch/stateful như bảng trên), không để mặc định cho mọi thứ.- Metric phù hợp: khuyến khích custom metric (RPS/queue) qua Managed Prometheus thay vì mặc định CPU cho service không CPU-bound.
- Bật Performance HPA Profile ở cấp cluster để hàng trăm HPA của các tenant không làm chậm chu kỳ tính lại tập thể.
Cùng với đó, platform team build dashboard tập trung trên log hpa-controller và các condition ScalingActive/ScalingLimited để phát hiện sớm HPA nào đang gãy metric pipeline hay chạm trần — thay vì đợi từng đội tự báo sự cố. Đây là sự chuyển dịch từ "mỗi đội tự lo HPA" sang "platform cung cấp autoscaling như một dịch vụ có chuẩn và quan sát được".
Anti-patterns thường gặp
- Để mặc định
behaviorcho mọi workload. Mặc định tốt cho web service nhưng sai cho batch (scale-down quá chậm tốn tiền) và stateful (scale-down quá nhanh reset connection). Đặtbehaviorcó chủ đích. - Scale theo CPU khi bottleneck ở nơi khác. Đo sai tín hiệu → autoscaling vô dụng. Chọn metric phản ánh đơn vị công việc.
- Dùng HPA + VPA trên cùng CPU/memory. Dao động không hội tụ. Dùng MPA hoặc tách metric.
- Giữ
replicascứng trong manifest khi có HPA. CI/CD ghi đè HPA mỗi lần deploy. - Không bật Performance HPA Profile ở quy mô lớn. Hàng nghìn HPA mà chu kỳ tính lại bị kéo dài → toàn bộ autoscaling phản ứng chậm.
- Đặt
maxReplicasquá thấp "cho an toàn".ScalingLimited=TooManyReplicasâm thầm nghĩa là HPA đang bị bóp nghẹt đúng lúc cần công suất nhất.