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 从集群中的 kubelet 收集资源指标,并通过 Kubernetes API 暴露这些指标,使用 APIService 添加新的资源类型来表示指标读数。
要了解如何部署 Metrics Server,请参阅 metrics-server 文档。
如果您正在运行 Minikube,请运行以下命令以启用 metrics-server
minikube addons enable metrics-server
运行并暴露 php-apache 服务器
为了演示 HorizontalPodAutoscaler,您将首先启动一个 Deployment,该 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 autoscale 子命令,是 kubectl 的一部分,可以帮助您执行此操作。
您将很快运行一个命令,该命令创建一个 HorizontalPodAutoscaler,以维持由您在这些说明的第一个步骤中创建的 php-apache Deployment 控制的 Pod 之间的 1 到 10 个副本。
粗略地说,HPA 控制器 将增加和减少副本数量(通过更新 Deployment)以维持所有 Pod 的平均 CPU 利用率达到 50%。然后 Deployment 更新 ReplicaSet - 这是所有 Deployment 的工作方式 - 然后 ReplicaSet 根据对 .spec 的更改添加或删除 Pod。
由于每个 pod 通过 kubectl run 请求 200 毫核,这意味着 100 毫核的平均 CPU 使用量。有关算法详细信息,请参阅 算法详细信息。
创建 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 上求平均值,然后与目标值进行比较以确定副本计数。它们的工作方式与资源指标非常相似,除了它们仅支持 target 类型为 AverageValue。
Pod 指标使用如下指标块指定
type: Pods
pods:
metric:
name: packets-per-second
target:
type: AverageValue
averageValue: 1k
第二种替代指标类型是对象指标。这些指标描述同一命名空间中的不同对象,而不是描述 Pod。这些指标不一定从对象中获取;它们只是描述它。对象指标支持 target 类型为 Value 和 AverageValue。使用 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 消耗其请求 CPU 的大约 50%,每秒处理 1000 个数据包,并且主路由 Ingress 后面的所有 Pod 每秒处理总共 10000 个请求。
基于更具体的指标进行自动伸缩
许多指标管道允许您通过名称或一组称为标签的附加描述符来描述指标。对于所有非资源指标类型(Pod、对象和外部,如下所述),您可以指定一个附加的标签选择器,该选择器会传递给您的指标管道。例如,如果您收集一个名为 http_requests 的指标,并带有 verb 标签,您可以指定以下指标块,仅基于 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 个未处理的任务一个 worker。
- 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 中称为 数量 的特殊整数表示法。例如,数量 10500m 在十进制表示法中将写为 10.5。指标 API 将在可能的情况下返回没有后缀的整数,并且通常会以毫单位返回数量。这意味着您可能会看到您的指标值在 1 和 1500m 之间波动,或者在以十进制表示法书写时在 1 和 1.5 之间波动。
其他可能的情况
以声明方式创建自动伸缩器
与其使用 kubectl autoscale 命令以命令方式创建 HorizontalPodAutoscaler,不如我们可以使用以下清单以声明方式创建它
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