Kubernetes HPA 水平自動擴縮容實作紀錄
在 Kubernetes 部署服務時,Pod 的數量通常不會一直維持固定不變。當流量或 CPU 使用率提高時,我們希望服務可以自動增加 Pod 來分攤負載;當負載下降後,也希望 Pod 數量可以自動縮回來,避免浪費叢集資源。
這篇文章會先整理 Kubernetes HPA(Horizontal Pod Autoscaler)的常用設定參數,再透過一份 Spring Boot 測試服務配置,展示如何依照 CPU 使用率自動調整 Deployment 的副本數。
什麼是 HPA?
HPA 是 Kubernetes 用來自動調整 Pod 數量的資源。它會根據指定的指標,例如 CPU 使用率、記憶體使用率或自訂指標,計算目前 Deployment 是否需要擴容或縮容。
以 CPU 使用率為例,HPA 會觀察 Deployment 底下所有 Pod 的平均 CPU 使用情況,並與設定的目標值比較。如果目前平均使用率高於目標值,HPA 就會增加 replicas;如果使用率長時間低於目標值,HPA 則會逐步減少 replicas。
HPA 常見的運作流程如下:
- Deployment 建立 Pod,並在容器中設定
resources.requests.cpu - metrics-server 收集 Pod 的 CPU 與記憶體使用量
- HPA 讀取 metrics-server 提供的指標
- HPA 計算 Deployment 需要的 replicas 數量
- Deployment 依照新的 replicas 建立或移除 Pod
也就是說,HPA 並不是直接建立 Pod,而是透過調整目標 Deployment 的 replicas 來完成擴縮容。
前置條件
使用 HPA 前,需要先確認叢集內已經安裝 metrics-server。HPA 依賴 metrics-server 提供 Pod 的資源使用量,如果 metrics-server 沒有正常運作,就會在 HPA 狀態中看到指標無法取得的訊息。
可以先確認 Node 與 Pod 指標是否能正常查詢:
1 | kubectl top nodes |
1 | kubectl top pods -A |
如果這兩個指令可以看到 CPU 與記憶體使用量,就代表 metrics-server 已經可以提供基本資源指標。
另外,若 HPA 要使用 CPU utilization 作為擴縮容依據,Deployment 的容器必須設定 resources.requests.cpu。HPA 的 CPU 使用率不是直接用容器實際用掉多少 CPU 來判斷,而是用實際 CPU 使用量相對於 requests.cpu 的比例來計算。
HPA 設定參數總覽
HPA 的設定可以大致分成三個部分:第一是指定要控制哪個工作負載,第二是設定要依照哪些指標擴縮容,第三是控制擴容與縮容的速度。
以下是一份較完整的 HPA 設定示意,裡面同時列出多種 metrics 類型與 behavior 控制方式:
1 | spec: |
這份 YAML 不一定要全部照抄到實際環境。實務上通常會先從 CPU 或記憶體這類資源指標開始,等到服務有更明確的業務指標後,再導入 Pods、Object 或 External 指標。
scaleTargetRef
scaleTargetRef 用來指定 HPA 要控制的目標資源。HPA 本身不會直接建立 Pod,而是調整目標資源的 replicas。最常見的目標是 Deployment,也可以是其他支援 scale subresource 的工作負載。
1 | scaleTargetRef: |
這段設定代表 HPA 會去調整 app-deployment 這個 Deployment 的 replicas。當 HPA 判斷需要擴容時,Deployment 會建立更多 Pod;當 HPA 判斷可以縮容時,Deployment 會減少 Pod 數量。
minReplicas 與 maxReplicas
minReplicas 與 maxReplicas 是 HPA 的副本數邊界:
1 | minReplicas: 1 |
minReplicas 代表最少保留幾個 Pod。一般服務通常會至少保留 1 個 Pod,避免被縮到 0 後無法處理請求。
maxReplicas 代表最多可以擴展到幾個 Pod。這個值不是越大越好,因為 Pod 最終仍然需要被排程到 Node 上。如果叢集資源不足,HPA 即使把 replicas 調高,新 Pod 也可能因為 CPU 或記憶體不足而停在 Pending。
metrics 指標設定
metrics 是 HPA 判斷是否擴縮容的依據。HPA 可以同時設定多個指標,Kubernetes 會分別計算每個指標需要的副本數,最後採用其中最高的建議值,避免某個指標已經過載卻被其他較低的指標蓋掉。
Resource 指標
Resource 是最常見的指標類型,用來依照 Pod 的 CPU 或記憶體使用率擴縮容。
1 | metrics: |
name: cpu 代表使用 CPU 作為指標,averageUtilization: 50 代表希望所有 Pod 的平均 CPU 使用率維持在 request 的 50%。如果容器設定 resources.requests.cpu: "200m",那麼 50% 約等於平均使用 100m CPU。
記憶體也可以使用相同方式設定:
1 | metrics: |
使用 CPU 或記憶體的 Utilization 時,容器必須設定對應的 resources.requests。例如 CPU utilization 需要 resources.requests.cpu,Memory utilization 需要 resources.requests.memory。
Pods 指標
Pods 指標會針對每個 Pod 收集某個自訂指標,並以所有 Pod 的平均值作為擴縮容依據。例如每個 Pod 每秒處理的封包數:
1 | metrics: |
AverageValue 代表 HPA 會觀察每個 Pod 的平均指標值。如果平均值高於 1k,HPA 就會評估是否需要增加 Pod 數量。這類指標通常需要搭配 Prometheus Adapter 或其他 custom metrics adapter,讓 Kubernetes 可以透過 Custom Metrics API 取得資料。
Object 指標
Object 指標會針對某個 Kubernetes 物件收集指標,例如 Ingress 每秒總請求數:
1 | metrics: |
describedObject 用來指定這個指標描述的是哪個物件。target.type: Value 代表 HPA 會用該物件的總指標值進行判斷。以上面例子來說,當 main-route 這個 Ingress 的每秒請求數高於 10k 時,HPA 就會評估是否要擴容。
External 指標
External 指標用來讀取 Kubernetes 叢集外部系統的數值,例如訊息佇列中等待處理的任務數:
1 | metrics: |
這類指標很適合用在背景任務 worker。例如佇列中待處理訊息越多,就增加更多 worker Pod;待處理訊息下降後,再逐步縮容。
External 指標通常需要透過 External Metrics API 提供資料來源。常見做法是由 Prometheus Adapter、KEDA 或雲端服務提供對應的外部指標。
target 設定
每個 metric 都會有 target,用來描述 HPA 希望指標維持在什麼水準。常見的 target.type 有三種:
| 類型 | 說明 | 常見搭配 |
|---|---|---|
Utilization |
以 request 為基準計算百分比 | CPU、Memory 的 Resource 指標 |
AverageValue |
每個 Pod 平均分攤後的目標值 | Pods、External 指標 |
Value |
物件或指標本身的總量目標值 | Object 指標 |
Utilization 最容易入門,但它依賴 container 的 resources.requests。AverageValue 與 Value 則常用於自訂指標或外部指標,適合描述更貼近業務的負載,例如每秒請求數、佇列長度、待處理任務數等。
behavior 擴縮容行為控制
behavior 用來控制 HPA 擴容與縮容的速度。這個區塊不決定「要不要擴縮容」,而是控制「一旦需要擴縮容時,可以多快調整」。
1 | behavior: |
scaleUp 是擴容策略,通常會設定得比較積極,讓服務在流量上升時可以快速補上 Pod。scaleDown 是縮容策略,通常會設定得保守一點,避免流量短暫下降就立刻刪掉 Pod。
stabilizationWindowSeconds 是穩定視窗。用在縮容時,常見目的是參考過去一段時間的建議副本數,避免 Pod 數量來回震盪。例如 scaleDown.stabilizationWindowSeconds: 300 代表縮容會觀察過去 300 秒內的建議值,讓縮容比較不容易因短暫低流量而太快發生。
policies 用來限制每段時間最多可以調整多少 Pod。type: Percent 代表依照目前副本數的百分比調整,type: Pods 則代表直接指定 Pod 數量。
以上面的 scaleUp 為例,HPA 每 15 秒最多可以擴容目前副本數的 100%,或最多新增 4 個 Pod。因為 selectPolicy: Max,HPA 會選擇兩個 policy 計算後較大的結果,讓擴容速度更快。
如果想讓擴容更保守,可以把 selectPolicy 改成 Min,或降低 Percent、Pods 的數值。如果想停用某個方向的調整,也可以使用 selectPolicy: Disabled。
Demo 範例架構
前面介紹完 HPA 的主要設定參數後,接著用 dockertest_hpa_v1.yml 展示一個基於 CPU 使用率的 HPA demo。
這次的配置檔 dockertest_hpa_v1.yml 會建立以下 Kubernetes 資源:
| 資源 | 說明 |
|---|---|
Namespace |
建立 dockertest 命名空間,集中管理測試資源 |
Secret |
儲存拉取私有 Container Registry 映像所需的認證資訊 |
Deployment |
部署 Spring Boot 測試服務 |
Service |
建立 ClusterIP,讓叢集內部可以存取服務 |
HorizontalPodAutoscaler |
依照 CPU 使用率自動調整 Deployment replicas |
其中 Secret 會依環境存放私有 registry 認證,實務上不建議直接把可用憑證寫入文章或公開版控。本文只說明用途,不展開 Secret 內容。
Deployment 資源設定
HPA 要依照 CPU utilization 計算擴縮容,Deployment 內的容器需要設定 resources.requests.cpu。在這份範例中,容器的 CPU request 與 limit 都設定為 150m:
1 | resources: |
150m 代表 0.15 顆 CPU。後面 HPA 的 averageUtilization 設定為 50,意思是希望所有 Pod 的平均 CPU 使用率維持在 CPU request 的 50%。以目前 requests.cpu: "150m" 來看,目標平均使用量約為 75m。
Deployment 同時設定了 readinessProbe,讓 Kubernetes 透過 /hello 判斷 Pod 是否已經可以接收流量:
1 | readinessProbe: |
readinessProbe 與 HPA 沒有直接關係,但它可以避免尚未就緒的 Pod 被 Service 導流。當 HPA 擴容建立新 Pod 時,這個設定可以讓流量在 Pod 準備好之後才進入服務。
Service 設定
配置檔中建立了一個 ClusterIP Service,將叢集內部流量轉發到 Spring Boot 容器的 8080 port:
1 | apiVersion: v1 |
這個 Service 的 selector 會選到 labels 為 app: dockertest 的 Pod。當 HPA 增加 Deployment replicas 後,新建立的 Pod 也會帶有相同 label,因此 Service 會自動把這些 Pod 納入流量轉發目標。
HPA 設定
HPA 的核心設定如下:
1 | apiVersion: autoscaling/v2 |
scaleTargetRef 指定 HPA 要控制的目標資源。這裡指向 dockertest-deployment,因此 HPA 會調整這個 Deployment 的 replicas。
minReplicas: 1 代表至少保留 1 個 Pod,避免服務被縮到 0;maxReplicas: 5 代表最多擴展到 5 個 Pod。這個上限需要搭配叢集節點資源一起評估,否則 HPA 雖然想擴容,但新的 Pod 可能會因為資源不足而停在 Pending。
averageUtilization: 50 代表 HPA 希望 Pod 平均 CPU 使用率維持在 request 的 50%。當目前使用率高於目標值時,HPA 會提高 replicas;當使用率下降後,HPA 會再依照縮容策略逐步降低 replicas。
這份配置也額外設定了 HPA 的擴縮容行為:
1 | behavior: |
scaleUp 設定代表需要擴容時,每 15 秒最多增加 2 個 Pod。scaleDown 則代表縮容時,每 15 秒最多減少 1 個 Pod,並加上 60 秒的穩定視窗,避免負載剛下降就太快縮容。
套用配置檔
確認配置檔準備好後,可以使用 kubectl apply 建立資源:
1 | kubectl apply -f ./assets/dockertest_hpa_v1.yml |
建立完成後,檢查 Deployment、Pod、Service 與 HPA 狀態:
1 | kubectl get deployment,pod,service,hpa -n dockertest |
也可以單獨觀察 HPA:
1 | kubectl get hpa -n dockertest |
如果 metrics-server 可以正常提供資料,TARGETS 欄位會出現類似 10%/50% 的數值。前面的數字是目前平均 CPU 使用率,後面的數字是 HPA 設定的目標值。
若要查看更完整的 HPA 判斷過程,可以使用:
1 | kubectl describe hpa dockertest-hpa -n dockertest |
在 Events 區塊中可以看到 HPA 是否成功取得指標,以及是否曾經觸發擴容或縮容。
觀察 HPA 擴容
要讓 HPA 觸發擴容,需要讓目標 Pod 的 CPU 使用率超過設定值。若測試服務的 /hello endpoint 會產生足夠 CPU 負載,可以在叢集內建立一個臨時 Pod 持續打流量:
1 | kubectl run hpa-load -n dockertest --rm -it --image=busybox:1.36 --restart=Never -- /bin/sh |
進入 Pod 後執行:
1 | while true; do wget -q -O- http://dockertest-clusterip:8080/hello > /dev/null; done |
接著開另一個終端機觀察 HPA 與 Pod 數量:
1 | kubectl get hpa,pod -n dockertest -w |
當 CPU 使用率持續高於目標值時,HPA 會逐步提高 Deployment replicas,Pod 數量會從 1 增加到 2、3,最多不會超過 maxReplicas: 5。
如果 /hello 只是很輕量的回應,CPU 使用率可能不足以觸發 HPA。這時候可以改用應用程式中較耗 CPU 的測試端點,或在服務內準備專門用來壓測的 endpoint,讓 demo 更容易觀察到擴容效果。
觀察 HPA 縮容
停止壓力測試後,CPU 使用率會逐漸下降。HPA 不會立刻把 Pod 全部縮回去,而是會依照 scaleDown 的設定逐步縮容。
在目前配置中,scaleDown.stabilizationWindowSeconds 設定為 60,代表 HPA 會等待一段穩定時間後再進行縮容判斷。縮容政策則設定為每 15 秒最多減少 1 個 Pod:
1 | scaleDown: |
這樣的設定可以避免服務在流量短暫下降時過快縮容,導致下一波流量進來又需要重新擴容。
常見問題
HPA 顯示 unknown
如果 kubectl get hpa 看到 TARGETS 顯示 <unknown>/50%,通常代表 HPA 無法取得 CPU 指標。可以依序檢查:
- metrics-server 是否已安裝並正常運作
kubectl top pods -n dockertest是否能取得 Pod 指標- Deployment 是否有設定
resources.requests.cpu - Pod 是否已經進入
Running並通過 readinessProbe
HPA 沒有擴容
HPA 沒有擴容不一定代表設定錯誤。可以先確認目前 CPU 使用率是否真的高於目標值:
1 | kubectl get hpa dockertest-hpa -n dockertest |
如果目前使用率低於 50%,HPA 就不會增加 replicas。若測試流量很輕,可能需要提高負載,或改用會消耗 CPU 的測試行為。
Pod 數量沒有超過 5 個
這是因為 HPA 設定了 maxReplicas: 5。即使 CPU 使用率持續偏高,HPA 也不會把 Deployment 擴展到超過 5 個 Pod。
如果要提高上限,可以調整 HPA 的 maxReplicas,但同時也要確認叢集節點資源是否足夠承載更多 Pod。
清理測試資源
測試完成後,可以用同一份配置檔刪除資源:
1 | kubectl delete -f ./assets/dockertest_hpa_v1.yml |
如果只想確認命名空間內剩餘資源,可以使用:
1 | kubectl get all -n dockertest |
小結
HPA 是 Kubernetes 中非常實用的自動擴縮容機制。它可以依照實際負載調整 Pod 數量,讓服務在高流量時具備更好的承載能力,也能在低流量時釋放不必要的資源。
在本次範例中,HPA 透過 metrics-server 取得 CPU 使用率,並依照 averageUtilization: 50 判斷是否要調整 dockertest-deployment 的 replicas。實作時要特別注意兩件事:第一,叢集必須能正常提供 metrics;第二,Deployment 必須設定 resources.requests.cpu,HPA 才能正確計算 CPU utilization。
只要掌握這兩個重點,再搭配適當的 minReplicas、maxReplicas 與 behavior 設定,就可以讓服務具備基本且可控的自動擴縮容能力。