為什麼需要 HPA#

像搶票系統、訂餐平台這類服務,每天都有可預期的尖峰時段:午餐時間外送平台流量必定爆衝。但人工每天定時把副本拉高再拉低不僅費神,還無法應付突如其來、無法預期的流量高峰。

水平擴縮(Horizontal Pod Autoscaler, HPA)的價值就在這裡 — 在十幾秒內就能對指標變化做出反應,自動把 Deployment 的副本數往上推或往下收,讓服務在尖峰自動擴容、平時收回資源。

動手前的準備#

在開始之前,要先確認叢集已經安裝好 Metrics Server,因為 HPA 依靠它收集 CPU、記憶體等指標:

kubectl top node

預期輸出類似:

NAME             CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
docker-desktop   258m         6%     5717Mi          72%

如果還沒裝,請參考前一章「Metrics Server 安裝」。

HPA 設定檔總覽#

下面先用一份骨架說明 HPA 的主要欄位,完整可運作的 YAML 留到「實際演練」段落。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  scaleTargetRef: # 指向要被擴縮的 Deployment/ReplicaSet
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics: # 可同時混合多種 type
    - type: Resource # CPU / 記憶體(Metrics Server 提供)
    - type: Pods # 每 Pod 的自訂指標(取平均)
    - type: Object # 以單一 K8s 物件(如 Ingress)的指標為條件
    - type: External # 非 K8s 來源(例如 message queue 長度)
  behavior: # 擴縮節奏控制,多半用預設即可
    scaleDown: { stabilizationWindowSeconds: 300, ... }
    scaleUp: { stabilizationWindowSeconds: 0, ... }

幾個重要欄位:

  • apiVersionautoscaling/v2beta2 之後才能用 Metrics Server 的記憶體當指標。
  • spec.minReplicas / spec.maxReplicas:副本數的下限與上限,不能設成 0。
  • spec.metrics[].type
    • Resource:Kubernetes 已知的資源指標(CPU、記憶體),描述當前擴縮目標中每個 Pod 的使用情形。
    • Pods:當前擴縮目標每個 Pod 的自訂指標,比較前會先平均。pods.metric.selector 可以視為 labelSelector,用來縮小指標範圍。
    • Object:以單一 Kubernetes 物件(例如 Ingress)的指標當作觸發條件,需要透過 describedObject 提供足夠資訊。
    • External:以非 Kubernetes 物件的外部指標當觸發條件,必須提供 metric.namemetric.selector
  • target.typeUtilization(以 Request 百分比,僅 Resource 適用)、AverageValue(跨 Pod 均值)、Value(單一目標值)。
  • behavior
    • stabilizationWindowSeconds:當指標顯示要縮容時,HPA 會回看過去這段時間內的最大期望值,避免副本數抖動太頻繁。
    • scaleUp / scaleDown policy:每隔 periodSeconds 一段時間,副本變化幅度不會超過 PercentPods 所指定的數量。

實際演練#

部署示範服務#

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  selector:
    matchLabels:
      run: php-apache
  replicas: 1
  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:
  ports:
    - port: 80
  selector:
    run: php-apache

設定 HPA#

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

部署:

kubectl apply -f ./deployment.yaml
kubectl apply -f ./hpa.yaml

模擬壓力測試#

開一個無窮迴圈打 php-apache:

kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"

另一個 terminal 持續觀察 HPA:

kubectl get hpa --watch

可以看到副本數從 1 一路跟著 CPU 使用率攀升而擴展:

NAME         REFERENCE               TARGETS                MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   0%/80%, <unknown>/50%  1         10        1          3s
php-apache   Deployment/php-apache   1%/80%, 0%/50%         1         10        1          33s
php-apache   Deployment/php-apache   1%/80%, 98%/50%        1         10        3          63s
php-apache   Deployment/php-apache   2%/80%, 29%/50%        1         10        3          94s
php-apache   Deployment/php-apache   2%/80%, 33%/50%        1         10        3          2m4s

小結#

HPA 提供了最直觀的水平擴縮能力,但在真正的高流量正式環境中,還需要更細緻的設定與更多外部指標來搭配。Kubernetes 預設的 HPA 在「兩分鐘內擴出上千台」這類場景上幫不上忙;那時就會用上 Prometheus、Grafana 等更完整的監控與指標體系。下一章會接著看垂直擴縮(Vertical Pod Autoscaler, VPA)。

原文出處#

  • GitHub:https://github.com/MikeHsu0618/2022-ithelp/tree/main/Day26
  • iThome:https://ithelp.ithome.com.tw/articles/10298858