HPA thêm pod. Cluster Autoscaler thêm node khi không còn chỗ cho pod. Bài này đo hai đầu của vòng lặp đó — và nói rõ phần giữa tôi không đo được.
Tín hiệu kích hoạt: pod Pending vì thiếu tài nguyên
6 pod × 10 CPU trên cụm 3 worker × 16 CPU:
pod đầu tiên vào Pending sau 0,1 giây
event FailedScheduling sau 0,2 giây
kết quả: 3 đang tạo, 3 Pending
0/4 nodes are available: 1 node(s) had untolerated taint(s), 3 Insufficient cpu
Hai phần mười giây để tín hiệu xuất hiện.
Đây chính là thứ Cluster Autoscaler theo dõi: pod Pending vì không node nào đủ chỗ.
Và đó là một điều kiện hẹp hơn nhiều người nghĩ. Pod Pending vì vết (taint) hoặc nodeSelector không khớp thì thêm node không giải quyết được gì, nên Cluster Autoscaler bỏ qua chúng.
Thông báo ở trên nêu rõ cả hai loại: 1 node(s) had untolerated taint(s) và 3 Insufficient cpu. Chỉ vế thứ hai kích hoạt việc thêm node.
Hệ quả thực dụng: nếu pod nằm Pending mãi mà cụm không lớn thêm, hãy đọc dòng FailedScheduling trước. Lý do thường không phải "hết tài nguyên".
Chiều thu gọn: cordon rồi drain
trước: 1 sc-worker 1 sc-worker2 1 sc-worker3
kubectl cordon + drain sc-worker3 -> 14,3 giây
sc-worker3 Ready,SchedulingDisabled
sau: 2 sc-worker 1 sc-worker2
Pod trên node bị drain được xếp lại ở node khác, và cả quá trình mất 14,3 giây.
Đây đúng là hai bước Cluster Autoscaler thực hiện trước khi trả node về cho nhà cung cấp:
cordon— đánh dấu node không nhận pod mới. Tức thì.drain— đuổi pod hiện có, tôn trọngPodDisruptionBudget.
PodDisruptionBudget là chỗ việc thu gọn hay bị kẹt. Một PDB minAvailable: 3 trên deployment 3 bản khiến drain không bao giờ xong — Cluster Autoscaler sẽ thử mãi và node không bao giờ được trả về.
Đây là nguyên nhân phổ biến nhất của "cụm không bao giờ thu gọn, tôi trả tiền cho node rỗng".
Phần tôi không đo được
Việc thật sự tạo ra một máy mới cần nhà cung cấp đám mây. kind chạy node bằng container Docker và không có API để Cluster Autoscaler gọi, nên tôi không dựng được phép đo đầu-cuối.
Hai con số quan trọng nhất — thời gian nhà cung cấp cấp máy, và thời gian node mới gia nhập cụm — tôi không có số đo riêng, và không mượn số của người khác rồi gọi đó là phép đo.
Cái đo được và đáng nhớ: hai đầu của vòng lặp đều rất nhanh — 0,2 giây để sinh tín hiệu, 14,3 giây để dọn một node. Toàn bộ thời gian chờ nằm ở khúc giữa, tức phần thuộc về nhà cung cấp hạ tầng.
Nghĩa là khi tối ưu thời gian phản ứng của cụm, đừng nhìn vào Kubernetes — nhìn vào thời gian khởi động máy và thời gian kéo ảnh (phần 48 đo được 6–16 giây cho ảnh).
Ba cơ chế co giãn, ba tầng khác nhau
| Đối tượng | Điều kiện | |
|---|---|---|
| HPA (phần 66) | số pod | chỉ số vượt ngưỡng |
| VPA (phần 67) | requests của pod |
mức dùng thật khác requests |
| Cluster Autoscaler | số node | pod Pending vì hết chỗ |
Chúng hoạt động nối tiếp: HPA thêm pod → không đủ chỗ → pod Pending → Cluster Autoscaler thêm node → pod được xếp.
Nên thời gian phản ứng đầu-cuối của một đỉnh tải là tổng của cả chuỗi: 30 giây HPA (phần 66) cộng thời gian cấp máy cộng thời gian kéo ảnh.
Với đỉnh tải nhanh, chuỗi này quá chậm. Cách xử lý thông dụng là pod giữ chỗ: chạy vài pod ưu tiên thấp không làm gì, chiếm sẵn tài nguyên. Khi tải thật tới, pod ưu tiên cao đuổi chúng đi (phần 47 đã đo cơ chế đuổi này) và chạy ngay, trong khi Cluster Autoscaler thong thả cấp node mới cho pod giữ chỗ.
Bốn thứ chặn việc thu gọn
Cluster Autoscaler không xoá node nếu trên đó còn:
- Pod không có bộ điều khiển — pod trần tạo bằng
kubectl run, không thuộc Deployment hay Job nào. - Pod dùng lưu trữ cục bộ (
emptyDir) mà không khai cho phép mất. - Pod bị
PodDisruptionBudgetchặn. - Pod trong
kube-systemkhông có PDB.
Ba cái đầu đều nằm trong tay bạn, và cả ba đều là lý do thật khiến hoá đơn không giảm khi tải giảm.
metadata:
annotations:
cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
Chú thích này nói với Cluster Autoscaler rằng pod chuyển đi được, kể cả khi nó dùng emptyDir.
Chiều ngược lại — "false" — ghim một pod tại chỗ, và một pod như vậy giữ nguyên cả node.
Thử ba mươi giây
Tìm thứ đang chặn việc thu gọn trên cụm của bạn:
# pod không thuộc bộ điều khiển nào
kubectl get pods -A -o json | python3 -c '
import sys,json
for p in json.load(sys.stdin)["items"]:
if not p["metadata"].get("ownerReferences"):
print(p["metadata"]["namespace"], p["metadata"]["name"])
'
# PodDisruptionBudget không cho phép đuổi ai
kubectl get pdb -A -o custom-columns=\
NS:.metadata.namespace,TEN:.metadata.name,\
CHO_PHEP_DUOI:.status.disruptionsAllowed
Cột CHO_PHEP_DUOI bằng 0 nghĩa là PDB đó đang chặn mọi việc đuổi — và node nào có pod của nó sẽ không bao giờ được trả về.
Phần sau đo RBAC: ai được làm gì, và cách tìm ra quyền thừa.