HPA tự tăng giảm số bản theo tải. Bài này đo cả hai chiều, và chúng chênh nhau gần chín lần.
Mở rộng: 30 giây
Mục tiêu 50% CPU, min 1 max 8. Tăng tải đột ngột:
| Thời điểm | CPU | Hiện có | Mong muốn | Pod chạy |
|---|---|---|---|---|
| 137 s | 43% | 2 | 2 | 2 |
| 152 s | 107% | 2 | 4 | 4 |
| 167 s | 165% | 4 | 7 | 7 |
| 182 s | 107% | 7 | 8 | 8 |
| 197 s | 47% | 8 | 8 | 8 |
Từ lúc CPU vượt ngưỡng tới lúc đủ 8 bản: 30 giây.
Chú ý HPA không nhảy từng bước. Nó tính thẳng số bản cần thiết:
số bản mong muốn = ceil( số bản hiện tại × CPU hiện tại / CPU mục tiêu )
Ở giây 152: ceil(2 × 107 / 50) = ceil(4,28) = 4. Ở giây 167: ceil(4 × 165 / 50) = 14, nhưng bị chặn ở maxReplicas sau khi qua giới hạn tốc độ mặc định — kết quả là 7.
Công thức này là lý do HPA phản ứng nhanh với đỉnh tải lớn: gấp mười lần tải thì nó nhân mười số bản trong một bước, không tăng từng cái một.
Thu gọn: 258 giây
Bỏ hết tải ở giây 235:
| Thời điểm | CPU | Hiện có | Mong muốn |
|---|---|---|---|
| 250 s | 5% | 8 | 8 |
| 311 s | 0% | 8 | 8 |
| 432 s | 0% | 8 | 8 |
| 478 s | 0% | 8 | 8 |
| 493 s | 0% | 8 | 6 |
258 giây — bốn phút rưỡi giữ 8 bản ở 0% CPU.
Bất đối xứng 8,6 lần so với chiều mở rộng.
Vì sao lệch nhau nhiều đến vậy
Đây là thiết kế có chủ ý, và lập luận đằng sau rất thẳng thắn:
Mở rộng chậm → người dùng chờ, yêu cầu hỏng. Rất tệ. Thu gọn chậm → trả thừa tiền vài phút. Chấp nhận được.
Cơ chế là stabilizationWindowSeconds:
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # mặc định
scaleUp:
stabilizationWindowSeconds: 0 # mặc định
Với scaleDown, HPA nhìn lại 5 phút gần nhất và lấy số bản lớn nhất trong khoảng đó. Một đỉnh tải sẽ giữ số bản cao thêm năm phút sau khi nó qua đi.
Không có cửa sổ này, HPA sẽ dao động: tải giảm → thu gọn → mỗi bản gánh nhiều hơn → CPU tăng → mở rộng → tải chia lại → thu gọn. Vòng lặp đó tốn hơn nhiều so với việc giữ thừa vài bản trong năm phút.
Chỉnh được nếu tải của bạn có hình dạng khác:
behavior:
scaleDown:
stabilizationWindowSeconds: 60
policies: [{type: Percent, value: 50, periodSeconds: 60}]
Nhưng hạ nó xuống dưới 60 giây thường là sai — dao động tốn hơn số tiền tiết kiệm được.
HPA cần requests.cpu
resources: {requests: {cpu: 100m}}
HPA tính phần trăm so với requests, không so với năng lực node. Không khai requests thì nó báo <unknown> và không bao giờ co giãn — không lỗi, không cảnh báo, HPA cứ nằm đó.
Đây là nguyên nhân số một của "HPA của tôi không chạy".
Và nó nối với phần 47: requests quyết định cả việc xếp chỗ lẫn ngưỡng co giãn. Khai requests quá cao thì HPA thấy phần trăm thấp và không bao giờ mở rộng, dù pod đang chậm.
metrics-server là bắt buộc
HPA đọc chỉ số qua metrics.k8s.io, và API đó do metrics-server cung cấp. Không cài thì HPA không có gì để đọc.
Trong kind, metrics-server cần thêm cờ vì chứng chỉ kubelet là tự ký:
kubectl -n kube-system patch deployment metrics-server --type=json \
-p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-",
"value":"--kubelet-insecure-tls"}]'
metrics-server lấy mẫu mỗi 15 giây mặc định, nên đó là độ trễ sàn của mọi phản ứng HPA — cộng vào 30 giây đo được ở trên.
Ba loại chỉ số
Resource — CPU hoặc bộ nhớ. Đơn giản nhất, và là thứ 90% trường hợp dùng.
Chú ý: co giãn theo bộ nhớ hiếm khi đúng. Phần lớn ứng dụng không trả lại bộ nhớ khi tải giảm (JVM, Go), nên HPA theo RAM chỉ mở rộng mà không bao giờ thu gọn.
Pods / Object — chỉ số tuỳ chọn từ ứng dụng: số yêu cầu mỗi giây, độ dài hàng đợi. Cần một adapter chỉ số.
External — chỉ số từ ngoài cụm: độ trễ hàng đợi Kafka (phần 32), độ sâu hàng đợi SQS. Đây thường là chỉ số đúng cho hệ thống xử lý bất đồng bộ — CPU chỉ là chỉ báo gián tiếp.
HPA và replicas trong manifest
Nếu Deployment khai replicas: 3 và HPA đang đặt 8, thì mỗi lần bạn kubectl apply lại manifest, số bản tụt về 3 rồi HPA lại kéo lên 8.
Cách chữa: bỏ hẳn replicas khỏi manifest khi đã có HPA. Kubernetes giữ nguyên giá trị hiện tại.
Với GitOps, thêm chú thích để công cụ bỏ qua trường đó — Argo CD có ignoreDifferences.
Thử ba mươi giây
Kiểm HPA của bạn có đọc được chỉ số không:
kubectl get hpa -A
Cột TARGETS hiện <unknown> nghĩa là một trong hai: metrics-server chưa chạy, hoặc pod đích không khai requests. Cả hai đều khiến HPA tồn tại mà không làm gì — và nó trông giống hệt một HPA đang bình thường vì tải chưa cao.
Phần sau đo RBAC: ai được làm gì, và cách tìm ra quyền thừa.