当前位置: 首页 > news >正文

9.HPA与VPA

HPA 与 VPA

​ 在前面的学习中我们使用了一个 kubectl scale 命令可以来实现 Pod 的扩缩容功能,但是这个是完全手动操作的,要应对线上的各种复杂情况,我们需要能够做到自动化去感知业务,来自动进行扩缩容。为此,Kubernetes 也为我们提供了这样的一个资源对象:Horizontal Pod Autoscaling(Pod 水平自动伸缩),简称 HPA,HPA 通过监控分析一些控制器控制的所有 Pod 的负载变化情况来确定是否需要调整 Pod 的副本数量,在 Kubernetes 控制平面内运行的 HPA控制器会定期调整其目标(例如:Deployment)的所需规模,以匹配观察到的指标,例如,平均 CPU 利用率、平均内存利用率或你指定的任何其他自定义指标。

​ 对于按 Pod 统计的资源指标(如 CPU),控制器从资源指标 API 中获取每一个 HorizontalPodAutoscaler 指定的Pod 的 metrics 值,如果设置了目标使用率,控制器获取每个 Pod 中的容器资源使用情况,并计算资源使用率。如果设置了 target 值,将直接使用原始数据(不再计算百分比)。接下来,控制器根据平均的资源使用率或原始值计算出扩缩的比例,进而计算出目标副本数。

需要注意的是,如果 Pod 某些容器不支持资源采集,那么控制器将不会使用该 Pod 的 CPU 使用率。

​ 如果 Pod 使用自定义指示,控制器机制与资源指标类似,区别在于自定义指标只使用原始值,而不是使用率。如果 Pod 使用对象指标和外部指标(每个指标描述一个对象信息)。 这个指标将直接根据目标设定值相比较,并生成一个上面提到的扩缩比例。 在 autoscaling/v2beta2 版本 API 中,这个指标也可以根据 Pod 数量平分后再计算。

​ 对 Metrics API 的支持解释了这些不同 API 的稳定性保证和支持状态。

​ HorizontalPodAutoscaler 控制器访问支持扩缩的相应工作负载资源(例如:Deployment 和 StatefulSet)。这些资源每个都有一个名为 scale 的子资源,该接口允许你动态设置副本的数量并检查它们的每个当前状态。

Metrics Server(监控指标服务器)

​ Kubernetes 将水平 Pod 自动扩缩实现为一个间歇运行的控制回路(不是一个连续的过程)。间隔由 kubecontroller-manager--horizontal-pod-autoscaler-sync-period 参数设置(默认间隔为 15 秒)。

​ 在每个时间段内,控制器都会根据每个 HorizontalPodAutoscaler 对象中指定的指标查询资源利用率。控制器找到由**scaleTargetRef **定义的目标资源,然后根据目标资源的 .spec.selector 标签选择 Pod, 并从资源 MetricsAPI(针对每个 Pod 的资源指标)或自定义指标获取指标 API(适用于所有其他指标)。

​ 而 HPA 通常会从聚合 API (metrics.k8s.io、custom.metrics.k8s.io 或 external.metrics.k8s.io)获取指标数据。 metrics.k8s.io API 通常由名为 Metrics Server 的插件提供,需要单独启动。Metrics Server可以通过标准的 Kubernetes API 把监控数据暴露出来,有了 Metrics Server 之后,我们就完全可以通过标准的Kubernetes API 来访问我们想要获取的监控数据了:

https://10.96.0.1/apis/metrics.k8s.io/v1beta1/namespaces/<namespace-name>/pods/<podname>

​ 比如当我们访问上面的 API 的时候,我们就可以获取到该 Pod 的资源数据,这些数据其实是来自于 kubelet 的**Summary API **采集而来的。不过需要说明的是我们这里可以通过标准的 API 来获取资源监控数据,并不是因为Metrics Server 就是 APIServer 的一部分,而是通过 Kubernetes 提供的 Aggregator 汇聚插件来实现的,是独立于 APIServer 之外运行的。

聚合 API

Aggregator 允许开发人员编写一个自己的服务,把这个服务注册到 Kubernetes 的 APIServer 里面去,这样我们就可以像原生的 APIServer 提供的 API 使用自己的 API 了,我们把自己的服务运行在 Kubernetes 集群里面,然后Kubernetes 的 Aggregator 通过 Service 名称就可以转发到我们自己写的 Service 里面去了。这样这个聚合层就带来了很多好处:

  • 增加了 API 的扩展性,开发人员可以编写自己的 API 服务来暴露他们想要的 API。
  • 丰富了 API,核心 kubernetes 团队阻止了很多新的 API 提案,通过允许开发人员将他们的 API 作为单独的服务公开,这样就无须社区繁杂的审查了。
  • 开发分阶段实验性 API,新的 API 可以在单独的聚合服务中开发,当它稳定之后,在合并会 APIServer 就很容易了。
  • 确保新 API 遵循 Kubernetes 约定,如果没有这里提出的机制,社区成员可能会被迫推出自己的东西,这样很可能造成社区成员和社区约定不一致。

安装

​ 所以现在我们要使用 HPA,就需要在集群中安装 Metrics Server 服务,要安装 Metrics Server 就需要开启Aggregator,因为 Metrics Server 就是通过该代理进行扩展的,不过我们集群是通过 Kubeadm 搭建的,默认已经开启了,如果是二进制方式安装的集群,需要单独配置 kube-apsierver 添加如下所示的参数:

--requestheader-client-ca-file=<path to aggregator CA cert>
--requestheader-allowed-names=aggregator
--requestheader-extra-headers-prefix=X-Remote-Extra-
--requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User
--proxy-client-cert-file=<path to aggregator proxy cert>
--proxy-client-key-file=<path to aggregator proxy key>

验证是否开启 Aggregator

ubuntu@ubuntu:~/example/hpa_vpa$ kubectl get apiservices
NAME                                SERVICE   AVAILABLE   AGE
v1.                                 Local     True        8d
v1.admissionregistration.k8s.io     Local     True        8d
v1.apiextensions.k8s.io             Local     True        8d
v1.apps                             Local     True        8d
v1.authentication.k8s.io            Local     True        8d
v1.authorization.k8s.io             Local     True        8d
v1.autoscaling                      Local     True        8d
v1.batch                            Local     True        8d
v1.certificates.k8s.io              Local     True        8d
v1.configuration.konghq.com         Local     True        8d
v1.coordination.k8s.io              Local     True        8d
v1.discovery.k8s.io                 Local     True        8d
v1.events.k8s.io                    Local     True        8d
v1.flowcontrol.apiserver.k8s.io     Local     True        8d
v1.networking.k8s.io                Local     True        8d
v1.node.k8s.io                      Local     True        8d
v1.policy                           Local     True        8d
v1.rbac.authorization.k8s.io        Local     True        8d
v1.scheduling.k8s.io                Local     True        8d
v1.storage.k8s.io                   Local     True        8d
v1alpha1.configuration.konghq.com   Local     True        8d
v1beta1.configuration.konghq.com    Local     True        8d
v2.autoscaling                      Local     True        8d
# AVAILABLE=True 表示对应的 API 已经可用。

如果 kube-proxy 没有和 APIServer 运行在同一台主机上,那么需要确保启用了如下 kube-apsierver 的参数:

--enable-aggregator-routing=true

Aggregator 聚合层启动完成后,就可以来安装 Metrics Server 了,我们可以获取该仓库的官方安装资源清单:

# 下载前去仓库看看兼容版本
sudo wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.8.0/components.yaml
# 运行
ubuntu@ubuntu:~$ kubectl apply -f ./components.yaml 
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
service/metrics-server created
deployment.apps/metrics-server created
ubuntu@ubuntu:~$ kubectl get pods -n kube-system -l k8s-app=metrics-server
NAME                              READY   STATUS    RESTARTS   AGE
metrics-server-867d48dc9c-j9b9b   0/1     Running   0          62s
# 查看日志
ubuntu@ubuntu:~$ kubectl logs -f metrics-server-867d48dc9c-j9b9b -n kube-system
...
I0912 07:13:21.571900       1 server.go:192] "Failed probe" probe="metric-storage-ready" err="no metrics to serve"
I0912 07:13:31.573874       1 server.go:192] "Failed probe" probe="metric-storage-ready" err="no metrics to serve"
E0912 07:13:35.422208       1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.236.103:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.236.103 because it doesn't contain any IP SANs" node="node2"
E0912 07:13:35.422812       1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.236.101:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.236.101 because it doesn't contain any IP SANs" node="master"
E0912 07:13:35.437967       1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.236.102:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.236.102 because it doesn't contain any IP SANs" node="node1"
...

​ 因为部署集群的时候,CA 证书并没有把各个节点的 IP 签上去,所以这里 Metrics Server 通过 IP 去请求时,提示签的证书没有对应的 IP(错误:x509: cannot validate certificate for xxxx because it doesn'tcontain any IP SANs),我们可以添加一个 --kubelet-insecure-tls 参数跳过证书校验:

直接禁用证书校验

    spec:containers:- args:- --cert-dir=/tmp- --secure-port=10250- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname- --kubelet-use-node-status-port- --metric-resolution=15s- --kubelet-insecure-tls # 禁用证书校验

重启验证

ubuntu@ubuntu:~$ kubectl apply -f ./components.yaml 
serviceaccount/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
service/metrics-server created
deployment.apps/metrics-server created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
ubuntu@ubuntu:~$ kubectl get pods -n kube-system -l k8s-app=metrics-server
NAME                              READY   STATUS    RESTARTS   AGE
metrics-server-56fb9549f4-sgljk   0/1     Running   0          10s
ubuntu@ubuntu:~$ kubectl logs -f metrics-server-56fb9549f4-sgljk -n kube-system
I0912 07:31:21.701379       1 serving.go:380] Generated self-signed cert (/tmp/apiserver.crt, /tmp/apiserver.key)
I0912 07:31:22.029297       1 handler.go:288] Adding GroupVersion metrics.k8s.io v1beta1 to ResourceManager
I0912 07:31:22.134555       1 requestheader_controller.go:180] Starting RequestHeaderAuthRequestController
I0912 07:31:22.134563       1 configmap_cafile_content.go:205] "Starting controller" name="client-ca::kube-system::extension-apiserver-authentication::client-ca-file"
I0912 07:31:22.134586       1 shared_informer.go:350] "Waiting for caches to sync" controller="client-ca::kube-system::extension-apiserver-authentication::client-ca-file"
I0912 07:31:22.134578       1 shared_informer.go:350] "Waiting for caches to sync" controller="RequestHeaderAuthRequestController"
I0912 07:31:22.134715       1 configmap_cafile_content.go:205] "Starting controller" name="client-ca::kube-system::extension-apiserver-authentication::requestheader-client-ca-file"
I0912 07:31:22.134723       1 shared_informer.go:350] "Waiting for caches to sync" controller="client-ca::kube-system::extension-apiserver-authentication::requestheader-client-ca-file"
I0912 07:31:22.134871       1 dynamic_serving_content.go:135] "Starting controller" name="serving-cert::/tmp/apiserver.crt::/tmp/apiserver.key"
I0912 07:31:22.134914       1 secure_serving.go:211] Serving securely on [::]:10250
I0912 07:31:22.134944       1 tlsconfig.go:243] "Starting DynamicServingCertificateController"
I0912 07:31:22.235335       1 shared_informer.go:357] "Caches are synced" controller="client-ca::kube-system::extension-apiserver-authentication::requestheader-client-ca-file"
I0912 07:31:22.235485       1 shared_informer.go:357] "Caches are synced" controller="client-ca::kube-system::extension-apiserver-authentication::client-ca-file"
I0912 07:31:22.235463       1 shared_informer.go:357] "Caches are synced" controller="RequestHeaderAuthRequestController"
# 查看服务
ubuntu@ubuntu:~kubectl get apiservice | grep metricscs
v1beta1.metrics.k8s.io              kube-system/metrics-server   True        105s
# 尝试是否能获取到节点信息
ubuntu@ubuntu:~$ kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
{"kind":"NodeMetricsList","apiVersion":"metrics.k8s.io/v1beta1","metadata":{},"items":[{"metadata":{"name":"master","creationTimestamp":"2025-09-12T07:33:38Z","labels":{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"master","kubernetes.io/os":"linux","node-role.kubernetes.io/control-plane":"","node.kubernetes.io/exclude-from-external-load-balancers":""}},"timestamp":"2025-09-12T07:33:30Z","window":"10.021s","usage":{"cpu":"69354355n","memory":"1408284Ki"}},{"metadata":{"name":"node1","creationTimestamp":"2025-09-12T07:33:38Z","labels":{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"node1","kubernetes.io/os":"linux"}},"timestamp":"2025-09-12T07:33:35Z","window":"20.013s","usage":{"cpu":"11092789n","memory":"622148Ki"}},{"metadata":{"name":"node2","creationTimestamp":"2025-09-12T07:33:38Z","labels":{"beta.kubernetes.io/arch":"amd64","beta.kubernetes.io/os":"linux","kubernetes.io/arch":"amd64","kubernetes.io/hostname":"node2","kubernetes.io/os":"linux"}},"timestamp":"2025-09-12T07:33:31Z","window":"10.009s","usage":{"cpu":"15286242n","memory":"654480Ki"}}]}
# 获取物理资源信息
ubuntu@ubuntu:~$ kubectl top nodes
NAME     CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)   
master   59m          1%       1375Mi          36%         
node1    13m          0%       607Mi           16%         
node2    14m          0%       639Mi           16%         

HPA

​ 现在我们用 Deployment 来创建一个 Nginx Pod,然后利用 HPA 来进行自动扩缩容。资源清单如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:name: hpa-demo
spec:replicas: 1selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginxports:- containerPort: 80
--- 
# HPA设置
apiVersion: autoscaling/v2   # 使用 HPA v2 API,支持更多 metrics 类型
kind: HorizontalPodAutoscaler
metadata:name: hpa-demo           # HPA 对象的名称
spec:scaleTargetRef:          # 指定 HPA 作用的目标对象apiVersion: apps/v1    # 目标对象的 API 版本kind: Deployment       # 目标对象类型,这里是 Deploymentname: hpa-demo         # 目标 Deployment 的名字minReplicas: 1           # 最小副本数maxReplicas: 10          # 最大副本数metrics:                 # 定义触发自动扩缩容的指标- type: Resource       # 指标类型,这里是资源类型(CPU 或内存)resource:name: cpu          # 指标名称:CPUtarget:type: Utilization           # 目标类型:平均使用率(百分比)averageUtilization: 10      # 目标 CPU 使用率为 10%,超过则扩容,低于则缩容

验证:

# 查看hpa信息
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl describe hpa hpa-demo
Name:                                                  hpa-demo
Namespace:                                             default
Labels:                                                <none>
Annotations:                                           <none>
CreationTimestamp:                                     Fri, 12 Sep 2025 07:55:27 +0000
Reference:                                             Deployment/hpa-demo
Metrics:                                               ( current / target )resource cpu on pods  (as a percentage of request):  <unknown> / 10%
Min replicas:                                          1
Max replicas:                                          10
Deployment pods:                                       1 current / 0 desired
Conditions:Type           Status  Reason                   Message----           ------  ------                   -------AbleToScale    True    SucceededGetScale        the HPA controller was able to get the target's current scaleScalingActive  False   FailedGetResourceMetric  the HPA was unable to compute the replica count: failed to get cpu utilization: missing request for cpu in container nginx of Pod hpa-demo-86c57bc6b8-9vhdb
Events:Type     Reason                        Age               From                       Message----     ------                        ----              ----                       -------Warning  FailedGetResourceMetric       0s (x3 over 30s)  horizontal-pod-autoscaler  failed to get cpu utilization: missing request for cpu in container nginx of Pod hpa-demo-86c57bc6b8-9vhdbWarning  FailedComputeMetricsReplicas  0s (x3 over 30s)  horizontal-pod-autoscaler  invalid metrics (1 invalid out of 1), first error is: failed to get cpu resource metric value: failed to get cpu utilization: missing request for cpu in container nginx of Pod hpa-demo-86c57bc6b8-9vhdb
ubuntu@ubuntu:~/example/hpa_vpa$ 
# 我们可以看到上面的事件信息里面出现了 failed to get cpu utilization: missing request for cpu 这样的
# 错误信息。这是因为我们上面创建的 Pod 对象没有添加 request 资源声明,这样导致 HPA 读取不到 CPU 指标信息,
# 所以如果要想让 HPA 生效,对应的 Pod 资源必须添加 requests 资源声明,更新我们的资源清单文件:
# resources:
#   requests:
#     memory: 50Mi
#     cpu: 50m
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl delete -f ./hpa-demo.yaml 
deployment.apps "hpa-demo" deleted
horizontalpodautoscaler.autoscaling "hpa-demo" deleted
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl apply -f ./hpa-demo.yaml 
deployment.apps/hpa-demo created
horizontalpodautoscaler.autoscaling/hpa-demo created
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl describe hpa hpa-demo
Name:                                                  hpa-demo
Namespace:                                             default
Labels:                                                <none>
Annotations:                                           <none>
CreationTimestamp:                                     Fri, 12 Sep 2025 07:58:12 +0000
Reference:                                             Deployment/hpa-demo
Metrics:                                               ( current / target )resource cpu on pods  (as a percentage of request):  <unknown> / 10%
Min replicas:                                          1
Max replicas:                                          10
Deployment pods:                                       0 current / 0 desired
Events:                                                <none>

CPU

进行压测:

# 一个终端执行
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl get pods -l app=nginx -w -o wide
NAME                        READY   STATUS    RESTARTS   AGE    IP             NODE    NOMINATED NODE   READINESS GATES
hpa-demo-75f9486867-pcgbt   1/1     Running   0          113s   10.244.2.100   node1   <none>           <none>
# 一个终端执行
ubuntu@ubuntu:~$ kubectl get hpa -w
NAME       REFERENCE             TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-demo   cpu: 0%/10%   1         10        1          2m46s
# 另一个终端执行
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl run -it --image busybox test-hpa --restart=Never --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # while true; do wget -q -O- http://10.244.2.100; done# 观察结果
# pod 侧
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl get pods -l app=nginx -w -o wide
NAME                        READY   STATUS    RESTARTS   AGE    IP             NODE    NOMINATED NODE   READ
hpa-demo-75f9486867-pcgbt   1/1     Running   0          113s   10.244.2.100   node1   <none>           <non
hpa-demo-75f9486867-b84zn   0/1     Pending   0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-b84zn   0/1     Pending   0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-pd9sd   0/1     Pending   0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-7s5j9   0/1     Pending   0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-pd9sd   0/1     Pending   0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-7s5j9   0/1     Pending   0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-b84zn   0/1     ContainerCreating   0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-pd9sd   0/1     ContainerCreating   0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-7s5j9   0/1     ContainerCreating   0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-b84zn   1/1     Running             0          4s     10.244.1.90    node2    <none>           <none>
hpa-demo-75f9486867-pd9sd   1/1     Running             0          4s     10.244.2.102   node1    <none>           <none>
hpa-demo-75f9486867-7s5j9   1/1     Running             0          6s     10.244.1.91    node2    <none>           <none>
hpa-demo-75f9486867-xwkl8   0/1     Pending             0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-xwkl8   0/1     Pending             0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-9786g   0/1     Pending             0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-lpvh8   0/1     Pending             0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-wkgbt   0/1     Pending             0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-lpvh8   0/1     Pending             0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-9786g   0/1     Pending             0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-xwkl8   0/1     ContainerCreating   0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-wkgbt   0/1     Pending             0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-9786g   0/1     ContainerCreating   0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-wkgbt   0/1     ContainerCreating   0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-lpvh8   0/1     ContainerCreating   0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-xwkl8   1/1     Running             0          4s     10.244.1.92    node2    <none>           <none>
hpa-demo-75f9486867-9786g   1/1     Running             0          4s     10.244.2.103   node1    <none>           <none>
hpa-demo-75f9486867-lpvh8   1/1     Running             0          6s     10.244.2.104   node1    <none>           <none>
hpa-demo-75f9486867-wkgbt   1/1     Running             0          7s     10.244.1.93    node2    <none>           <none>
hpa-demo-75f9486867-7pbvv   0/1     Pending             0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-7pbvv   0/1     Pending             0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-q9jbq   0/1     Pending             0          0s     <none>         <none>   <none>           <none>
hpa-demo-75f9486867-q9jbq   0/1     Pending             0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-7pbvv   0/1     ContainerCreating   0          0s     <none>         node1    <none>           <none>
hpa-demo-75f9486867-q9jbq   0/1     ContainerCreating   0          0s     <none>         node2    <none>           <none>
hpa-demo-75f9486867-q9jbq   1/1     Running             0          3s     10.244.1.94    node2    <none>           <none>
hpa-demo-75f9486867-7pbvv   1/1     Running             0          4s     10.244.2.105   node1    <none>           <none># hpa侧
ubuntu@ubuntu:~$ kubectl get hpa -w
NAME       REFERENCE             TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-demo   cpu: 0%/10%   1         10        1          2m46s
hpa-demo   Deployment/hpa-demo   cpu: 158%/10%   1         10        1          4m15s
hpa-demo   Deployment/hpa-demo   cpu: 180%/10%   1         10        4          4m30s
hpa-demo   Deployment/hpa-demo   cpu: 44%/10%    1         10        8          4m45s
hpa-demo   Deployment/hpa-demo   cpu: 35%/10%    1         10        10         5m
hpa-demo   Deployment/hpa-demo   cpu: 20%/10%    1         10        10         5m15s# 由于副本数量最大 10 所以数量就一直是这样了# 取消循环 观察减少副本的行为
ubuntu@ubuntu:~$ kubectl get hpa -w
NAME       REFERENCE             TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-demo   cpu: 0%/10%   1         10        1          2m46s
hpa-demo   Deployment/hpa-demo   cpu: 158%/10%   1         10        1          4m15s
hpa-demo   Deployment/hpa-demo   cpu: 180%/10%   1         10        4          4m30s
hpa-demo   Deployment/hpa-demo   cpu: 44%/10%    1         10        8          4m45s
hpa-demo   Deployment/hpa-demo   cpu: 35%/10%    1         10        10         5m
hpa-demo   Deployment/hpa-demo   cpu: 20%/10%    1         10        10         5m15s
hpa-demo   Deployment/hpa-demo   cpu: 18%/10%    1         10        10         5m30s
hpa-demo   Deployment/hpa-demo   cpu: 17%/10%    1         10        10         6m15s
hpa-demo   Deployment/hpa-demo   cpu: 10%/10%    1         10        10         6m30s
hpa-demo   Deployment/hpa-demo   cpu: 0%/10%     1         10        10         6m45s
hpa-demo   Deployment/hpa-demo   cpu: 0%/10%     1         10        10         11m
hpa-demo   Deployment/hpa-demo   cpu: 0%/10%     1         10        10         11m
hpa-demo   Deployment/hpa-demo   cpu: 0%/10%     1         10        1          11m# 这里可以看到副本数量 不会立即减少大约等5min 后 会缩容

从 Kubernetes v1.12 版本开始我们可以通过设置 kube-controller-manager 组件的!"horizontalpod-autoscaler-downscale-stabilization 参数来设置一个持续时间,用于指定在当前操作完成后,HPA 必须等待多长时间才能执行另一次缩放操作。默认为 5 分钟,也就是默认需要等待 5 分钟后才会开始自动缩放。

内存

​ 要使用基于内存或者自定义指标进行扩缩容(现在的版本都必须依赖 metrics-server 这个项目)。现在我们再用Deployment 来创建一个 Nginx Pod,然后利用 HPA 来进行自动扩缩容。资源清单如下所示:

# hpa-mem-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: hpa-mem-demo       # Deployment 名称
spec:replicas: 1              # 初始副本数selector:matchLabels:app: nginx           # 用于匹配 Pod 的标签template:metadata:labels:app: nginx         # Pod 模板的标签,必须和 selector 匹配spec:volumes:- name: increase-mem-script    # 定义卷名configMap:name: increase-mem-config  # 使用的 ConfigMap 名称,挂载脚本或配置containers:- name: nginximage: nginx                  # 使用 nginx 镜像ports:- containerPort: 80        # 容器暴露端口volumeMounts:- name: increase-mem-scriptmountPath: /etc/script   # 将 ConfigMap 挂载到容器路径resources:requests:                   # Pod 启动时保证的资源请求memory: 50Micpu: 50msecurityContext:privileged: true           # 以特权模式运行容器(有高权限访问能力)

​ 这里和前面普通的应用有一些区别,我们将一个名为 increase-mem-config 的 ConfigMap 资源对象挂载到了容器中,该配置文件是用于后面增加容器内存占用的脚本,配置文件如下所示(这里就是弄了个资源对象,放了个sh脚本):

# increase-mem-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:name: increase-mem-config       # ConfigMap 名称,用于存放脚本
data:increase-mem.sh: |              # 脚本文件名称,使用多行字符串 (|) 表示#!/bin/bash# 创建临时目录,用于挂载 tmpfsmkdir /tmp/memory# 挂载一个大小为 40M 的 tmpfs 文件系统mount -t tmpfs -o size=40M tmpfs /tmp/memory# 在 tmpfs 中创建一个大文件,模拟内存占用dd if=/dev/zero of=/tmp/memory/block# 保持占用 60 秒sleep 60# 删除临时文件rm /tmp/memory/block# 卸载 tmpfsumount /tmp/memory# 删除临时目录rmdir /tmp/memory

​ 然后需要创建一个基于内存的 HPA 资源对象:

# hpa-mem.yaml
apiVersion: autoscaling/v2       # HPA 使用 v2 API,支持多种指标类型
kind: HorizontalPodAutoscaler
metadata:name: hpa-mem-demo             # HPA 对象名称namespace: default             # 所属命名空间
spec:scaleTargetRef:                # 指定 HPA 作用的目标对象apiVersion: apps/v1          # 目标对象的 API 版本kind: Deployment             # 目标对象类型name: hpa-mem-demo           # 目标 Deployment 名称minReplicas: 1                 # 最小副本数maxReplicas: 5                 # 最大副本数metrics:                        # 指定触发扩缩容的指标- type: Resource             # 指标类型为资源(CPU/内存)resource:name: memory             # 指标名称:内存target:type: Utilization      # 指标类型:平均使用率百分比averageUtilization: 30 # 目标内存使用率 30%,高于会扩容,低于会缩容

验证:

ubuntu@ubuntu:~/example/hpa_vpa$ kubectl apply -f ./hpa-mem-demo.yaml 
deployment.apps/hpa-mem-demo created
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl apply -f ./increase-mem-cm.yaml 
configmap/increase-mem-config created
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl apply -f ./hpa-mem.yaml 
horizontalpodautoscaler.autoscaling/hpa-mem-demo created
# 查看资源
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl get pods -A -o wide
NAMESPACE      NAME                              READY   STATUS    RESTARTS         AGE     IP                NODE     NOMINATED NODE   READINESS GATES
default        hpa-mem-demo-77c8b5c447-k2dq4     1/1     Running   0                5m17s   10.244.2.106      node1    <none>           <none>
# 开始压测,一个观察hpv
ubuntu@ubuntu:~$  kubectl get hpa -w
NAME           REFERENCE                 TARGETS          MINPODS   MAXPODS   REPLICAS   AGE
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 9%/30%   1         5         1          4m18s
# 一个压测
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl exec -it hpa-mem-demo-77c8b5c447-k2dq4 -- /bin/bash
root@hpa-mem-demo-77c8b5c447-k2dq4:/# ls
bin   dev		   docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc			 lib   media  opt  root  sbin  sys  usr
root@hpa-mem-demo-77c8b5c447-k2dq4:/# cd /etc/script/
root@hpa-mem-demo-77c8b5c447-k2dq4:/etc/script# ls
increase-mem.sh
root@hpa-mem-demo-77c8b5c447-k2dq4:/etc/script# source ./increase-mem.sh 
dd: writing to '/tmp/memory/block': No space left on device
81921+0 records in
81920+0 records out
41943040 bytes (42 MB, 40 MiB) copied, 0.129849 s, 323 MB/s
# 观察结果
ubuntu@ubuntu:~$  kubectl get hpa -w
NAME           REFERENCE                 TARGETS          MINPODS   MAXPODS   REPLICAS   AGE
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 9%/30%   1         5         1          4m18s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 13%/30%   1         5         1          6m16s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 13%/30%   1         5         1          6m31s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 13%/30%   1         5         1          6m46s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 95%/30%   1         5         1          7m16s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 95%/30%   1         5         4          7m31s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 30%/30%   1         5         4          7m46s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 10%/30%   1         5         4          9m31s
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 10%/30%   1         5         4          13m
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 11%/30%   1         5         2          13m
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 11%/30%   1         5         2          18m
hpa-mem-demo   Deployment/hpa-mem-demo   memory: 14%/30%   1         5         1          18m

可以看到内存使用已经超过了我们设定的 30% 这个阈值了,HPA 资源对象也已经触发了自动扩容,变成了 4 个副本了

扩缩容行为

​ 如果使用 v2 版本的 HPA API,我们还可以使用 behavior 字段来配置单独的放大和缩小行为,可以通过在 behavior 字段下设置 scaleUp 和/或 scaleDown 来指定这些行为。你可以指定一个稳定窗口,以防止扩缩目标的副本计数发生波动,扩缩策略还允许你在扩缩时控制副本的变化率。

扩缩策略(Behavior)

HPA 可以通过 behavior 字段自定义扩缩容策略。

  • 可以同时指定多个策略,当存在多个策略时,默认会选择 允许最大更改量的策略
  • 每个策略包含以下参数:
    • type:策略类型(PodsPercent
    • value:允许在指定时间内改变的副本数量或百分比
    • periodSeconds:时间窗口长度(秒),在该时间内允许的变更量

缩容策略示例(scaleDown)

behavior:scaleDown:policies:- type: Podsvalue: 4periodSeconds: 60- type: Percentvalue: 10periodSeconds: 60

说明:

  • 第一条策略:在 60 秒内最多缩容 4 个 Pod
  • 第二条策略:在 60 秒内最多缩容当前副本数的 10%
  • 策略选择逻辑
    • 默认选择允许最大更改量的策略
    • 当副本数 > 40 时,第二条策略生效
    • 当副本数 ≤ 40 时,第一条策略生效

示例说明:

  • 当前副本数为 80,目标副本数为 10:
    1. 第一轮迭代:应用第二条策略(10%),减少 8 个 Pod → 剩余 72 个
    2. 下一轮迭代:10% = 7.2,向上取整 8,继续缩容
    3. 当副本数 ≤ 40 时,切换到第一条策略,每次最多缩容 4 个 Pod

稳定窗口(Stabilization Window)

  • 当指标波动频繁时,稳定窗口用于限制副本数量的波动,避免频繁扩缩容导致不必要的 Pod 重建。
  • HPA 会查看 过去指定时间窗口内的期望状态,并选择最大值或最小值来决定缩容或扩容。

缩容稳定窗口示例

behavior:scaleDown:stabilizationWindowSeconds: 300

说明:

  • 过去 5 分钟内计算出的期望副本数都会被考虑
  • 仅适用于缩容,扩容没有稳定窗口,指标达到目标时立即扩容
  • 避免频繁删除 Pod,再触发重新创建

默认行为

  • 不需要指定所有字段,只需自定义需要的部分
  • HPA 会将自定义值与默认值合并
  • 默认行为:
    • 扩容:立即扩容,按指标计算增加副本
    • 缩容:采用 horizontal-pod-autoscaler-downscale-stabilization 参数值(默认 300 秒)限制缩容速度

扩容策略示例

behavior:scaleUp:policies:- type: Podsvalue: 4periodSeconds: 15- type: Percentvalue: 100periodSeconds: 15

说明:

  • 每 15 秒增加最多 4 个 Pod 或最多 100% 当前副本数
  • 扩容没有稳定窗口,目标会尽快达到 HPA 计算的期望状态

总结

缩容:有稳定窗口,避免频繁缩容;可以通过多条策略控制最大缩容量

扩容:无稳定窗口,目标指标达到时立即扩容;可通过策略控制最大扩容量

策略组合逻辑:当同时存在 Pods 与 Percent 策略,默认选择允许更大幅度更改的策略

VPA

​ VPA 全称 VerticalPodAutoscaler,即 Pod 的纵向扩缩容,和 HPA 是相对应的,其根据容器资源使用率自动设置CPU 和内存的 requests 及 limit,从而允许在节点上进行适当的调度,以便为每个 Pod 提供适当的资源。它既可以缩小过度请求资源的容器,也可以根据其使用情况随时提升资源不足的容量。我们可以通过 VPA 来自动资源配置提升管理效率、提高集群资源利用率。

​ VPA 主要由三个组件组成,分别为 recommender、updater、admission-controller

  • recommender:引入 VerticalPodAutoscaler 对象,其由 Pod 的标签选择器、资源策略(控制 VPA 如何计算资源)、更新策略(控制如何将更改应用于 Pod)和推荐的 Pod 资源组成,其根据 metric-server 获取到的容器指标并观测 OOM 事件,计算推荐指标,最终更新 VerticalPodAutoscaler 对象
  • updater:其是负责 Pod 更新的组件。如果 Pod 在 Auto 模式下使用 VPA,则 Updater 可以决定使用推荐器资源对其进行更新。这只是通过驱逐 Pod 以便使用新资源重新创建它来实现的。简单来说,其是根据 pod 的request 中设置的指标和 recommend 计算的推荐指标,在一定条件下驱逐 pod,
  • admission-controller:这是一个 webhook 组件,所有 Pod 创建请求都通过 VPA AdmissionController,如果 Pod 与 VerticalPodAutoscaler 对象匹配,把 recommend 计算出的指标应用到 pod 的request 和 limit,如果 Recommender 不可用,它会回退到 VPA 对象中缓存的推荐。

使用 VPA 主要会用到两个资源:VerticalPodAutoscaler 、VerticalPodAutoscalerCheckpoint

  • VerticalPodAutoscaler:该资源由用户创建,用于设置纵向扩容的目标对象和存储 recommend 组件计算出的推荐指标。
  • VerticalPodAutoscalerCheckpoint:该资源由 recommend 组件创建和维护,用于存储指标相关信息,一个vpa 对应的多个容器,每个容器创建一个该资源。

VPA 也需要依赖 metrics-server,所以也需要提前安装。然后我们可以通过下面命令来安装 VPA 相关组件。

# 查看是否安装
ubuntu@ubuntu:~$ kubectl get crds | grep verticalpodautoscalers
# 开始安装 千万不要sudo
ubuntu@ubuntu:~$ git clone https://github.com/kubernetes/autoscaler.git
ubuntu@ubuntu:~$ cd autoscaler/vertical-pod-autoscaler
ubuntu@ubuntu:~$ ./hack/vpa-up.sh
# 证书权限问题
ubuntu@ubuntu:~/autoscaler/vertical-pod-autoscaler$ sudo rm -rf /tmp/vpa-certs
ubuntu@ubuntu:~/autoscaler/vertical-pod-autoscaler$ mkdir -p /tmp/vpa-certs
ubuntu@ubuntu:~/autoscaler/vertical-pod-autoscaler$ sudo chown $USER:$USER /tmp/vpa-certs
ubuntu@ubuntu:~/autoscaler/vertical-pod-autoscaler$ sudo chmod 700 /tmp/vpa-certs
ubuntu@ubuntu:~/autoscaler/vertical-pod-autoscaler$ ./hack/vpa-up.sh
# 查看是否安装成功
ubuntu@ubuntu:~/autoscaler/vertical-pod-autoscaler$  kubectl get crds | grep verticalpodautoscalers
verticalpodautoscalers.autoscaling.k8s.io             2025-09-12T09:34:57Z
# 查看相关pod
ubuntu@ubuntu:~/autoscaler/vertical-pod-autoscaler$ kubectl get pods -n kube-system |grep vpa
vpa-admission-controller-84998b6899-7g2bh   1/1     Running   0                4m23s
vpa-recommender-6d7fbcb5f6-vfncg            1/1     Running   0                4m23s
vpa-updater-864ff5f65f-wj5dz                1/1     Running   0                4m23s

VPA使用

资源文件:

# vpa-demo-with-vpa.yaml
---
# 部署 Deployment 对象
apiVersion: apps/v1
kind: Deployment
metadata:name: vpa-demo   # Deployment 名称
spec:replicas: 1      # 副本数量selector:matchLabels:app: vpa    # 选择标签,匹配 Podtemplate:metadata:labels:app: vpa  # Pod 的标签spec:containers:- name: nginx       # 容器名称image: nginx      # 镜像resources:requests:cpu: 20m      # 初始 CPU 请求memory: 25Mi  # 初始内存请求
---
# VerticalPodAutoscaler 对象
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:name: nginx-vpa-test  # VPA 名称
spec:targetRef:apiVersion: apps/v1kind: Deploymentname: vpa-demo       # VPA 作用的目标 DeploymentupdatePolicy:updateMode: "Off"    # 更新模式:Off 表示不自动更新 Pod,只监控resourcePolicy:containerPolicies:- containerName: nginx  # 对哪个容器应用策略minAllowed:cpu: 50m          # 最小允许 CPUmemory: 50Mi      # 最小允许内存maxAllowed:cpu: 2000m         # 最大允许 CPUmemory: 2048Mi     # 最大允许内存

验证:

ubuntu@ubuntu:~/example/hpa_vpa$ kubectl apply -f ./vpa-demo-with-vpa.yaml 
deployment.apps/vpa-demo unchanged
verticalpodautoscaler.autoscaling.k8s.io/nginx-vpa-test created
# 查看Pod状态
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl get pods -A -o wide
NAMESPACE      NAME                                        READY   STATUS    RESTARTS      AGE     IP                NODE     NOMINATED NODE   READINESS GATES
default        vpa-demo-85b64c5555-dtx95                   1/1     Running   0             70s     10.244.2.111      node1    <none>           <none>
# 先看状态
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl describe vpa nginx-vpa
Name:         nginx-vpa-test
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  autoscaling.k8s.io/v1
Kind:         VerticalPodAutoscaler
Metadata:Creation Timestamp:  2025-09-12T09:56:10ZGeneration:          1Resource Version:    247343UID:                 f81b5af8-6b56-4a82-8627-5e3078949bc4
Spec:Resource Policy:Container Policies:Container Name:  nginxMax Allowed:Cpu:     2000mMemory:  2048MiMin Allowed:Cpu:     50mMemory:  50MiTarget Ref:API Version:  apps/v1Kind:         DeploymentName:         vpa-demoUpdate Policy:Update Mode:  Off
Status:Conditions:Last Transition Time:  2025-09-12T09:56:12ZMessage:               No pods match this VPA objectReason:                NoPodsMatchedStatus:                TrueType:                  NoPodsMatchedLast Transition Time:  2025-09-12T09:56:12ZStatus:                TrueType:                  RecommendationProvidedRecommendation:Container Recommendations:Container Name:  nginxLower Bound:Cpu:     50mMemory:  262144kTarget:Cpu:     50mMemory:  262144kUncapped Target:Cpu:     25mMemory:  262144kUpper Bound:Cpu:     2Memory:  1523828767
Events:          <none># 一个终端压测
ubuntu@ubuntu:~$ kubectl run -it --image busybox test-vpa --restart=Never --rm /bin/sh
If you don't see a command prompt, try pressing enter.
/ # while true; do wget -q -O- http://10.244.2.111; done# 一个终端再次观察
ubuntu@ubuntu:~/example/hpa_vpa$ kubectl describe vpa nginx-vpa |tail -n 20Last Transition Time:  2025-09-12T09:56:12ZStatus:                TrueType:                  RecommendationProvidedRecommendation:Container Recommendations:Container Name:  nginxLower Bound:Cpu:     50mMemory:  262144kTarget:Cpu:     50mMemory:  262144kUncapped Target:Cpu:     35mMemory:  262144kUpper Bound:Cpu:     2Memory:  1194357142
Events:          <none>
# 可以看到   Uncapped Target: 的值变化了

​ 从上面可以看到给出的 Target 为 143m,因为设置 updateMode:"Off",所以不会应用到 Pod。

​ Vertical Pod Autoscaler (VPA) 中的 updateMode 有以下几种有效选项:

  • Off:VPA 只会提供资源建议,不会自动更改 Pod 的资源请求。

  • Initial:VPA 只在 Pod 创建时分配资源请求,以后不会再修改。

  • Recreate:VPA 在 Pod 创建时分配资源请求,并通过驱逐和重新创建现有 Pod 来更新资源。

  • Auto:VPA 根据资源建议自动重新创建 Pod,实现资源动态调整。

​ 当然 VPA 也并不是万能的,具有一些局限性,在使用的时候以下注意事项值得我们关注:

  • 不要将 VPA 与 HPA 一起使用,后者基于相同的资源指标(例如 CPU 和 MEMORY 使用率)进行缩放。这是因为当指标(CPU/MEMORY)达到其定义的阈值时,VPA 和 HPA 将同时发生缩放事件,这可能会产生未知的副作用并可能导致问题。
  • VPA 可能会推荐比集群中可用的资源更多的资源,从而导致 Pod 未分配给节点(由于资源不足),因此永远不会运行。
  • VPA 不会驱逐不在控制器下运行的 pod,驱逐可以单独处理

​ 为了能够自动资源配置提升管理效率、提高集群资源利用率,除了 VPA 之外,我们后续还会学习其他更智能更适合生产环境使用的工具,不过 VPA 的实现思路值得我们学习。

http://www.wxhsa.cn/company.asp?id=2143

相关文章:

  • MyEMS在行动:揭秘开源能源管理系统如何重塑工业与楼宇的能效未来
  • 题解:P14015 [ICPC 2024 Nanjing R] 生日礼物
  • 吻得太逼真
  • HyperWorks许可回收机制
  • flink on k8s的基本介绍
  • 高性能计算基础
  • flutter开发window打包成exe可执行文件的步骤
  • Transtion动画组件要求包裹元素必须是单一根节点
  • linux启动ntp服务
  • 企业级 AI Agent 开发指南:基于函数计算 FC Sandbox 方案实现类 Chat Coding AI Agent
  • android开发局域网内通过NTP服务端自动更新系统时间
  • 一招解决Proxmox VE虚拟机磁盘空间耗尽:LVM在线扩容实战 - 若
  • jiaozi
  • 基于Linux系统的定制软件安装硬件设备选型指南
  • c++之is_trivially_default_constructible
  • python3协程学习-async,await
  • 猫树分治
  • Rust太难了。。。。。。。
  • AI导航生成寻路点-FindPathToLocationSynchronously
  • cache写策略
  • 个人微信开发
  • C++之std::is_trivially_copyable
  • PostgreSQL技术大讲堂 - 第104讲:PostgreSQL分区表应用实践
  • redis实现缓存1-添加商户缓存
  • qemu的外部快照实现原理
  • Springboot 集成 飞书群消息
  • 最新爆料:GitHub Copilot全面推出OpenAI GPT-5 和 GPT-5 mini!
  • netstat 命令查看端口状态详解
  • 智聘无界:AI 破解全球化招聘合规、成本与人才匹配难题的实践路径
  • Nature | 本周最新文献速递