HorizontalPodAutoscaler使用示例
HorizontalPodAutoscaler(简称 HPA ) 自动更新工作负载资源(例如 Deployment 或者 StatefulSet), 目的是自动扩缩工作负载以满足需求。
水平扩缩意味着对增加的负载的响应是部署更多的 Pod。 这与 “垂直(Vertical)” 扩缩不同,对于 Kubernetes, 垂直扩缩意味着将更多资源(例如:内存或 CPU)分配给已经为工作负载运行的 Pod。
如果负载减少,并且 Pod 的数量高于配置的最小值, HorizontalPodAutoscaler 会指示工作负载资源(Deployment、StatefulSet 或其他类似资源)缩减。
本文档将引导你完成启用 HorizontalPodAutoscaler 以自动管理示例 Web 应用程序的扩缩的示例。 此示例工作负载是运行一些 PHP 代码的 Apache httpd。
前提准备
你的 Kubernetes 服务器版本必须不低于版本 1.23. 要获知版本信息,请 输入 kubectl version
.
按照本演练进行操作,你需要一个部署并配置了 Metrics Server 的集群。 Kubernetes Metrics Server 从集群中的 kubelets 收集资源指标, 并通过 Kubernetes API 公开这些指标, 使用 APIService 添加代表指标读数的新资源。
要了解如何部署 Metrics Server,请参阅 metrics-server 文档。
运行 php-apache 服务器并暴露服务
为了演示 HorizontalPodAutoscaler,你将首先启动一个 Deployment 用 hpa-example
镜像运行一个容器, 然后使用以下清单文件将其暴露为一个 服务(Service):
cat > php-apache.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
replicas: 1
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
#image: registry.k8s.io/hpa-example
image: uhub.service.ucloud.cn/996.icu/hpa-example:latest
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
EOF
创建
kubectl apply -f php-apache.yaml
查看,会创建名为 php-apache
的 deployment
和 service
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/php-apache-6d9d9dd4fb-5hmwb 1/1 Running 0 76s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/php-apache ClusterIP 10.96.106.148 <none> 80/TCP 76s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/php-apache 1/1 1 1 76s
NAME DESIRED CURRENT READY AGE
replicaset.apps/php-apache-6d9d9dd4fb 1 1 1 76s
创建 HorizontalPodAutoscaler
现在服务器正在运行,使用 kubectl
创建自动扩缩器。 kubectl autoscale
子命令是 kubectl
的一部分, 可以帮助你执行此操作。
你将很快运行一个创建 HorizontalPodAutoscaler 的命令, 该 HorizontalPodAutoscaler 维护由你在这些说明的第一步中创建的 php-apache Deployment 控制的 Pod 存在 1 到 10 个副本。
粗略地说,HPA 控制器将增加和减少副本的数量 (通过更新 Deployment)以保持所有 Pod 的平均 CPU 利用率为 50%。 Deployment 然后更新 ReplicaSet —— 这是所有 Deployment 在 Kubernetes 中工作方式的一部分 —— 然后 ReplicaSet 根据其 .spec
的更改添加或删除 Pod。
由于每个 Pod 通过 kubectl run
请求 200 milli-cores,这意味着平均 CPU 使用率为 100 milli-cores。 有关算法的更多详细信息, 请参阅算法详细信息。
创建 HorizontalPodAutoscaler:
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
查看hpa
# 可以使用 hpa 或 horizontalpodautoscaler 任何一个名字都可以
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 0%/50% 1 10 1 26s
请注意当前的 CPU 利用率是 0%,这是由于我们尚未发送任何请求到服务器 (TARGET
列显示了相应 Deployment 所控制的所有 Pod 的平均 CPU 利用率)。
增加负载
接下来,看看自动扩缩器如何对增加的负载做出反应。 为此,你将启动一个不同的 Pod 作为客户端。 客户端 Pod 中的容器在无限循环中运行,向 php-apache 服务发送查询。
# 在新的终端中运行
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"
一分钟时间左右之后,通过以下命令,我们可以看到 CPU 负载升高了;例如:
$ kubectl get hpa php-apache --watch
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 240%/50% 1 10 4 4m2s
此时查看pod,可以看到pod数量已经增多了
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
load-generator 1/1 Running 0 74s
php-apache-6d9d9dd4fb-2ldl9 1/1 Running 0 44s
php-apache-6d9d9dd4fb-5hmwb 1/1 Running 0 13m
php-apache-6d9d9dd4fb-9frdd 1/1 Running 0 44s
php-apache-6d9d9dd4fb-fspqt 1/1 Running 0 29s
php-apache-6d9d9dd4fb-nb8z9 1/1 Running 0 44s
查看deployment
$ kubectl get deployment php-apache
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 7/7 7 7 14m
查看hpa
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 44%/50% 1 10 7 5m34s
可以看到deployment中的副本数量和hpa中的副本数量相同
:::tip说明
有时最终副本的数量可能需要几分钟才能稳定下来。由于环境的差异, 不同环境中最终的副本数量可能与本示例中的数量不同。
:::
停止产生负载
在我们创建 busybox
容器的终端中,输入 <Ctrl> + C
来终止负载的产生。
持续查看hpa,可以看到一段时间后,由于我们手动停止了 busybox
容器,因此负载会降为0
$ kubectl get hpa php-apache --watch
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 240%/50% 1 10 4 4m2s
php-apache Deployment/php-apache 76%/50% 1 10 5 4m15s
php-apache Deployment/php-apache 57%/50% 1 10 5 4m30s
php-apache Deployment/php-apache 64%/50% 1 10 6 4m46s
php-apache Deployment/php-apache 57%/50% 1 10 6 5m1s
php-apache Deployment/php-apache 55%/50% 1 10 7 5m16s
php-apache Deployment/php-apache 44%/50% 1 10 7 5m31s
php-apache Deployment/php-apache 47%/50% 1 10 7 5m46s
php-apache Deployment/php-apache 47%/50% 1 10 7 6m1s
php-apache Deployment/php-apache 46%/50% 1 10 7 6m16s
php-apache Deployment/php-apache 42%/50% 1 10 7 6m31s
php-apache Deployment/php-apache 50%/50% 1 10 7 6m46s
php-apache Deployment/php-apache 10%/50% 1 10 7 7m1s
php-apache Deployment/php-apache 0%/50% 1 10 7 7m16s
查看deployment,一旦 CPU 利用率降至 0,HPA 会自动将副本数缩减为 1。
$ kubectl get deployment php-apache
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 1/1 1 1 24m
基于多项度量指标和自定义度量指标自动扩缩
利用 autoscaling/v2
API 版本,你可以在自动扩缩 php-apache 这个 Deployment 时使用其他度量指标。
首先,将 HorizontalPodAutoscaler 的 YAML 文件改为 autoscaling/v2
格式:
kubectl get hpa php-apache -o yaml > /tmp/hpa-v2.yaml
查看 /tmp/hpa-v2.yaml
$ cat /tmp/hpa-v2.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
creationTimestamp: "2022-12-03T14:53:06Z"
name: php-apache
namespace: test
resourceVersion: "81599"
uid: c0859860-36b7-49cc-bd28-c373200ab4b9
spec:
maxReplicas: 10
metrics:
- resource:
name: cpu
target:
averageUtilization: 50
type: Utilization
type: Resource
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
status:
conditions:
- lastTransitionTime: "2022-12-03T14:53:21Z"
message: recommended size matches current size
reason: ReadyForNewScale
status: "True"
type: AbleToScale
- lastTransitionTime: "2022-12-03T14:53:21Z"
message: the HPA was able to successfully calculate a replica count from cpu resource
utilization (percentage of request)
reason: ValidMetricFound
status: "True"
type: ScalingActive
- lastTransitionTime: "2022-12-03T15:05:07Z"
message: the desired replica count is less than the minimum replica count
reason: TooFewReplicas
status: "True"
type: ScalingLimited
currentMetrics:
- resource:
current:
averageUtilization: 0
averageValue: 1m
name: cpu
type: Resource
currentReplicas: 1
desiredReplicas: 1
lastScaleTime: "2022-12-03T15:05:07Z"
需要注意的是,targetCPUUtilizationPercentage
字段已经被名为 metrics
的数组所取代。 CPU 利用率这个度量指标是一个 resource metric(资源度量指标),因为它表示容器上指定资源的百分比。 除 CPU 外,你还可以指定其他资源度量指标。默认情况下,目前唯一支持的其他资源度量指标为内存。 只要 metrics.k8s.io
API 存在,这些资源度量指标就是可用的,并且他们不会在不同的 Kubernetes 集群中改变名称。
你还可以指定资源度量指标使用绝对数值,而不是百分比,你需要将 target.type
从 Utilization
替换成 AverageValue
,同时设置 target.averageValue
而非 target.averageUtilization
的值。
还有两种其他类型的度量指标,他们被认为是 custom metrics(自定义度量指标): 即 Pod 度量指标和 Object 度量指标。 这些度量指标可能具有特定于集群的名称,并且需要更高级的集群监控设置。
第一种可选的度量指标类型是 Pod 度量指标。这些指标从某一方面描述了 Pod, 在不同 Pod 之间进行平均,并通过与一个目标值比对来确定副本的数量。 它们的工作方式与资源度量指标非常相像,只是它们 仅 支持 target
类型为 AverageValue
。
Pod 度量指标通过如下代码块定义:
type: Pods
pods:
metric:
name: packets-per-second
target:
type: AverageValue
averageValue: 1k
第二种可选的度量指标类型是对象 (Object)度量指标。 这些度量指标用于描述在相同名字空间中的别的对象,而非 Pod。 请注意这些度量指标不一定来自某对象,它们仅用于描述这些对象。 对象度量指标支持的 target
类型包括 Value
和 AverageValue
。 如果是 Value
类型,target
值将直接与 API 返回的度量指标比较, 而对于 AverageValue
类型,API 返回的度量值将按照 Pod 数量拆分, 然后再与 target
值比较。 下面的 YAML 文件展示了一个表示 requests-per-second
的度量指标。
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
命令将上述 Horizontal Pod Autoscaler 的定义更改为:
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 个。