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)