Kubernetes VPA 垂直自動擴縮容實作紀錄
在 Kubernetes 裡部署服務時,除了要思考 Pod 數量是否足夠,也要思考每個 Pod 本身配置的 CPU 與記憶體是否合理。若 resources.requests 設得太低,服務可能容易被資源限制影響;若設定得太高,又會讓叢集排程時預留過多資源,造成浪費。
這篇文章會整理 Kubernetes VPA(Vertical Pod Autoscaler)的基本概念,並透過一份測試用 Deployment 與 VPA 配置,觀察 VPA 如何根據 Pod 的使用情況提供資源建議。文末也附上 Demo 影片,方便對照完整操作流程。
什麼是 VPA?
VPA 是 Kubernetes 用來調整 Pod CPU 與記憶體資源配置的元件。和 HPA(Horizontal Pod Autoscaler)透過增加或減少 Pod 數量來處理負載不同,VPA 關注的是單一 Pod 需要多少 resources.requests,也就是每個 Pod 應該預留多少 CPU 與記憶體。
VPA 會觀察目標工作負載的資源使用情況,並計算出建議值。依照 updatePolicy.updateMode 的設定不同,VPA 可以只提供建議,也可以在需要時重新建立 Pod,讓新的 Pod 使用更新後的資源配置。
常見的 VPA 運作角色如下:
vpa-recommender觀察 Pod 的資源使用量,計算 CPU 與記憶體建議值vpa-updater判斷現有 Pod 是否需要更新資源配置,必要時驅逐 Podvpa-admission-controller在 Pod 建立時注入 VPA 建議的 requests 設定
也就是說,VPA 的重點不是增加 Pod 數量,而是讓 Pod 的資源請求值更接近實際需求。
VPA 與 HPA 的差異
HPA 與 VPA 都是自動擴縮容機制,但兩者調整的方向不同。
| 項目 | HPA | VPA |
|---|---|---|
| 調整目標 | Pod 數量 | Pod 的 CPU 與記憶體 requests |
| 常見用途 | 流量變大時增加副本數 | 服務資源設定不準時提供建議或自動修正 |
| 對應資源 | Deployment 的 replicas | Container resources requests |
| 可能影響 | 建立或移除 Pod | 可能重新建立 Pod |
實務上要特別注意,VPA 不建議與 HPA 同時針對 CPU 或記憶體使用率調整同一個工作負載。原因是 HPA 會依據資源使用率計算副本數,而 VPA 會改變 requests,兩者若同時作用在相同指標上,可能讓擴縮容判斷互相影響。
前置條件
使用 VPA 前,需要先準備一個可正常操作的 Kubernetes 叢集,並確認 kubectl 已經連到目標叢集。VPA 需要觀察 Pod 的資源使用量,因此叢集內也應該先安裝 metrics-server,讓資源指標可以被正常取得。
可以先用以下指令確認 metrics-server 是否能提供資料:
1 | kubectl top nodes |
1 | kubectl top pods -A |
如果可以看到 Node 與 Pod 的 CPU、記憶體使用量,就代表後續觀察 VPA 建議值時比較不容易遇到指標缺失問題。
安裝 VPA
本次實作使用 Kubernetes 官方 autoscaler 專案內提供的安裝腳本。先將專案 clone 到本機:
1 | git clone https://github.com/kubernetes/autoscaler.git |
接著進入 VPA 目錄,執行安裝腳本:
1 | cd autoscaler/vertical-pod-autoscaler/ |
安裝過程會建立 VPA 需要的 CRD、RBAC、ServiceAccount、Deployment 與 Admission Controller 憑證。完成後可以看到 vpa-recommender、vpa-updater、vpa-admission-controller 等元件被建立起來。
確認 VPA 元件是否正常執行:
1 | kubectl get pods -n kube-system | grep vpa |
如果三個 VPA Pod 都是 Running,就代表 VPA 基本元件已經啟動完成。
VPA 基本使用方式
安裝 VPA 元件後,實際使用時通常會先準備好要被觀察的工作負載,例如 Deployment,接著建立一個 VerticalPodAutoscaler 資源指向該 Deployment。VPA 會根據目標 Pod 的資源使用情況計算建議值,並依照 updatePolicy 決定是否只提供建議,或在 Pod 建立、重建時套用新的 requests。
以下是一份基本的 VPA 設定範例:
1 | apiVersion: autoscaling.k8s.io/v1 |
這份 YAML 的意思是:VPA 會觀察 dockertest namespace 裡的 dockertest-deployment,根據 Pod 的資源使用情況計算 CPU 與記憶體建議值,並把建議值限制在 minAllowed 與 maxAllowed 範圍內。
建立 VPA 時可以使用:
1 | kubectl apply -f vpa.yml |
建立完成後,可以透過以下指令查看 VPA 狀態與建議值:
1 | kubectl describe vpa dockertest-vpa -n dockertest |
在 Recommendation 區塊中,通常會看到 Lower Bound、Target、Uncapped Target 與 Upper Bound。其中 Target 是 VPA 建議套用的 requests,Lower Bound 與 Upper Bound 則是建議值的上下界。
VPA 設定重點
VPA 的核心設定在 VerticalPodAutoscaler 這個資源中。以下整理幾個實作時最常調整的欄位。
官方文件可參考 Kubernetes Vertical Pod Autoscaling。
targetRef
targetRef 用來指定 VPA 要觀察與調整的工作負載:
1 | targetRef: |
這段設定代表 VPA 會針對 dockertest-deployment 這個 Deployment 底下的 Pod 計算資源建議值。
updatePolicy
updatePolicy.updateMode 決定 VPA 要如何套用建議值:
1 | updatePolicy: |
常見模式如下:
| 模式 | 說明 |
|---|---|
Off |
只產生建議值,不會修改或重新建立 Pod |
Initial |
只在 Pod 建立時套用建議值,既有 Pod 不會被更新 |
Recreate |
當 VPA 判斷需要更新時,會驅逐 Pod,讓工作負載重新建立 Pod |
InPlaceOrRecreate |
優先嘗試在不重啟 Pod 的情況下原地更新資源,若無法原地更新則退回 Recreate 行為 |
InPlace |
嘗試只用原地更新套用資源建議,不會退回驅逐重建;需要叢集與 VPA 啟用對應 feature gate |
Auto |
自 VPA 1.4.0 起已棄用,不建議新配置使用;目前等同 Recreate,需要驅逐重建時建議明確使用 Recreate |
新配置建議使用語意明確的 Off、Initial 或 Recreate,避免沿用舊範例中的 Auto。如果叢集與 VPA 版本支援原地調整 Pod 資源,也可以再評估 InPlaceOrRecreate 或 InPlace。使用 Recreate 時,當 VPA 判斷資源設定需要更新,可能會重新建立 Pod。測試環境可以用這個模式觀察效果,但正式環境要先評估服務是否能承受 Pod 被重建的影響。
resourcePolicy
resourcePolicy 可以限制 VPA 建議值的範圍,避免建議值過低或過高:
1 | resourcePolicy: |
以上設定代表所有容器的 CPU 建議值不得低於 50m,也不得高於 1000m;記憶體則限制在 50Mi 到 1000Mi 之間。這種上下限在正式環境很重要,因為它可以避免 VPA 根據短期負載產生過度極端的資源建議。
Demo 範例:測試用配置文件
前面整理完 VPA 的基本使用方式後,接著用 dockertest_vpa_v1.yml 示範一次完整流程。
這次範例使用 dockertest_vpa_v1.yml 作為測試配置,內容包含三個部分:
- 建立
dockertestNamespace - 建立一個使用
polinux/stressimage 的 Deployment - 建立對應 Deployment 的
VerticalPodAutoscaler
配置內容如下:
1 | # 建立專案使用的命名空間,讓相關資源集中在 dockertest 底下管理。 |
這份配置裡,Deployment 一開始只設定 cpu: "100m" 與 memory: "100Mi"。VPA 會觀察這個 Pod 的實際使用量,再依照 resourcePolicy 設定的上下限提供建議。
Demo 範例:調整 VPA Recommender 半衰期
VPA 的建議值是由 vpa-recommender 根據歷史資源使用量計算出來的。預設情況下,VPA 會保留較長時間的歷史資料,因此在 Demo 或測試環境中,即使已經製造新的 CPU 或記憶體負載,也可能需要等待一段時間,建議值才會明顯變化。
如果希望 VPA 更快忘記舊資料,可以調整 vpa-recommender 的 histogram decay half-life。半衰期越短,舊資料的權重下降越快,VPA 對近期負載變化的反應也會更快。不過這也代表建議值可能更容易受到短時間尖峰影響,所以正式環境不建議設定得太短。
安裝完成後,可以編輯 kube-system namespace 裡的 vpa-recommender Deployment:
1 | kubectl edit deployment vpa-recommender -n kube-system |
找到 recommender container 的 command 區塊,加入以下兩個參數:
1 | containers: |
這裡的 --cpu-histogram-decay-half-life=10m 與 --memory-histogram-decay-half-life=10m 代表 CPU 與記憶體歷史資料的半衰期都調整為 10 分鐘。對 demo 來說,這可以讓 VPA 在壓力測試後更快反映新的資源建議;但在正式環境中,建議先保守評估,避免建議值因短期波動而頻繁改變。
修改完成後,Kubernetes 會重新建立 vpa-recommender Pod。可以用以下指令確認 Pod 是否回到 Running:
1 | kubectl get pods -n kube-system | grep vpa-recommender |
Demo 範例:部署與壓力測試
套用測試配置:
1 | kubectl apply -f ./assets/dockertest_vpa_v1.yml |
確認 Deployment、Pod 與 VPA 是否建立成功:
1 | kubectl get deploy,pod,vpa -n dockertest |
接著可以進入測試 Pod,透過 stress 產生 CPU 或記憶體壓力,讓 VPA 有資料可以觀察:
1 | kubectl exec -it -n dockertest deploy/dockertest-deployment -- stress --cpu 1 --timeout 300s |
影片中則是直接指定 Pod 名稱與 container 執行壓力測試。由於 Pod 名稱每次建立時都可能不同,請先用 kubectl get pod -n dockertest 查詢實際名稱,再替換以下指令中的 <pod-name>:
1 | kubectl exec -it <pod-name> -n dockertest -c dockertest -- stress --cpu 2 --timeout 300 |
等待一段時間後,查看 VPA 狀態:
1 | kubectl describe vpa dockertest-vpa -n dockertest |
在輸出內容中,可以觀察 Recommendation 區塊。這裡通常會出現 Lower Bound、Target、Uncapped Target、Upper Bound 等欄位,代表 VPA 根據目前觀察到的資源使用情況所計算出的建議值。
Demo 範例:觀察重點
測試 VPA 時,可以特別觀察以下幾件事:
Recommendation是否出現 CPU 與 Memory 建議值- 建議值是否落在
minAllowed與maxAllowed範圍內 - 使用
Recreate模式時,Pod 是否曾被重新建立 - 新 Pod 的
resources.requests是否被更新成 VPA 建議值
可以使用以下指令查看 Pod 目前的資源設定:
1 | kubectl get pod -n dockertest -o yaml |
若只想快速觀察 VPA 建議值,也可以使用:
1 | kubectl describe vpa dockertest-vpa -n dockertest |
這次 Demo 影片示範了從部署測試資源、製造負載到觀察 VPA 建議值的完整流程。
Demo 範例:完整重置 VPA 歷史紀錄
在反覆測試 VPA 時,有時候會發現即使重新建立 Pod,VPA 的建議值仍然受到前一次測試結果影響。這是因為 VPA 會透過 VerticalPodAutoscalerCheckpoint 保留歷史統計資料,讓 vpa-recommender 可以延續先前的資源使用紀錄來計算建議值。
如果想重新跑一次乾淨的 demo,可以刪除測試 Deployment、VPA 物件與 namespace 內的 checkpoint,再重新套用配置檔:
1 | kubectl delete deployment dockertest-deployment -n dockertest |
這組指令會先移除目前的測試 Pod 與 VPA,再清掉 dockertest namespace 裡所有 VPA checkpoint。重新 apply 後,VPA 會從新的觀察資料重新累積歷史紀錄,適合用在 demo 前重置狀態,或是在調整半衰期參數後重新驗證建議值變化。
可以用以下指令確認 checkpoint 是否已經被清除:
1 | kubectl get verticalpodautoscalercheckpoints.autoscaling.k8s.io -n dockertest |
若輸出顯示沒有資源,代表目前 namespace 內沒有殘留的 VPA 歷史紀錄。
Demo 範例:清除測試資源
測試完成後,可以先刪除本次建立的 Namespace,連同 Deployment 與 VPA 一起清掉:
1 | kubectl delete namespace dockertest |
如果也要移除 VPA 元件,可以回到 autoscaler/vertical-pod-autoscaler/ 目錄,執行官方提供的清除腳本:
1 | ./hack/vpa-down.sh |
清除過程會刪除 VPA 相關的 CRD、RBAC、ServiceAccount、Deployment、Service 與 Admission Controller 設定。若看到部分 CRD 顯示找不到資源,通常代表前面的刪除步驟已經先移除完成。
小結
VPA 適合用來處理「Pod 資源設定不準」的問題。當服務剛上線、負載型態還不明確,或是希望定期檢查 requests 是否過高或過低時,VPA 可以提供很有參考價值的建議。
不過,若要讓 VPA 自動更新正式環境的 Pod,仍然需要評估服務可用性、Pod 重建影響,以及是否會和 HPA 使用相同資源指標互相干擾。比較穩健的做法是先使用 Off 模式觀察建議值,確認 VPA 的推薦結果符合預期後,再逐步評估是否改用 Initial、Recreate,或在環境支援時評估 InPlaceOrRecreate、InPlace 模式。