HPA thêm bản. VPA sửa requests của từng bản. Bài này đo VPA trên cụm thật và tìm ra nó làm hai việc khác nhau mà nhiều người tưởng là một.

Khuyến nghị đầu tiên, hai cơ chế của Auto mode, và vì sao VPA trông như không làm gì

Khuyến nghị đầu tiên sau khoảng một phút

Ứng dụng khai requests: cpu=500m, memory=512Mi. Dùng thật: 338m CPU, 83Mi bộ nhớ — khai thừa rõ ràng.

VPA sau ~60 giây:

lowerBound:  cpu=25m       mem=262144k
target:      cpu=410m      mem=262144k
upperBound:  cpu=8856410m  mem=2372108436351

target là con số dùng được: 410m — cao hơn mức dùng thật 338m một chút, đúng như mong đợi vì VPA cộng biên an toàn.

upperBound thì 8.856 nhân và 2,2 terabyte. Vô nghĩa.

Đó không phải lỗi: VPA chưa đủ dữ liệu lịch sử nên khoảng tin cậy rộng gần như vô hạn. Phải để nó chạy vài ngày trước khi con số có ý nghĩa — và phần dưới sẽ cho thấy vì sao điều đó ảnh hưởng tới hành vi chứ không chỉ tới độ chính xác.

Bộ nhớ khuyến nghị là 262144k = 250Mi, đúng bằng mức sàn mặc định của VPA, dù ứng dụng chỉ dùng 83Mi. VPA không khuyến nghị thấp hơn 250Mi trừ khi bạn khai minAllowed.

Auto mode: hai cơ chế khác nhau

Đây là phần bất ngờ nhất.

1) Bộ admission ghi đè requests lúc tạo pod

Deployment khai:  {"cpu":"50m","memory":"64Mi"}
Pod thực tế có:   {"cpu":"410m","memory":"262144k"}
VPA khuyến nghị:  {"cpu":"410m","memory":"262144k"}

Tôi đặt requests: cpu=50m trong Deployment. Pod chạy ra với 410m.

Bộ admission của VPA chặn ở giữa (phần 46 — admission nằm ngay trên đường ghi) và thay giá trị. Manifest nói một đằng, pod chạy một nẻo — và kubectl get deployment vẫn hiện con số cũ.

Với GitOps, điều này gây bối rối thật: kho Git là nguồn sự thật cho Deployment, nhưng không phải cho requests mà pod thực sự nhận.

2) Bộ updater giết pod cũ — nhưng chỉ khi cần

Pod cũ có requests: cpu=500m, khoảng tin cậy [187m .. 196122m].

500m nằm TRONG khoảng  ->  KHÔNG bị giết
sau 110 giây vẫn nguyên, RESTARTS=0

VPA không giết pod chỉ vì target khác requests hiện tại. Nó chỉ giết khi requests nằm ngoài khoảng tin cậy.

Vì sao VPA trông như "không làm gì"

Ghép hai điều trên lại:

  • Khoảng tin cậy ban đầu rất rộng (upperBound 8.856 nhân), nên gần như mọi requests đều nằm trong.
  • Chỉ pod mới nhận giá trị khuyến nghị, qua bộ admission.

Nên bật VPA Auto rồi ngồi chờ nó sửa pod đang chạy là chờ vô ích. Muốn thấy hiệu quả ngay thì phải:

kubectl rollout restart deployment/<ten>

Hoặc chờ tới khi VPA thu thập đủ dữ liệu và khoảng tin cậy hẹp lại — thường là vài ngày.

Đây là lý do khuyến nghị phổ biến là bắt đầu với updateMode: "Off": để VPA quan sát và đưa khuyến nghị, con người đọc rồi tự sửa manifest. Bạn được giá trị chính của VPA (biết nên khai bao nhiêu) mà không có phần khó đoán.

Bốn chế độ

Off        chỉ khuyến nghị, không đụng gì          <- nên bắt đầu ở đây
Initial    chỉ đặt requests lúc pod được TẠO
Recreate   giết pod để áp giá trị mới
Auto       hiện tại giống Recreate

Auto được thiết kế để một ngày nào đó dùng cơ chế sửa tại chỗ, nhưng hiện tại nó vẫn giết pod.

Và đó là điểm yếu lớn nhất của VPA: nó phải giết pod để đổi requests. Với ứng dụng khởi động chậm hoặc có trạng thái, việc bị giết định kỳ là cái giá thật.

Kubernetes 1.27 có tính năng thử nghiệm InPlacePodVerticalScaling cho phép đổi requests mà không khởi động lại. Khi nó ổn định, VPA sẽ hữu dụng hơn nhiều.

VPA và HPA không dùng chung được

VPA và HPA theo CPU cùng lúc trên một Deployment sẽ đá nhau:

  • Tải tăng → HPA thêm bản → CPU mỗi bản giảm → VPA hạ requests → tỉ lệ CPU/requests tăng → HPA thêm bản nữa.

Kết hợp được khi HPA dùng chỉ số khác: số yêu cầu mỗi giây, độ dài hàng đợi (phần 66). Khi ấy hai bộ điều khiển nhìn hai thứ khác nhau và không xung đột.

Quy tắc thực dụng:

  • Tải thay đổi theo thời gian → HPA. Thêm bản là cách đúng.
  • Không biết nên khai bao nhiêu → VPA ở chế độ Off, đọc khuyến nghị, sửa tay.
  • Cả hai → HPA theo chỉ số ứng dụng, VPA Off để hiệu chỉnh requests.

Ba tham số nên khai

spec:
  resourcePolicy:
    containerPolicies:
      - containerName: "*"
        minAllowed: {cpu: 50m, memory: 64Mi}
        maxAllowed: {cpu: 2, memory: 2Gi}
        controlledResources: [cpu, memory]

maxAllowed là quan trọng nhất: không có nó, một đỉnh tải bất thường có thể khiến VPA khuyến nghị con số lớn tới mức không node nào xếp nổi pod — và pod nằm Pending mãi (phần 47).

minAllowed chặn chiều ngược lại: VPA hạ requests quá thấp khiến pod thành gần BestEffort và bị đuổi trước (phần 55).

Thử ba mươi giây

Xem VPA khuyến nghị gì so với những gì bạn đang khai:

kubectl get vpa -A -o custom-columns=\
NS:.metadata.namespace,TEN:.metadata.name,\
CHE_DO:.spec.updatePolicy.updateMode,\
CPU:.status.recommendation.containerRecommendations[0].target.cpu,\
RAM:.status.recommendation.containerRecommendations[0].target.memory

So với requests trong manifest. Chênh lệch lớn theo chiều khai thừa là tiền đang lãng phí; chênh lệch theo chiều khai thiếu là pod sắp bị đuổi khi node cạn.

Phần sau đo Cluster Autoscaler — và nói rõ phần nào tôi không dựng được để đo.