HorizontalPodAutoscaler 演練

HorizontalPodAutoscaler (簡稱 HPA) 會自動更新工作負載資源(例如 DeploymentStatefulSet),旨在自動擴縮工作負載以匹配需求。

水平擴縮意味著,為了應對增加的負載,將部署更多 Pod。這與**垂直**擴縮不同。在 Kubernetes 中,垂直擴縮意味著為工作負載中已執行的 Pod 分配更多資源(例如:記憶體或 CPU)。

如果負載減少,並且 Pod 數量高於配置的最小值,則 HorizontalPodAutoscaler 會指示工作負載資源(Deployment、StatefulSet 或其他類似資源)縮減。

本文件將透過一個示例,指導你如何啟用 HorizontalPodAutoscaler 自動管理示例 Web 應用程式的擴縮。這個示例工作負載是執行一些 PHP 程式碼的 Apache httpd。

準備工作

你需要有一個 Kubernetes 叢集,並且 kubectl 命令列工具已配置為與你的叢集通訊。建議在至少有兩個不是控制平面主機的節點組成的叢集上執行此教程。如果你還沒有叢集,可以使用 minikube 建立一個,或者使用以下 Kubernetes 演練場之一

你的 Kubernetes 伺服器版本必須在 1.23 或更高。

要檢查版本,請輸入 kubectl version

如果你執行的是較早版本的 Kubernetes,請參閱該版本的文件(請參閱可用的文件版本)。

要完成本演練,你還需要使用一個已部署和配置 Metrics Server 的叢集。Kubernetes Metrics Server 從叢集中的 kubelets 收集資源指標,並透過 Kubernetes API 公開這些指標,使用 APIService 新增表示指標讀數的新資源型別。

要了解如何部署 Metrics Server,請參閱 metrics-server 文件

如果你正在執行 Minikube,請執行以下命令以啟用 metrics-server

minikube addons enable metrics-server

執行並暴露 php-apache 伺服器

為了演示 HorizontalPodAutoscaler,你首先要啟動一個 Deployment,它使用 `hpa-example` 映象執行一個容器,並透過以下清單將其作為 Service 公開

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
spec:
  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
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: php-apache
  labels:
    run: php-apache
spec:
  ports:
  - port: 80
  selector:
    run: php-apache

為此,請執行以下命令

kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
deployment.apps/php-apache created
service/php-apache created

建立 HorizontalPodAutoscaler

現在伺服器正在執行,使用 `kubectl` 建立自動擴縮器。`kubectl` 的 `kubectl autoscale` 子命令可以幫助你完成此操作。

你很快將執行一個命令,該命令會建立一個 HorizontalPodAutoscaler,它將在 1 到 10 個副本之間維護你在這些說明的第一步中建立的 php-apache Deployment 所控制的 Pod。

大致來說,HPA 控制器 將增加和減少副本數量(透過更新 Deployment),以將所有 Pod 的平均 CPU 利用率維持在 50%。Deployment 隨後會更新 ReplicaSet——這是 Kubernetes 中所有 Deployment 的工作方式的一部分——然後 ReplicaSet 會根據其 `.spec` 的變化新增或刪除 Pod。

由於每個 Pod 透過 `kubectl run` 請求 200 毫核,這意味著平均 CPU 使用率為 100 毫核。有關演算法的更多詳細資訊,請參閱 演算法詳細資訊

建立 HorizontalPodAutoscaler

kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled

你可以透過執行以下命令檢查新建立的 HorizontalPodAutoscaler 的當前狀態

# You can use "hpa" or "horizontalpodautoscaler"; either name works OK.
kubectl get hpa

輸出類似於:

NAME         REFERENCE                     TARGET    MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%  1         10        1          18s

(如果你看到其他不同名稱的 HorizontalPodAutoscaler,那意味著它們已經存在,這通常不是問題)。

請注意,當前 CPU 消耗為 0%,因為沒有客戶端向伺服器傳送請求(`TARGET` 列顯示對應部署所控制的所有 Pod 的平均值)。

增加負載

接下來,看看自動擴縮器如何響應增加的負載。為此,你將啟動另一個 Pod 作為客戶端。客戶端 Pod 中的容器將無限迴圈執行,向 php-apache 服務傳送查詢。

# Run this in a separate terminal
# so that the load generation continues and you can carry on with the rest of the steps
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"

現在執行

# type Ctrl+C to end the watch when you're ready
kubectl get hpa php-apache --watch

大約一分鐘左右,你應該會看到更高的 CPU 負載;例如

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        1          3m

然後,更多的副本。例如

NAME         REFERENCE                     TARGET      MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   305% / 50%  1         10        7          3m

此時,CPU 消耗已增加到請求的 305%。結果,Deployment 被調整為 7 個副本。

kubectl get deployment php-apache

你應該看到副本數量與 HorizontalPodAutoscaler 中的數字相匹配

NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   7/7      7           7           19m

停止生成負載

要結束此示例,請停止傳送負載。

在你建立執行 `busybox` 映象的 Pod 的終端中,透過鍵入 `<Ctrl> + C` 終止負載生成。

然後驗證結果狀態(大約一分鐘後)

# type Ctrl+C to end the watch when you're ready
kubectl get hpa php-apache --watch

輸出類似於:

NAME         REFERENCE                     TARGET       MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache/scale   0% / 50%     1         10        1          11m

Deployment 也顯示它已經縮減

kubectl get deployment php-apache
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           27m

一旦 CPU 利用率降至 0,HPA 會自動將副本數量縮減回 1。

副本自動擴縮可能需要幾分鐘。

基於多指標和自定義指標的自動擴縮

透過使用 `autoscaling/v2` API 版本,你可以引入額外的指標來自動擴縮 `php-apache` Deployment。

首先,獲取 `autoscaling/v2` 形式的 HorizontalPodAutoscaler 的 YAML

kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml

用編輯器開啟 `/tmp/hpa-v2.yaml` 檔案,你應該會看到如下所示的 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
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
      current:
        averageUtilization: 0
        averageValue: 0

注意,`targetCPUUtilizationPercentage` 欄位已被一個名為 `metrics` 的陣列替換。CPU 利用率指標是一個**資源指標**,因為它表示為 Pod 容器上指定資源的百分比。請注意,除了 CPU,你還可以指定其他資源指標。預設情況下,唯一支援的另一個資源指標是 `memory`。這些資源在叢集之間不會改變名稱,並且應該始終可用,只要 `metrics.k8s.io` API 可用。

你還可以透過使用 `target.type` 為 `AverageValue` 而不是 `Utilization`,並設定相應的 `target.averageValue` 欄位而不是 `target.averageUtilization`,來以直接值而不是請求值的百分比來指定資源指標。

  metrics:
  - type: Resource
    resource:
      name: memory
      target:
        type: AverageValue
        averageValue: 500Mi

還有兩種其他型別的指標,它們都被認為是**自定義指標**:Pod 指標和物件指標。這些指標的名稱可能因叢集而異,並且需要更高階的叢集監控設定。

第一種替代指標型別是**Pod 指標**。這些指標描述 Pod,並在 Pod 之間進行平均,然後與目標值進行比較以確定副本計數。它們的工作方式與資源指標非常相似,只是它們**只**支援 `AverageValue` 型別的 `target`。

Pod 指標使用如下所示的指標塊指定

type: Pods
pods:
  metric:
    name: packets-per-second
  target:
    type: AverageValue
    averageValue: 1k

第二種替代指標型別是**物件指標**。這些指標描述同一名稱空間中的另一個物件,而不是描述 Pod。這些指標不一定是從物件中獲取的;它們只描述它。物件指標支援 `Value` 和 `AverageValue` 兩種 `target` 型別。使用 `Value` 時,目標直接與從 API 返回的指標進行比較。使用 `AverageValue` 時,從自定義指標 API 返回的值除以 Pod 數量,然後與目標進行比較。以下示例是 `requests-per-second` 指標的 YAML 表示。

type: Object
object:
  metric:
    name: requests-per-second
  describedObject:
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    name: main-route
  target:
    type: Value
    value: 2k

如果你提供多個這樣的指標塊,HorizontalPodAutoscaler 將依次考慮每個指標。HorizontalPodAutoscaler 將為每個指標計算建議的副本數,然後選擇副本數最高的那個。

例如,如果你的監控系統收集了關於網路流量的指標,你可以使用 `kubectl edit` 更新上述定義,使其看起來像這樣

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: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k
status:
  observedGeneration: 1
  lastScaleTime: <some-time>
  currentReplicas: 1
  desiredReplicas: 1
  currentMetrics:
  - type: Resource
    resource:
      name: cpu
    current:
      averageUtilization: 0
      averageValue: 0
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: main-route
      current:
        value: 10k

然後,你的 HorizontalPodAutoscaler 將嘗試確保每個 Pod 消耗大約 50% 的所請求 CPU,每秒處理 1000 個數據包,並且 main-route Ingress 後面的所有 Pod 總共每秒處理 10000 個請求。

基於更具體指標的自動擴縮

許多指標管道允許你透過名稱或一組稱為**標籤**的額外描述符來描述指標。對於所有非資源指標型別(Pod、物件和外部,如下所述),你可以指定一個額外的標籤選擇器,該選擇器將傳遞給你的指標管道。例如,如果你收集帶有 `verb` 標籤的 `http_requests` 指標,你可以指定以下指標塊以僅對 GET 請求進行擴縮

type: Object
object:
  metric:
    name: http_requests
    selector: {matchLabels: {verb: GET}}

此選擇器使用與完整的 Kubernetes 標籤選擇器相同的語法。如果名稱和選擇器匹配多個序列,則監控管道會確定如何將多個序列摺疊成單個值。選擇器是附加的,不能選擇描述**非**目標物件(`Pods` 型別中的目標 Pod,`Object` 型別中描述的物件)的指標。

在 Kubernetes 上執行的應用程式可能需要根據與 Kubernetes 叢集中的任何物件沒有明顯關係的指標進行自動擴縮,例如描述沒有直接關聯到 Kubernetes 名稱空間的託管服務的指標。在 Kubernetes 1.10 及更高版本中,你可以使用**外部指標**來解決此用例。

使用外部指標需要了解你的監控系統;設定類似於使用自定義指標時所需的設定。外部指標允許你根據監控系統中可用的任何指標自動擴縮叢集。提供一個包含 `name` 和 `selector` 的 `metric` 塊(如上所述),並使用 `External` 指標型別而不是 `Object`。如果 `metricSelector` 匹配到多個時間序列,HorizontalPodAutoscaler 會使用它們的總和值。外部指標支援 `Value` 和 `AverageValue` 兩種目標型別,其功能與使用 `Object` 型別時完全相同。

例如,如果你的應用程式從託管佇列服務處理任務,你可以在 HorizontalPodAutoscaler 清單中新增以下部分,以指定你需要每 30 個待處理任務一個工作程式。

- type: External
  external:
    metric:
      name: queue_messages_ready
      selector:
        matchLabels:
          queue: "worker_tasks"
    target:
      type: AverageValue
      averageValue: 30

在可能的情況下,優先使用自定義指標目標型別而不是外部指標,因為叢集管理員更容易保護自定義指標 API。外部指標 API 可能會允許訪問任何指標,因此叢集管理員在公開它時應謹慎。

附錄:Horizontal Pod Autoscaler 狀態條件

使用 `autoscaling/v2` 形式的 HorizontalPodAutoscaler 時,你將能夠看到 Kubernetes 在 HorizontalPodAutoscaler 上設定的**狀態條件**。這些狀態條件表明 HorizontalPodAutoscaler 是否能夠擴縮,以及它目前是否受到任何限制。

這些條件出現在 `status.conditions` 欄位中。要檢視影響 HorizontalPodAutoscaler 的條件,我們可以使用 `kubectl describe hpa`

kubectl describe hpa cm-test
Name:                           cm-test
Namespace:                      prom
Labels:                         <none>
Annotations:                    <none>
CreationTimestamp:              Fri, 16 Jun 2017 18:09:22 +0000
Reference:                      ReplicationController/cm-test
Metrics:                        ( current / target )
  "http_requests" on pods:      66m / 500m
Min replicas:                   1
Max replicas:                   4
ReplicationController pods:     1 current / 1 desired
Conditions:
  Type                  Status  Reason                  Message
  ----                  ------  ------                  -------
  AbleToScale           True    ReadyForNewScale        the last scale time was sufficiently old as to warrant a new scale
  ScalingActive         True    ValidMetricFound        the HPA was able to successfully calculate a replica count from pods metric http_requests
  ScalingLimited        False   DesiredWithinRange      the desired replica count is within the acceptable range
Events:

對於這個 HorizontalPodAutoscaler,你可以看到幾個健康狀態的條件。第一個是 `AbleToScale`,表示 HPA 是否能夠獲取和更新擴縮,以及是否有任何與回退相關的條件會阻止擴縮。第二個是 `ScalingActive`,表示 HPA 是否已啟用(即目標的副本數不為零)並且能夠計算所需的擴縮。當它為 `False` 時,通常表示獲取指標有問題。最後,最後一個條件 `ScalingLimited`,表示所需的擴縮被 HorizontalPodAutoscaler 的最大值或最小值限制。這表明你可能希望提高或降低 HorizontalPodAutoscaler 上的最小或最大副本數限制。

數量

HorizontalPodAutoscaler 和指標 API 中的所有指標都使用一種特殊的整數表示法指定,在 Kubernetes 中稱為 數量(quantity)。例如,數量 `10500m` 在十進位制表示中將寫為 `10.5`。指標 API 在可能的情況下將返回不帶字尾的整數,否則通常會返回毫單位的數量。這意味著你的指標值可能會在 `1` 和 `1500m` 之間波動,或者在十進位制表示中在 `1` 和 `1.5` 之間波動。

其他可能的情況

宣告式建立自動擴縮器

我們可以使用以下清單宣告性地建立 HorizontalPodAutoscaler,而不是使用 `kubectl autoscale` 命令命令式地建立它

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

然後,透過執行以下命令建立自動擴縮器

kubectl create -f https://k8s.io/examples/application/hpa/php-apache.yaml
horizontalpodautoscaler.autoscaling/php-apache created
最後修改時間:2024 年 7 月 6 日太平洋標準時間上午 11:31:澄清句子 (39e0fd6023)