# Kubernetes(K8s)笔记Day11 : HPA 自动扩缩容

发布时间:2026/9/2 8:26:02
# Kubernetes(K8s)笔记Day11 : HPA 自动扩缩容 一、扩缩容概述弹性伸缩是根据用户的业务需求和策略自动“调整”其“弹性资源”的管理服务。通过弹性伸缩功能用户可设置定时、周期或监控策略恰到好处地增加或减少“弹性资源”并完成实例配置保证业务平稳健康运行。在实际工作中我们常常需要做一些扩容缩容操作如电商平台在 618 和双十一搞秒杀活动由于资源紧张、工作负载降低等都需要对服务实例数进行扩缩容操作。在 Kubernetes 中扩缩容分为两种扩缩容类型说明Node 层面对 K8s物理节点扩容和缩容根据业务规模实现物理节点自动扩缩容Pod 层面一般使用 Deployment 中的replicas参数设置多个副本集来保证高可用但这是一个固定值。流量少时也是多个 Pod 同时运行造成资源浪费流量暴增时又可能出现 Pod 不够用的情况二、自动伸缩的方案2.1 HPAHorizontal Pod AutoscalerPod 水平自动伸缩HPAHorizontal Pod Autoscaler是控制器如deploymentStatefulSet等的控制器是 Kubernetes 生态里最通用、应用最广泛的自动扩缩容方案没有之一。核心功能通过此功能只需简单的配置便可以利用监控指标CPU 使用率、磁盘、自定义指标等自动地扩容或缩容服务中 Pod 数量当业务需求增加时系统将无缝地自动增加适量 Pod 容器提高系统稳定性。HPA 版本支持的指标HPA v1仅支持根据 CPU 使用率进行自动扩缩容HPA v2支持根据 CPU、内存以及自定义指标进行自动扩缩容HPA 默认每 15 秒同步一次且有“稳定窗口”防止频繁扩缩容默认缩容等待 5 分钟2.2 KPAKnative Pod Autoscaler基于请求数的自动扩缩容核心功能基于请求数对 Pod 自动扩缩容。主要限制不支持基于 CPU 的自动扩缩容。相比 HPA 的优势会考虑更多的场景其中一个比较重要的是流量突发的时候能够更快地响应突发流量。KPA 是 Knative 生态下的组件非通用方案2.3 VPAVertical Pod Autoscaler垂直 Pod 自动扩缩容核心功能VPA 会基于 Pod 的资源使用情况自动为集群设置资源占用的限制从而让集群将 Pod 调度到有足够资源的最佳节点上。VPA 也会保持最初容器定义中资源 request 和 limit 的占比。工作原理根据容器资源使用率自动设置 Pod 的 CPU 和内存的 requests允许在节点上进行适当的调度以便为每个 Pod 提供适当的可用节点既可以缩小过度请求资源的容器也可以根据其使用情况随时提升资源不足的容量VPA 不是内置组件需要单独安装更新模式有三种模式行为Off只给建议不自动调整Auto自动调整需重建 Pod才能生效Initial只在 Pod 创建时设置一次三、HPA 工作原理HPA 全称是 Horizontal Pod Autoscaler翻译成中文是 Pod 水平自动伸缩。HPA 可以基于 CPU 利用率对 Deployment 中的 Pod 数量进行自动扩缩容除了 CPU 也可以基于自定义的指标进行自动扩缩容。重要说明HPA Pod 自动扩缩容不适用于无法扩缩容的对象比如 DaemonSets。因为HPA 靠调整 Deployment/StatefulSet 的 spec.replicas副本数 字段来工作。而 DaemonSet 没有 replicas 字段。但是可以使用VPA调整它的资源限制3.1 核心机制HPA 由 Kubernetes API 资源和控制器实现。控制器会周期性地获取平均 CPU 利用率并与目标值相比较后调整 Deployment 中的副本数量。3.2 工作原理流程图┌─────────────────────────────────────────────────────────────────────────────┐ │ HPA 工作原理 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ① 创建 HPA 资源设定目标 CPU 使用率限额以及最大、最小实例数 │ │ │ │ │ ▼ │ │ ② 收集一组中PodSelector每个 Pod 最近一分钟内的 CPU 使用率并计算平均 │ │ │ │ │ ▼ │ │ ③ 读取 HPA 中设定的 CPU 使用限额 │ │ │ │ │ ▼ │ │ ④ 计算平均值之和/限额求出目标调整的实例个数 │ │ │ │ │ ▼ │ │ ⑤ 目标调整的实例数不能超过①中设定的最大、最小实例数 │ │ 未超过则扩容超过则扩容至最大的实例个数 │ │ │ │ │ ▼ │ │ ⑥ 回到 ②不断循环 │ │ │ └─────────────────────────────────────────────────────────────────────────────┘3.3 关键参数参数说明检测周期默认每 30s 检测一次指标扩缩容冷却默认在 5min 内没有重新扩缩容的情况下才会触发扩缩容算法特点HPA 本身的算法相对比较保守快速流量突发场景下如果正处在 5min 内的 HPA 稳定期会导致无法扩容3.4 查看支持的 API 版本kubectl api-versions|grepautoscal3.5 Metrics APIMetrics API 是 Kubernetes 集群里的一个标准 API 端点它专门用来对外提供 Pod 和 Node 的实时资源使用数据如 CPU、内存。它的全称是 metrics.k8s.io API 组。Kubernetes 从 1.8 版本开始CPU、内存等资源的 metrics 信息可以通过Metrics API来获取用户可以直接获取这些 metrics 信息例如通过执行kubectl top命令HPA 使用这些 metrics 信息来实现动态伸缩。数据来源每个节点上的 kubelet 内置了cAdvisor容器监控工具它会采集本机所有容器的 CPU/内存使用量。Metrics Server 从每个节点的 kubelet 的 /stats/summary 接口拉取这些数据。Metrics Server 将汇总后的数据缓存在内存中并通过 Kubernetes API 的 /apis/metrics.k8s.io/v1beta1 端点暴露出去。它主要用来做什么kubectl top 命令的后端你执行 kubectl top nodes 或 kubectl top pods 时kubectl 请求的就是 Metrics API。HPA水平 Pod 自动扩缩容的决策依据HPA 默认基于 Metrics API 获取 CPU/内存使用率然后计算是否需要扩容。如果你没装 Metrics ServerHPA 就永远拿不到数据会一直处于 状态。Metrics API vs PrometheusMetrics API 是 Kubernetes 内置的基础监控标准接口它只提供当前值瞬时值且只包含 CPU/内存。它的定位是给 HPA 调度和 kubectl top 做轻量决策用的。而 Prometheus 是独立的第三方监控系统它提供历史趋势数据支持自定义业务指标如 QPS、HTTP 错误率、消息队列积压数。如果 HPA 想基于自定义指标比如 Kafka 的消费 Lag扩缩容就需要通过Prometheus Adapter转换成Metrics API 的扩展格式喂给 HPA而不是靠原生的 Metrics Server。排错要点如果HPA 拿不到 Metrics 数据先查看 kubectl top pod命令能否执行若不能先看 Metrics Server Pod 是否 Running再看 kubelet 的 /stats/summary 接口是否可达。四、HPA 部署与实战案例4.1 安装数据采集组件 metrics-servermetrics-server是一个集群范围内的资源数据集和工具。它主要关注的是资源度量 API 的实现比如 CPU、文件描述符、内存、请求延时等指标。metrics-server 收集数据给 K8s 集群内使用如kubectl top、HPA、Scheduler 等。项目地址https://github.com/kubernetes-sigs/metrics-server稳定版本v0.6.2YAML 下载地址https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.2/components.yamla. 部署 metrics-server 组件上传镜像压缩包并导入根据容器运行时选择# 容器运行时为 docker[rooth2 ~]# docker load -i metrics-server0.62.tar.gz[rooth3 ~]# docker load -i metrics-server0.62.tar.gz# 容器运行时为 containerd[rooth2 ~]# ctr -n k8s.io images import metrics-server0.62.tar.gz[rooth3 ~]# ctr -n k8s.io images import metrics-server0.62.tar.gzb. 修改 kube-apiserver 配置在/etc/kubernetes/manifests里面修改 apiserver 的配置添加--enable-aggregator-routingtrue参数# 修改 kube-apiserver.yaml 文件添加一行内容[roothd1 ~]# vim /etc/kubernetes/manifests/kube-apiserver.yaml# 添加内容如下- --enable-aggregator-routingtrue[roothd1 ~]# kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml# 这一步可以省略因为 /etc/kubernetes/manifests 中的 yaml 文件对应的资源都是静态 PodKubelet 会实时监控不需要单独执行 kubectl apply重要说明这是 Kubernetes 在 1.17 的新特性如果是 1.16 版本的可以不用添加1.17 以后要添加。这个参数的作用是Aggregation聚合允许在不修改 Kubernetes 核心代码的同时扩展 Kubernetes API。c. 部署 metrics-server# 上传 components.yaml 文件[roothd1 ~]# kubectl apply -f components.yaml由于文件中配置较多可能需要等待一到两分钟才能完成部署d. 验证 metrics-server 是否部署成功# 查看 Pod 状态[roothd1 ~]# kubectl get pods -n kube-system | grep metricsmetrics-server-59549d9b85-7xxkq1/1 Running05m16s# 测试 kubectl top 命令查看节点资源[roothd1 ~]# kubectl top nodesNAME CPU(cores)CPU% MEMORY(bytes)MEMORY% hd1 689m17% 2005Mi61% hd2 429m10% 1342Mi40% hd3 450m11% 1832Mi55%# 测试 kubectl top 命令查看 Pod 资源[roothd1 ~]# kubectl top pods -n kube-systemNAME CPU(cores)MEMORY(bytes)calico-kube-controllers-57b57c56f-khn8l 3m 67Mi calico-node-bqzkp 37m 177Mi calico-node-kpwwf 36m 153Mi calico-node-tkm27 45m 158Mi coredns-5bbd96d687-464gw 3m 37Mi coredns-5bbd96d687-wcvmp 3m 39Mi etcd-hd1 42m 127Mi fluentd-elasticsearch-d22wn 2m 53Mi fluentd-elasticsearch-pmqzx 1m 57Mi fluentd-elasticsearch-tm8rz 1m 51Mi kube-apiserver-hd1 81m 341Mi kube-controller-manager-hd1 40m 59Mi kube-proxy-lzqgk 1m 49Mi kube-proxy-mfhjd 1m 48Mi kube-proxy-srfhg 1m 49Mi kube-scheduler-hd1 6m 20Mi metrics-server-59549d9b85-7xxkq 6m 17Mi4.2 创建 php-apache 服务利用 HPA 进行自动扩缩容步骤 1构建 PHP 应用镜像上传我们的源镜像包 php-5-apache.tar[roothd1 ~]# docker load -i php-5-apache.tarLoaded image: php:5-apache先创建目标目录编写DockerFile[roothd1 ~]# mkdir php[roothd1 ~]# cd php/[roothd1 php]# cat dockerfileFROM php:5-apache ADD index.php /var/www/html/index.php#将本地当前目录下的 index.php 文件复制到容器内的 /var/www/html/index.phpRUNchmodarx index.php#给 index.php 文件授权创建 index.php这是一个 CPU 密集计算脚本[roothd1 php]# cat index.php?php$x0.0001;for($i0;$i1000000;$i){$xsqrt($x);}echoOK!;?构建镜像# 构建镜像[roothd1 php]# docker build -t hpa-example:v1 .# 打包镜像[roothd1 php]# docker save -o hpa-example.tar.gz hpa-example:v1分发镜像[roothd1 php]# scp hpa-example.tar.gz hd2:/root/[roothd1 php]# scp hpa-example.tar.gz hd3:/root/在 K8s 工作节点解压镜像#假如容器运行时为docker[roothd2 ~]# docker load -i hpa-example.tar.gz[roothd3 ~]# docker load -i hpa-example.tar.gz# 假如容器运行时为 containerd[roothd2 ~]# ctr -n k8s.io images import hpa-example.tar.gz[roothd3 ~]# ctr -n k8s.io images import hpa-example.tar.gz步骤 2通过 Deployment 部署 php-apache 服务创建deployment和service[roothd1 ~]# php-apache.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:php-apachespec:selector:matchLabels:run:php-apachereplicas:1template:metadata:labels:run:php-apachespec:containers:-name:php-apacheimage:hpa-example:v1imagePullPolicy:IfNotPresentports:-containerPort:80resources:limits:cpu:500mrequests:cpu:200m---apiVersion:v1kind:Servicemetadata:name:php-apachelabels:run:php-apachespec:ports:-port:80selector:run:php-apache#选择标签为run: php-apache的pod# 更新资源清单文件[roothd1 ~]# kubectl apply -f php-apache.yamldeployment.apps/php-apache created service/php-apache created# 验证 php 是否部署成功[roothd1 ~]# kubectl get podNAME READY STATUS RESTARTS AGE php-apache-6c4dd55c95-5wv2t1/1 Running08m2s步骤 3创建 HPA# 给 php-apache 这个 Deployment 创建 HPA。autoscale自动扩缩容的缩写[roothd1 ~]# kubectl autoscale deployment php-apache --cpu-percent50 --min1 --max10horizontalpodautoscaler.autoscaling/php-apache autoscaled命令参数说明参数说明php-apacheDeployment 的名字--cpu-percent50CPU 使用率目标值不超过 50%--min1最少 1 个 Pod--max10最多 10 个 Pod# 验证 HPA 是否创建成功[roothd1 ~]# kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE php-apache Deployment/php-apache0%/50%110118s由于没有向服务器发送任何请求当前 CPU 消耗为 0%TARGET 列显示了由相应 Deployment 控制的所有 Pod 的平均值。4.3 压测 php-apache 服务基于 CPU步骤 1导入压测用镜像把busybox.tar.gz和nginx-1-9-1.tar.gz上传到hd2、hd3上#容器运行时为 docker[roothd2 ~]#ctr -n k8s.io images import -i busybox.tar.gz[roothd3 ~]#ctr -n k8s.io images import -i busybox.tar.gz# 容器运行时为 containerd[roothd2 ~]# ctr -n k8s.io images import busybox.tar.gz[roothd3 ~]# ctr -n k8s.io images import busybox.tar.gz步骤 2启动压测容器启动一个容器并将无限查询循环发送到 php-apache 服务打开一个新的终端窗口[roothd1]# kubectl run v2 -it --imagedocker.io/library/busybox:1.28 \--image-pull-policyIfNotPresent /bin/sh登录到容器之后执行如下命令# 持续发送请求触发 CPU 负载/# while true; do wget -q -O- http://php-apache.default.svc.cluster.local; done步骤 3观察 HPA 自动扩容在一分钟左右的时间内在另一个终端窗口通过执行以下命令看到更高的 CPU 负载[roothd1]# kubectl get hpa显示如下 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS php-apache Deployment/php-apache231%/50%1104上面可以看到CPU 消耗已经达到 231%每个 Pod 的目标 CPU 使用率是 50%所以 php-apache 这个 Deployment 创建的 Pod 副本数将调整为 5 个副本。[roothd1]# kubectl get deployment php-apache显示如下 NAME READY UP-TO-DATE AVAILABLE AGE php-apache5/5552h1m步骤 4停止压测并观察自动缩容停止向 php-apache 服务发送查询请求在 busybox 镜像创建容器的终端中通过CtrlC把刚才 while 请求停止然后验证结果状态大约 5 分钟后[roothd1]# kubectl get deployment php-apache显示如下 php-apache1/1115sPod 数量自动恢复到 1 个。4.4 利用 HPA 基于内存指标实现 Pod 自动扩缩容步骤 1创建 Nginx Deployment 和 Service[roothd1 ~]# vim nginx.yamlapiVersion: apps/v1 kind: Deployment metadata: name: nginx-hpa spec: selector: matchLabels: app: nginx replicas:1template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest imagePullPolicy: IfNotPresent ports: - containerPort:80name: http protocol: TCP resources:#资源限制requests: cpu:0.01memory: 25Mi limits: cpu:0.05memory: 60Mi --- apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: selector: app: nginx type: NodePort ports: - name: http protocol: TCP port:80targetPort:80# 更新资源清单文件[roothd1 ~]# kubectl apply -f nginx.yamldeployment.apps/nginx-hpa created service/nginx created# 验证 nginx 是否运行[roothd1 ~]# kubectl get podsNAME READY STATUS RESTARTS AGE nginx-hpa-69964b867b-9j6ml1/1 Running069s注意Nginx 的 Pod 里必须包含resources.requests和resources.limits资源限制的字段否则 HPA 无法采集到内存指标。步骤 2创建基于内存的 HPAv2 版本[roothd1 ~]# vim hpa-v2.yamlapiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:nginx-hpaspec:maxReplicas:10# 扩容上限最多不能超过 10 个 Pod防止资源耗尽minReplicas:1# 缩容下限至少保留 1 个 Pod防止全缩容导致服务不可用scaleTargetRef:#指定目标资源管理谁apiVersion:apps/v1# 目标资源的 API 版本kind:Deployment# 目标资源的类型也支持 StatefulSet不支持 DaemonSetname:nginx-hpa# 目标 Deployment 的名称必须与 metadata.name 一致metrics:-type:Resourceresource:name:memorytarget:averageUtilization:60# 【平均利用率】所有匹配 Pod 的内存平均值达到 60% 即触发扩容type:Utilization# 指标类型Utilization利用率百分比基于 Pod 的 requests 计算# 更新资源清单文件[roothd1 ~]# kubectl apply -f hpa-v2.yaml# 查看创建的 HPA[roothd1 ~]# kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE nginx-hpa Deployment/nginx-hpa17%/60%110131s4.5 压测内存步骤 1进入容器生成大文件增加内存使用[roothd1]# kubectl exec -it nginx-hpa-fb74696c-6m6st -- /bin/sh进入容器内部执行压测命令# 生成一个 1GB 的大文件消耗内存# dd if/dev/zero of/tmp/a bs1M count1000# 或持续写入# dd if/dev/zero of/tmp/a步骤 2观察 HPA 自动扩容打开新的终端进行查看[roothd1 ~]# kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE nginx-hpa Deployment/nginx-hpa200%/60%1104TARGETS 列显示200%/60%200% 表示当前内存使用率60% 表示所有 Pod 的内存使用率维持在 60%。内存使用率达到 200%Pod 自动增加到 4 个。# 查看 Deployment[roothd1 ~]# kubectl get deploymentNAME READY UP-TO-DATE AVAILABLE AGE nginx-hpa4/44425m# 查看 Pod 数量[roothd1 ~]# kubectl get podsNAME READY STATUS RESTARTS AGE nginx-hpa-bb598885d-j4kcp1/1 Running025m nginx-hpa-bb598885d-rj5hk1/1 Running063s nginx-hpa-bb598885d-twv9c1/1 Running018s nginx-hpa-bb598885d-v9ft51/1 Running063s步骤 3取消压测并观察自动缩容# 取消对 Nginx 内存的压测[roothd1 ~]# kubectl exec -it nginx-hpa-fb74696c-6m6st -- /bin/sh# rm -rf /tmp/a# 查看 HPA 指标内存使用率逐渐下降[roothd1 ~]# kubectl get hpaNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS nginx-hpa Deployment/nginx-hpa5%/60%1101# 查看 DeploymentPod 恢复到 1 个[roothd1 ~]# kubectl get deploymentNAME READY UP-TO-DATE AVAILABLE AGE nginx-hpa1/11138m五、三种扩缩容方案对比方案全称扩缩容维度基于的指标主要限制适用场景HPAHorizontal Pod Autoscaler水平Pod 数量CPU、内存、自定义指标v2默认 5min 冷却期最常见的 Pod 弹性伸缩场景VPAVertical Pod Autoscaler垂直Pod 资源配额CPU、内存使用率需要重启 Pod 才能调整资源优化 Pod 资源 requests/limits 配置KPAKnative Pod Autoscaler水平Pod 数量请求数QPS不支持基于 CPU 的扩缩容Serverless 场景流量突发时快速响应六、关键要点速查HPA 常用命令# 使用命令行创建 HPAkubectl autoscale deploymentdeployment-name\--cpu-percent目标CPU使用率\--min最小副本数\--max最大副本数# 查看 HPAkubectl get hpa kubectl describe hpahpa-name# 查看 HPA 支持的所有版本kubectl api-versions|grepautoscal# 查看节点/ Pod 资源使用情况kubectltopnodes kubectltoppods[-nnamespace]HPA 核心参数说明参数说明--cpu-percent目标 CPU 使用率阈值百分比超过即触发扩容--min最小副本数即使没有流量也不会少于这个数--max最大副本数防止无限扩容耗尽集群资源averageUtilization平均利用率模式v2 版本中用于内存等指标HPA v2 vs v1版本支持的指标类型说明v1仅 CPU基础版本功能简单v2CPU、内存、自定义指标如 QPS、消息队列长度等推荐使用功能更强大七、常见问题排查问题可能原因解决方法kubectl top无法获取指标metrics-server 未安装或未正常运行检查kubectl get pods -n kube-system | grep metricsHPA 不触发扩容未配置 Pod 的resources.requestsHPA 必须基于requests计算使用率需在 Pod 模板中配置HPA 扩容后一直不缩容HPA 默认有 5min 冷却期等待 5 分钟后自动缩容或调整 HPA 参数内存 HPA 采集不到数据Pod 没有设置resources.requests.memory添加 memory requests 字段metrics-server Pod 启动失败kube-apiserver 未开启聚合层检查--enable-aggregator-routingtrue是否添加八、拓展补充前文为了方便理解和示例对部分内容进行简化了说明在本节进行拓展补充1. 关于 HPA 算法的准确说明计算方式“平均值之和/限额”不够准确。HPA 实际算法的核心逻辑是期望副本数 ceil(当前副本数 × (当前指标值 / 期望指标值))示例当前 1 个 PodCPU 使用率 231%期望 50%则期望副本数 ceil(1 × (231% / 50%)) ceil(4.62) 5这就是为什么kubectl get hpa显示REPLICAS变为 4 或 5 的原因。2. 关于 HPA 冷却时间原文提到“5min 内没有重新扩缩容的情况下才会触发扩缩容”更准确的理解是HPA 有两个控制稳定性的参数扩容稳定窗口horizontal-pod-autoscaler-upscale-stabilization-window默认 5 分钟。HPA 会记录最近 5 分钟内的所有扩容决策取其中的最大值作为实际扩容目标避免因指标抖动导致频繁扩容。缩容稳定窗口horizontal-pod-autoscaler-downscale-stabilization-window默认 5 分钟。类似地缩容时也会取最近 5 分钟内的最小决策值。3. 关于 metrics-server 的作用metrics-server 只负责实时采集和提供CPU/内存指标数据不负责存储历史数据。这也是为什么kubectl top只能看到当前数据无法查看历史趋势的原因。如果需要历史数据需要部署 Prometheus 自定义指标适配器。4. 关于自定义指标的补充HPA v2 支持自定义指标常见的使用场景包括基于 RabbitMQ/Kafka 消息队列长度自动扩容消费者 Pod基于 HTTP 请求数QPS自动扩容基于数据库连接数自动扩容这需要配合 Prometheus Adapter 或 Custom Metrics API 实现。九、总结Kubernetes HPA 是实现 Pod 水平自动伸缩的核心组件它通过 Metrics API 获取 Pod 的资源使用指标根据设定的阈值自动调整 Deployment 的副本数量。HPA v1 仅支持 CPU 指标HPA v2 则支持 CPU、内存和自定义指标满足更复杂的弹性伸缩场景。核心要点前提条件必须先部署 metrics-server 采集资源指标配置要求Pod 必须设置resources.requestsHPA 才能计算使用率稳定性保护HPA 默认有 5 分钟的稳定窗口防止频繁扩缩容版本选择生产环境推荐使用 autoscaling/v2支持更多指标类型容量规划合理设置minReplicas和maxReplicas避免资源浪费或扩容不足

相关新闻