跳至主要内容

AutoScaling - Horizontal Pod AutoScaler

什麼是 Horizontal Pod Autoscaler?

horizontal-pod-autoscaler

Horizontal Pod Autoscaler (HPA) 是 Kubernetes 提供的自動擴展機制之一,負責根據 CPU、記憶體或自訂指標(如請求數量、佇列長度等)動態調整 Pod 的數量,以確保應用程式的效能與穩定性,同時提升資源利用率。

HPA 透過定期監測指標來決定是否需要擴展或縮減 Pod 數量,適用於 Deployment、ReplicaSet 及 StatefulSet 等資源。


HPA 運作原理

HPA 主要透過 Kubernetes Metrics API 來監控資源使用率,並根據定義的規則自動調整 Pod 數量。其運作流程如下:

  1. 監測指標數據:HPA 透過 metrics-serverPrometheus Adapter 等監控工具,收集 CPU、記憶體或自訂指標數據。
  2. 計算目標 Pod 數量:根據定義的 minReplicasmaxReplicas 和指標目標值,計算理想的 Pod 數量。
  3. 自動擴展或縮減:當指標超過設定閾值時,HPA 會自動調整 Pod 數量,確保應用程式有足夠的計算資源應對負載變化。
  4. 行為控制 (Behavior):可透過 behavior 設定 scaleUpscaleDown 的速率,避免過度擴展或頻繁縮減導致不穩定。

HPA 配置範例

以下是一個 HPA 設定範例,根據 CPU、記憶體及外部指標來自動調整 php-apache 應用的 Pod 數量:

apiVersion: autoscaling/v2beta2 # v2beta2 後可以使用Metrics Server中的memory當作縮減指標
kind: HorizontalPodAutoscaler
metadata:
name: php-apache # HPA 名稱
spec:
scaleTargetRef:
apiVersion: apps/v1 # 目標資源的 API 版本
kind: Deployment # 目標類型(可以是 Deployment、StatefulSet、ReplicaSet)
name: php-apache # 目標 Deployment 的名稱

minReplicas: 1 # 最小 Pod 數量,確保至少有 1 個 Pod 在運行
maxReplicas: 10 # 最大 Pod 數量,避免擴展過度導致資源浪費

metrics:
# 1. 基於 CPU 使用率的擴展
- type: Resource
resource:
name: cpu # 指標類型為 CPU
target:
type: Utilization # 以 CPU 使用率為基準
averageUtilization: 50 # 當 CPU 使用率達 50% 時觸發擴展

# 2. 基於 Memory(記憶體)使用率的擴展
- type: Resource
resource:
name: memory # 指標類型為 Memory
target:
type: Utilization # 以 Memory 使用率為基準
averageUtilization: 80 # 當記憶體使用率達 80% 時觸發擴展

# 3. 基於 Pod 層級的自訂指標
- type: Pods
pods:
metric:
name: packets-per-second # 監控每秒處理的封包數
target:
type: AverageValue # 以平均值為基準
averageValue: 1k # 當封包數達到 1000 個/秒時觸發擴展

# 4. 針對特定 Kubernetes 資源(Object)的指標,例如 Ingress
- type: Object
object:
metric:
name: requests-per-second # 監控 Ingress 的每秒請求數
describedObject:
apiVersion: networking.k8s.io/v1 # Ingress API 版本
kind: Ingress # 目標資源類型
name: main-route # Ingress 名稱
target:
type: Value # 以固定值為基準
value: 10k # 當請求數達到 10,000 req/s 時觸發擴展

# 5. 基於外部系統(如 RabbitMQ、Kafka)的指標
- type: External
external:
metric:
name: queue_messages_ready # 監控佇列中待處理的訊息數
selector:
matchLabels:
queue: 'worker_tasks' # 監控特定佇列的訊息
target:
type: AverageValue # 以平均數據為基準
averageValue: 30 # 當待處理訊息超過 30 時觸發擴展

# HPA 的行為策略
behavior:
# 縮減 Pod 數量的策略
scaleDown:
stabilizationWindowSeconds: 300 # 5 分鐘內不會突然縮減 Pod,避免頻繁變動
policies:
- type: Percent # 以百分比調整 Pod 數量
value: 100 # 一次最多縮減 100%(即移除所有額外的 Pod)
periodSeconds: 15 # 每 15 秒評估一次是否需要縮減

# 擴展 Pod 數量的策略
scaleUp:
stabilizationWindowSeconds: 0 # 允許即時擴展 Pod
policies:
- type: Percent # 以百分比調整 Pod 數量
value: 100 # 一次最多擴展 100% 的 Pod 數量
periodSeconds: 15 # 每 15 秒評估一次擴展需求
- type: Pods # 以固定數量擴展 Pod
value: 1 # 每 60 秒最多增加 1 個 Pod
periodSeconds: 60
- type: Pods # 另一種擴展策略,每 15 秒最多增加 4 個 Pod
value: 4
periodSeconds: 15
selectPolicy: Max # 使用最大擴展策略,確保擴展時能最大化新增 Pod 數量

HPA 指標類型

metrics 指標類型

HPA 可使用以下 4 種指標類型 來決定是否擴展 Pod:

  1. Resource 指標
    • CPU / Memory:監測 Pod 使用的 CPU 及記憶體,依據 Utilization 來決定擴展。
  2. Pods 指標
    • 監測 Pod 層級的自訂指標,如 packets-per-secondrequests-per-second
  3. Object 指標
    • 監測 Kubernetes 內部資源(如 Ingress、Service)的自訂指標,如 HTTP 請求數。
  4. External 指標
    • 監測 Kubernetes 以外的數據來源(如 Kafka、RabbitMQ 佇列長度),可搭配 Prometheus Adapter

behavior 指標說明

縮減策略 (scaleDown)

當負載下降時,HPA 需要決定何時縮減 Pod,避免浪費資源,但又不能過快減少 Pod,影響服務可用性。

  1. stabilizationWindowSeconds(穩定時間窗口)
    • 這是 縮減 Pod 前的等待時間,用來確保負載下降是長期趨勢,而非短期波動。
    • 例如設定 300 秒(5 分鐘),HPA 會在 5 分鐘內持續觀察負載變化,若負載一直低於門檻,才會真正縮減 Pod。這樣可以避免負載瞬間下降後又上升,導致 Pod 被砍掉又馬上新增的情況。
    • 另外,HPA 在此時間窗口內會根據最不利的指標來做出決策。當需要擴展時,HPA 會選擇最不需要資源的最小值來進行縮減,避免過度縮減導致系統無法應對其他指標的需求。
  2. policies(縮減策略)
    • 你可以設定縮減的方式,例如:
      • 15 秒 內最多縮減 100% 的 Pod(即一次砍掉所有多餘的 Pod)。
      • 這樣的設計適合突然流量下降的情境,例如批次作業完成後的計算資源釋放。

擴展策略 (scaleUp)

  1. stabilizationWindowSeconds(穩定時間窗口)
    • scaleUp 部分,通常設為 0 秒,代表當負載超過門檻時,HPA 會立即擴展 Pod,確保服務不會因為資源不足而影響性能。
    • 這與 scaleDown 相反,因為縮減需要穩定性,而擴展則需要即時性。
    • 另外,HPA 在此時間窗口內會根據最不利的指標來做出決策。當需要擴展時,HPA 會選擇當前指標中最需要資源的最大值來進行擴展,以確保系統有足夠的資源應對需求。
  2. policies(擴展策略)
    • 你可以設定多種擴展策略,例如:
      • 15 秒 內最多增加 100% 的 Pod(即 Pod 數量翻倍)。
      • 60 秒 最少增加 1 個 Pod,確保擴展速度不會過慢。
      • 15 秒 最多增加 4 個 Pod,用於應對突發性高流量需求。
    • selectPolicy: Max 代表當多種策略同時生效時,會選擇擴展最快的策略,確保 Pod 能夠迅速補上需求。

HPA 適用場景

HPA 適用於需要根據負載變化自動調整 Pod 數量的應用場景,例如:

  • Web 應用:流量高峰時擴展 Pod,流量下降時縮減 Pod,提升資源利用率。
  • 消息佇列處理:根據 Kafka / RabbitMQ 佇列長度動態擴展消費者 Pod。
  • 即時數據處理:針對 CPU 或記憶體密集型工作負載,如即時影音轉碼、自動化 AI 推理,動態調整計算資源。
  • 微服務架構:根據 API 請求數量調整後端微服務的 Pod 數量,避免請求過載。

HPA 限制與最佳實踐

HPA 限制

  • 僅適用於 Deployment、ReplicaSet、StatefulSet,無法直接擴展單獨的 Pod。
  • 依賴 Metrics API,如果 metrics-serverPrometheus Adapter 未正確配置,HPA 可能無法獲取指標。
  • 適合短時間內可彈性擴展的應用,但不適用於狀態依賴性強的應用(如單例應用)。

HPA 最佳實踐

  • 設定 minReplicas 以確保最低可用 Pod 數,避免流量突增時無法及時擴展。
  • 搭配 behavior 控制擴展/縮減策略,避免 Pod 過度擴展或頻繁縮減導致不穩定。
  • 使用 Prometheus Adapter 來監測更細緻的應用層級指標,例如請求延遲、佇列長度等。
  • 對於有狀態應用(如資料庫),建議使用 StatefulSet 搭配 Cluster Autoscaler 進行節點擴展

實作一個 HPA

  1. 首先我們需要確認 Metrics Server 有正確執行。
kubectl.exe top node
---

NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
docker-desktop 164m 0% 3426Mi 21%
  1. 首先我們線設計一個 deployment,能夠讓 HPA 來控制他。
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
replicas: 1
selector:
matchLabels:
run: php-apache
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
limits:
cpu: '500m'
memory: '512Mi'
requests:
cpu: '500m'
memory: '512Mi'
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
selector:
run: php-apache
ports:
- protocol: TCP
port: 80
  1. 接著我們設定 HPA,讓他監測 CPU 和 Memory 的使用率,如果使用率達到就會自動擴展和縮小。
hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1 # 最少 1 個 Pod
maxReplicas: 10 # 最多 10 個 Pod
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # 當 CPU 使用率達到 50% 時擴展
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80 # 當內存使用率達到 80% 時擴展
  1. 我們可以透過指令來運行他們。
kubectl.exe apply -f deployment.yaml
---

deployment.apps/php-apache created
service/php-apache created
kubectl.exe apply -f hpa.yaml
---

horizontalpodautoscaler.autoscaling/php-apache created
  1. 接者我們可以執行 HPA 的監控。
kubectl.exe get hpa --watch
---

NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache <unknown>/50%, <unknown>/80% 1 10 0 52s
php-apache Deployment/php-apache 0%/50%, 1%/80% 1 10 1 60s
  1. 接著我們做一個 busybox 這個 pod,他可以不斷地向 web-server 發送請求,當作壓力測試,其中的--rm 代表結束就刪除,--restart=Never 代表不會重啟,並透過 tty 做交互式互動,使用/bin/sh 當最殼。
kubectl.exe run -it load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh
---

If you don't see a command prompt, try pressing enter.
/ # /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
---

OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!
  1. 最後我們可以發現當 CPU 超過預設的 50% 閥值後,Replicas 的數量逐漸上升,直到指標下降到 50% 以下。
kubectl.exe get hpa --watch
---

NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache <unknown>/50%, <unknown>/80% 1 10 0 52s
php-apache Deployment/php-apache 0%/50%, 1%/80% 1 10 1 60s
php-apache Deployment/php-apache 13%/50%, 2%/80% 1 10 1 7m1s
php-apache Deployment/php-apache 99%/50%, 2%/80% 1 10 1 8m1s
php-apache Deployment/php-apache 61%/50%, 2%/80% 1 10 2 9m1s
php-apache Deployment/php-apache 43%/50%, 2%/80% 1 10 3 10m
php-apache Deployment/php-apache 43%/50%, 2%/80% 1 10 3 11m
  1. 接著我們將剛剛的 busybox 取消,按下 ctrl+c,我們在觀察 HPA,就可以發現牠漸漸把 Replica 刪除,直到回到 minReplicas。
kubectl.exe get hpa --watch
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache <unknown>/50%, <unknown>/80% 1 10 0 52s
php-apache Deployment/php-apache 0%/50%, 1%/80% 1 10 1 60s
php-apache Deployment/php-apache 13%/50%, 2%/80% 1 10 1 7m1s
php-apache Deployment/php-apache 99%/50%, 2%/80% 1 10 1 8m1s
php-apache Deployment/php-apache 61%/50%, 2%/80% 1 10 2 9m1s
php-apache Deployment/php-apache 43%/50%, 2%/80% 1 10 3 10m
php-apache Deployment/php-apache 43%/50%, 2%/80% 1 10 3 11m
php-apache Deployment/php-apache 21%/50%, 2%/80% 1 10 3 12m
php-apache Deployment/php-apache 0%/50%, 2%/80% 1 10 3 13m
php-apache Deployment/php-apache 0%/50%, 2%/80% 1 10 3 16m
php-apache Deployment/php-apache 0%/50%, 2%/80% 1 10 2 17m
php-apache Deployment/php-apache 0%/50%, 2%/80% 1 10 1 18m
php-apache Deployment/php-apache 0%/50%, 2%/80% 1 10 1 22m