HorizontalPodAutoscaler 演练
HorizontalPodAutoscaler (简称 HPA) 会自动更新工作负载资源(例如 Deployment 或 StatefulSet),旨在自动扩缩工作负载以匹配需求。
水平扩缩意味着,为了应对增加的负载,将部署更多 Pod。这与**垂直**扩缩不同。在 Kubernetes 中,垂直扩缩意味着为工作负载中已运行的 Pod 分配更多资源(例如:内存或 CPU)。
如果负载减少,并且 Pod 数量高于配置的最小值,则 HorizontalPodAutoscaler 会指示工作负载资源(Deployment、StatefulSet 或其他类似资源)缩减。
本文档将通过一个示例,指导你如何启用 HorizontalPodAutoscaler 自动管理示例 Web 应用程序的扩缩。这个示例工作负载是运行一些 PHP 代码的 Apache httpd。
准备工作
你需要有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议在至少有两个不是控制平面主机的节点组成的集群上运行此教程。如果你还没有集群,可以使用 minikube 创建一个,或者使用以下 Kubernetes 演练场之一
你的 Kubernetes 服务器版本必须在 1.23 或更高。要检查版本,请输入 kubectl version
。
要完成本演练,你还需要使用一个已部署和配置 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 命名空间的托管服务的指标。在 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