Skip to content

VerticalPodAutoscaler — Kiến Trúc, Recommender & Update Modes

Vì sao right-sizing là bài toán khó hơn nó trông

HorizontalPodAutoscaler trả lời câu hỏi "cần bao nhiêu Pod?". VerticalPodAutoscaler trả lời câu hỏi khó hơn: "mỗi Pod cần bao nhiêu tài nguyên?". Câu hỏi này khó vì câu trả lời sai theo cả hai hướng đều tốn kém:

  • requests quá cao: Pod chiếm chỗ nó không dùng. Scheduler bin-packing kém, node utilization thấp, Cluster Autoscaler không bao giờ đạt ngưỡng scale-down (file 5), hóa đơn phình ra. Đây là dạng lãng phí phổ biến nhất trên GKE và khó phát hiện nhất vì "hệ thống vẫn chạy ổn".
  • requests quá thấp: container bị CPU throttling (CFS quota, xem Chương 8) hoặc bị OOMKilled khi vượt memory. Latency tăng, Pod restart, và HPA — vốn tính utilization so với requests — ra quyết định sai.

Con người đặt requests rất tệ. Đa số copy từ một workload khác, nhân đôi "cho chắc", rồi không bao giờ xem lại. VPA tự động hóa việc này bằng cách quan sát usage thật theo thời gian và đề xuất requests đúng. Nhưng "đúng" ở đây dựa trên một thuật toán cụ thể với những giả định cụ thể — và hiểu thuật toán đó là điều kiện để tin (hoặc không tin) recommendation của nó.

Ba thành phần của VPA

VerticalPodAutoscaler không phải một process duy nhất mà là ba thành phần phối hợp (GKE VPA). Trên GKE, điểm quan trọng là cả ba chạy như control plane process do Google quản lý, không phải Deployment trên node worker của bạn — khác với khi bạn tự cài VPA trên cluster tự dựng. Điều này nghĩa là VPA trên GKE không tiêu tốn tài nguyên node của bạn và được Google vận hành/nâng cấp.

1. Recommender

Bộ não của VPA. Nó quan sát mức tiêu thụ CPU và memory lịch sử của các container, cộng với các sự kiện (đặc biệt là OOM), rồi tính ra recommendation: target, lowerBound, upperBound cho mỗi tài nguyên. Recommender luôn chạy kể cả ở mode Off — nghĩa là bạn luôn có thể xem recommendation mà không cần để VPA tự áp dụng.

2. Updater

Thành phần quyết định hành động. Nó so sánh requests hiện tại của Pod với recommendation và, tùy update mode, quyết định có cần thay đổi không. Trong mode Recreate/Auto, nó evict Pod để Pod được tạo lại với requests mới. Trong mode InPlaceOrRecreate (preview), nó thử resize tại chỗ trước.

3. Admission Controller

Một mutating admission webhook. Khi một Pod được tạo (kể cả Pod do Updater evict rồi controller tạo lại), Admission Controller ghi đè requests trong Pod spec bằng giá trị recommendation trước khi Pod được lập lịch. Đây là lý do recommendation chỉ thực sự áp dụng được khi Pod được tạo mới (trừ trường hợp IPPR): giá trị mới được tiêm vào lúc admission.

Luồng end-to-end ở mode Auto: Recommender tính target mới → Updater thấy lệch quá ngưỡng → evict Pod → controller (Deployment/StatefulSet) tạo Pod thay thế → Admission Controller chặn request tạo Pod và tiêm requests mới → scheduler lập lịch Pod với requests đúng.

Thuật toán Recommender: histogram phân rã theo thời gian

Đây là phần quyết định bạn có nên tin VPA hay không. Recommender không đơn giản lấy max hay trung bình. Nó xây một histogram có trọng số phân rã theo thời gian cho mỗi tài nguyên, rồi lấy percentile.

Các đặc điểm chính của thuật toán (theo thiết kế của Kubernetes VPA recommender):

  • Cửa sổ lịch sử mặc định 8 ngày: recommender giữ sample trong khoảng này. Tài liệu GKE nhấn mạnh workload nên chạy ít nhất 24 giờ để có recommendation đáng tin (GKE VPA).
  • Phân rã theo nửa-chu-kỳ (decay half-life) 24 giờ: sample càng cũ càng nhẹ. Một sample của hôm qua có trọng số bằng một nửa sample của hôm nay. Điều này khiến VPA vừa nhớ peak lịch sử vừa ưu tiên xu hướng gần đây — nó thích nghi nhưng không quên đột ngột.
  • Histogram bucket theo cấp số nhân: bucket CPU trải từ ~1m tới ~1000 core, bucket memory từ ~1Mi tới ~1Ti, kích thước bucket tăng theo tỷ lệ ~1.05. Cách này cho độ phân giải cao ở giá trị nhỏ và vẫn bao phủ dải rộng.
  • CPU và memory xử lý khác nhau: CPU lấy theo một percentile cao (xấp xỉ p90) của các sample — vì CPU là compressible, vượt một chút chỉ gây throttling chứ không giết Pod. Memory thiên về peak trong cửa sổ — vì memory là incompressible, đánh giá thấp peak nghĩa là OOMKill.

Tín hiệu OOM: phản ứng tức thời

Histogram là cơ chế chậm, dựa trên lịch sử. Nhưng OOMKill là sự kiện cấp bách không thể đợi. Theo tài liệu GKE, nếu một Pod bị OOMKilled, VPA lập tức quan sát sự kiện và tăng recommendation memory thêm khoảng 20% (hoặc 100 MB, lấy giá trị lớn hơn) (GKE VPA).

Đây là cơ chế feedback quan trọng: VPA học từ thất bại memory một cách chủ động, không đợi histogram tích lũy. Hệ quả: một workload mới với memory request thấp sẽ trải qua vài lần OOM rồi VPA dần đẩy recommendation lên đủ — đây là hành vi đúng nhưng cần nhận biết khi quan sát một workload mới onboard.

Vì sao thuật toán này quan trọng với quyết định production

Hiểu thuật toán cho phép suy luận về recommendation:

  • VPA "nhớ" peak khá lâu (half-life 24h, cửa sổ 8 ngày). Một spike lớn hôm nay sẽ ảnh hưởng recommendation suốt nhiều ngày. Nếu workload có một event bất thường (batch job lớn, sự cố), recommendation sẽ bị kéo cao một thời gian — đây là lý do VPA scale-down chậm khi traffic giảm (phân tích kỹ ở phần giới hạn).
  • Cần đủ dữ liệu. Dưới 24 giờ, recommendation có độ tin cậy thấp. Đừng để VPA Auto enforce một workload mới toanh — chạy Off trước để thu thập.
  • Recommendation memory thiên về peak, nên nó bảo thủ (an toàn chống OOM) nhưng có thể "đắt" với workload có peak hiếm và cao.

Update modes: bốn (cộng một preview) cách áp dụng recommendation

Theo tài liệu GKE (GKE VPA), updatePolicy.updateMode có các giá trị:

ModeHành viUse case
OffChỉ tạo recommendation, không áp dụngQuan sát/right-size thủ công; an toàn nhất
InitialÁp dụng chỉ khi Pod khởi động lần đầu; không đổi Pod đang chạyWorkload nhạy với restart nhưng muốn request đúng từ đầu
RecreateEvict Pod để áp request mới khi recommendation lệch đủ xaCần enforce liên tục, chấp nhận disruption
AutoHiện tương đương Recreate (sẽ chuyển dần sang in-place khi GA)Mặc định "tự động hóa hoàn toàn"
InPlaceOrRecreate (Preview)Thử resize tại chỗ không giết Pod; fallback Recreate nếu không đượcGiảm disruption của right-sizing

Off — chế độ bị đánh giá thấp nhất

Trong thực tế production, Off thường là chế độ đúng nhất cho phần lớn workload. Lý do: nó tách việc tính recommendation (an toàn, không disruption) khỏi việc áp dụng (có disruption, có rủi ro). Bạn để VPA Off chạy, đọc target qua kubectl describe vpa, rồi đưa giá trị đó vào manifest/GitOps một cách có kiểm soát, review được, rollback được. Bạn được lợi ích right-sizing mà không giao quyền giết Pod cho một controller tự động.

Recreate/Auto — sức mạnh và cái giá

Recreate enforce recommendation bằng cách evict Pod. Đây là điểm đau lớn nhất của VPA truyền thống: mỗi lần điều chỉnh request là một lần giết và tạo lại Pod. Với service phục vụ traffic, điều này nghĩa là disruption định kỳ. VPA cố giảm thiểu bằng cách chỉ evict khi lệch đủ xa và tôn trọng PodDisruptionBudget, nhưng bản chất "phải tạo lại Pod để đổi request" là giới hạn cố hữu — chính giới hạn mà In-Place Pod Resize sinh ra để giải quyết.

In-Place Pod Resize (IPPR) và mode InPlaceOrRecreate

Đây là bước tiến lớn nhất của VPA gần đây. Theo tài liệu Kubernetes, từ v1.35 tính năng InPlacePodVerticalScaling đã stable và bật mặc định (In-place resize). Nó cho phép thay đổi requests/limits của container mà không cần tạo lại Pod.

Cơ chế

Container khai báo resizePolicy cho từng tài nguyên:

yaml
resizePolicy:
- resourceName: cpu
  restartPolicy: NotRequired      # đổi CPU không cần restart container
- resourceName: memory
  restartPolicy: RestartContainer # đổi memory thì restart container
  • restartPolicy: NotRequired (mặc định): áp thay đổi mà không restart container.
  • restartPolicy: RestartContainer: restart container để áp (thường cần cho ứng dụng đọc giới hạn memory lúc khởi động, ví dụ JVM heap).

Trạng thái resize phản ánh trong status.conditions của Pod:

  • type: PodResizeInProgress: kubelet đã chấp nhận và đang cấp phát.
  • type: PodResizePending với reason: Infeasible (bất khả thi trên node hiện tại) hoặc reason: Deferred (có thể khả thi sau, kubelet sẽ thử lại).

Mode InPlaceOrRecreate của VPA

Trên GKE (từ Kubernetes 1.34.0-gke.2201000+), VPA mode InPlaceOrRecreate (preview) thử resize tại chỗ trước, và fallback về Recreate nếu (GKE VPA):

  • Node không đủ công suất cho request mới.
  • QoS class sẽ thay đổi (không cho phép).
  • resizePolicy đặt RestartContainer.
  • Hết thời gian chờ cho update đang pending.

Giới hạn quan trọng của IPPR

  • Chỉ resize được CPU và memory.
  • QoS class không được đổi: Pod sinh ra là Guaranteed/Burstable/BestEffort thì giữ nguyên lớp đó. Guaranteed phải giữ requests == limits; Burstable không được để requests bằng limits đồng thời.
  • Giảm memory với NotRequired: kubelet cố gắng tránh OOM theo best-effort nhưng không đảm bảo. Đây là lý do nhiều ứng dụng đặt RestartContainer cho memory.
  • Init container không-restartable và ephemeral container không resize được; sidecar thì được.
  • Pod dùng static CPU/Memory manager policy hoặc swap có hạn chế riêng; Windows Pod không hỗ trợ.

Tác động vận hành: IPPR biến VPA từ "công cụ gây disruption định kỳ" thành "công cụ right-sizing gần như trong suốt" cho CPU. Với memory, vẫn cần cân nhắc RestartContainer cho ứng dụng đọc giới hạn lúc khởi động. Đây là thay đổi đủ lớn để xem lại quyết định "không dùng VPA vì sợ disruption".

Cấu hình VPA: resourcePolicy và đọc recommendation

Một VPA spec đầy đủ với các dây cương quan trọng:

yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Off"            # bắt đầu bằng Off để quan sát
  resourcePolicy:
    containerPolicies:
    - containerName: '*'
      controlledResources: ["cpu", "memory"]
      controlledValues: RequestsAndLimits   # hoặc RequestsOnly
      minAllowed:
        cpu: 50m
        memory: 64Mi
      maxAllowed:
        cpu: "2"
        memory: 2Gi

Hai trường quyết định hành vi:

  • controlledValues: RequestsOnly chỉ điều chỉnh requests (giữ nguyên limits bạn đặt); RequestsAndLimits (mặc định) điều chỉnh cả hai, giữ tỷ lệ requests:limits ban đầu. Lưu ý quan trọng: nếu bạn dựa vào limits cố định để chống một container chạy loạn, RequestsAndLimits sẽ nâng cả limit theo — cân nhắc RequestsOnly khi muốn giữ trần cứng.
  • minAllowed/maxAllowed: cận dưới/trên cho recommendation. maxAllowed là dây cương chống recommendation runaway (một spike hiếm kéo recommendation lên cao); minAllowed tránh VPA đề xuất request quá nhỏ gây throttle.

Đọc recommendation

kubectl describe vpa my-app-vpa hiển thị bốn giá trị cho mỗi tài nguyên trong status.recommendation:

  • target: giá trị VPA khuyến nghị đặt làm requests. Đây là con số bạn dùng khi áp thủ công (mode Off).
  • lowerBound: cận dưới — dưới mức này VPA coi là under-provisioned (sẽ muốn tăng).
  • upperBound: cận trên — trên mức này coi là over-provisioned (sẽ muốn giảm).
  • uncappedTarget: target trước khi áp minAllowed/maxAllowed. So sánh target với uncappedTarget cho biết recommendation có đang bị maxAllowed cắt không — nếu uncappedTarget cao hơn target, dây cương đang chặn, cân nhắc nới (hoặc giữ nếu cố ý).

Khoảng [lowerBound, upperBound] rộng là dấu hiệu workload có usage biến động lớn hoặc chưa đủ dữ liệu; khoảng hẹp nghĩa là recommendation tự tin. Đây là tín hiệu trực quan để biết có nên tin con số target chưa.

Giới hạn của VPA cần nắm rõ

  1. Scale-down chậm khi traffic giảm. Do histogram phân rã half-life 24h và cửa sổ 8 ngày, VPA nhớ peak lâu. Khi tải giảm thật, recommendation memory (thiên về peak) tụt rất chậm. VPA tốt ở việc chống under-provisioning hơn là cắt over-provisioning nhanh.
  2. Không dùng được cho standalone Pod. VPA cần một workload controller (Deployment, StatefulSet, DaemonSet...) vì cơ chế enforce là evict-và-tạo-lại; standalone Pod không có gì tạo lại nó (GKE VPA).
  3. Không kết hợp HPA và VPA trên cùng CPU/memory. Dùng Multidimensional Pod Autoscaling (file 4).
  4. Cẩn trọng với workload JVM. Tài liệu GKE lưu ý VPA chưa sẵn sàng cho workload dựa trên JVM — vì JVM quản lý heap riêng, usage memory của process không phản ánh trực tiếp nhu cầu thật theo cách VPA giả định.
  5. minReplicas cho eviction: VPA Updater chỉ evict khi đủ replica để không vi phạm tính sẵn sàng (mặc định liên quan tới phiên bản GKE — 1 từ 1.35.2+, 2 ở các phiên bản trước). Một Deployment 1 replica có thể không được VPA Recreate evict để tránh downtime.
  6. Bật VPA: trên Autopilot, VPA bật mặc định; trên Standard cần bật ở cấp cluster (gcloud container clusters update CLUSTER --enable-vertical-pod-autoscaling).

Real-world: chiến lược right-sizing theo giai đoạn

Một pattern production đã được kiểm chứng cho việc đưa VPA vào một fleet lớn mà không gây disruption:

  1. Giai đoạn quan sát (tuần 1–2): bật VPA Off cho tất cả workload. Thu thập recommendation, so sánh với requests hiện tại. Đây là lúc bạn phát hiện mức over-provisioning thật của fleet — thường là 2–4x.
  2. Giai đoạn áp thủ công có kiểm soát: đưa target recommendation vào GitOps theo từng đợt, review qua PR, deploy qua pipeline bình thường (tận dụng rolling update). Không giao quyền evict cho controller.
  3. Giai đoạn tự động cho workload phù hợp: với workload chịu được disruption hoặc đã có IPPR (InPlaceOrRecreate), chuyển sang Auto để duy trì right-sizing liên tục.
  4. Loại trừ: giữ Off (hoặc không dùng VPA) cho workload JVM, workload 1 replica, và workload đã dùng HPA trên CPU/memory.

VPA và init container, sidecar, multi-container Pod

Một Pod thực tế hiếm khi chỉ có một container. VPA tính recommendation riêng cho từng container (theo containerName trong resourcePolicy), điều này gây vài tình huống cần lưu ý:

  • Sidecar (service mesh, logging agent): thường có usage ổn định và nhỏ. Nếu không loại trừ, VPA vẫn sinh recommendation cho chúng — đôi khi hợp lý (right-size sidecar cũng tiết kiệm), đôi khi gây nhiễu. Dùng containerPolicies với mode: "Off" cho container cụ thể để loại trừ container bạn không muốn VPA động vào.
  • Init container: trước In-Place Pod Resize, init container có cách xử lý riêng vì chúng chạy trước container chính. Với IPPR, init container không-restartable không resize được (file trên).
  • Container "ăn" tài nguyên không đều: nếu một container trong Pod thỉnh thoảng spike rất cao, recommendation memory của nó (thiên về peak) sẽ kéo requests cả container lên — trong khi các container khác trong Pod vẫn nhỏ. VPA xử lý đúng từng container, nhưng tổng requests Pod là tổng các container, ảnh hưởng tới scheduling và bin-packing.

Nguyên tắc: với Pod nhiều container, dùng containerPolicies để chỉ định rõ container nào VPA kiểm soát và container nào loại trừ, thay vì để mặc định '*' áp cho tất cả. Đặc biệt loại trừ các sidecar do platform tiêm tự động nếu chúng không nên bị VPA điều chỉnh.

VPA như recommendation engine cho capacity planning

Ngoài việc enforce request, VPA Off là một công cụ capacity planning mạnh mà nhiều đội bỏ qua. Vì Recommender luôn chạy kể cả ở Off, bạn có thể bật VPA Off cho toàn bộ fleet chỉ để thu thập recommendation, rồi xuất target của tất cả workload ra một báo cáo. So sánh tổng target với tổng requests hiện tại cho biết chính xác mức over-provisioning của cả cluster — thường là con số gây sốc (2–4x). Đây là dữ liệu cứng để biện minh cho một đợt right-sizing, thay vì tranh luận cảm tính về "có đang lãng phí không". Recommendation cũng phân biệt được workload nào under-provisioned (đang ở rìa OOM/throttle) — những quả bom hẹn giờ cần xử lý trước khi chúng nổ ở production.

Anti-patterns thường gặp

  • Bật Auto cho workload mới chưa đủ 24h dữ liệu. Recommendation độ tin cậy thấp + evict liên tục = bất ổn. Bắt đầu bằng Off.
  • Dùng VPA Auto trên service 1 replica. Mỗi lần điều chỉnh là một lần downtime. Cần đủ replica hoặc dùng IPPR.
  • Kỳ vọng VPA cắt over-provisioning nhanh. Nó nhớ peak lâu; scale-down chậm là bản chất thuật toán.
  • Dùng VPA Auto + HPA trên cùng CPU. Dao động không hội tụ (xem file 2, 4).
  • Bỏ qua resizePolicy cho memory của JVM/ứng dụng đọc limit lúc start. Resize memory tại chỗ mà không restart có thể không có hiệu lực với ứng dụng đã đọc heap size lúc khởi động.
  • Coi recommendation VPA là chân lý tuyệt đối. Nó dựa trên lịch sử quan sát được; một workload có pattern tải sẽ đổi trong tương lai (ví dụ chuẩn bị cho một sự kiện lớn) cần override thủ công.

References