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 收集资源指标,并使用 APIService 通过 Kubernetes API 暴露这些指标,以添加表示指标读数的新资源类型。
要了解如何部署 Metrics Server,请参阅 metrics-server 文档。
如果你正在运行 Minikube,请运行以下命令启用 metrics-server
minikube addons enable metrics-server
运行并暴露 php-apache 服务器
为了演示 HorizontalPodAutoscaler,你将首先启动一个使用 hpa-example
镜像运行容器的 Deployment,并使用以下清单将其暴露为 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,它将维护你在本说明第一步中创建的 php-apache Deployment 控制的 Pod 的 1 到 10 个副本。
粗略地说,HPA 控制器 将增加或减少副本数量(通过更新 Deployment),以将所有 Pod 的平均 CPU 利用率维持在 50%。然后,Deployment 会更新 ReplicaSet(这是所有 Deployment 在 Kubernetes 中的工作方式的一部分),接着 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
列显示对应 deployment 控制的所有 Pod 的平均值)。
增加负载
接下来,看看自动扩缩器如何对增加的负载作出反应。为此,你将启动另一个 Pod 作为客户端。客户端 Pod 中的容器无限循环运行,向 php-apache service 发送查询。
# 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。这些指标不一定从对象中获取;它们只是描述对象。对象指标支持 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 消耗其请求 CPU 的大约 50%,每秒处理 1000 个数据包,并且主路由 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 中称为数量。例如,数量 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