1. HPA 到底解决了什么问题先聊点实在的。K8s 系列写到第十篇前面几篇我们聊过集群初始化、Node 管理、工作负载、服务暴露这些基础内容有件事一直没细说就是副本数到底怎么定。很多朋友刚上手 K8s 的时候Deployment 的 replicas 都是拍脑袋写的。测试环境 2 个副本上线了改 5 个遇到大促再手动扩到 20 个。这套玩法在流量平稳的项目里勉强能用但一旦业务流量有波动手动扩缩容的短板就很明显半夜流量突增没人看着应用被打爆流量回落以后副本数还傻乎乎地挂着钱白烧。而且手动操作本身就是一个很大的失误源生产环境里点错按钮把副本数从 20 改成 2 的事我见过不止一次。HPA全称 HorizontalPodAutoscaler就是来解决这个问题的。它的思路很直接让 K8s 自己盯着 Pod 的指标指标涨了自动加副本指标降了自动减副本。不需要人盯着 Grafana 面板手动操作也不需要在半夜爬起来改 replicas。不过这里要说清楚HPA 不是万能的。它只负责“横 向”扩缩容也就是调整副本数量。如果单个 Pod 的资源请求不够用加再多副本也白搭那是“纵 向”扩容VPA和资源规划该管的事。另外 HPA 的生效有延迟指标采集、聚合、评估都是有周期的不可能像实时监控系统那样秒级响应。理解这些边界后面踩坑时才知道往哪个方向排查。这篇就把 HPA 的机制、配置、实战、自定义指标一条龙讲透。文章里所有示例都是我在测试环境实测过的YAML 可以直接抄但也建议你理解每一行的含义再改着用。先交代一下测试环境背景。我用的是 Rocky Linux 9 上装的 K8s 1.36 单节点集群Control-Plane 节点就是工作节点没有额外配置调度策略。如果你的环境是纯 Master 加 Worker 的架构只需要把示例中的 nodeSelector 去掉或者改成你自己的节点标签即可。单节点测试有个好处Pod 调度不会因为节点资源不足而失败排查 HPA 问题时能把变量控制得更干净。2. HPA 工作机制拆解采集、评估、执行2.1 控制循环的三个核心环节HPA 本质上是一个控制循环跟 K8s 里大多数控制器一样它持续地“观察现状、对比期望、执行动作”。现在 K8s 1.36 里默认的 HPA 走的是 autoscaling/v2 API这个版本支持按 CPU、内存、自定义指标、外部指标来做扩缩容判断。它的工作节奏大约每 15 秒一轮但中间涉及指标采集延迟和聚合延迟所以实际一个完整的控制周期通常在 30 到 60 秒左右。这点先记住后面排查“为什么半天没反应”时会用到。整个控制循环分三步第一步是指标采集。HPA 通过 K8s 的 metrics API 读取指标数据。CPU、内存这类资源指标来自 metrics-server它每 15 秒从 kubelet 拉一次数据保留最近 1 到 2 分钟的历史。自定义指标则来自自定义 metrics API这个在后面第四节详细讲。第二步是评估计算。HPA 控制器拿到指标后会用当前副本数和目标值做比较算出一个期望副本数。计算逻辑很简单核心是一个除法加向上取整。期望副本数等于当前副本数乘以当前指标值除以目标指标值最后向上取整。举个例子当前 2 个副本的平均 CPU 利用率是 80%目标值是 50%期望副本数就是 2 乘以 80 除以 50等于 3.2向上取整后是 4 个副本。第三步是执行变更。计算出的期望副本数如果跟当前副本数不一致HPA 就会去更新 Deployment、StatefulSet 或者 ReplicaSet 的副本数。这里有个细节要注意HPA 并不直接创建和删除 Pod它只负责修改控制器的 spec.replicas真正的 Pod 生命周期管理还是由 Deployment 的控制器来完成。这套分层的设计意味着 HPA 不能直接管 DaemonSet因为 DaemonSet 的副本数由节点数量决定改不了。2.2 扩容和缩容为什么“不对称”HPA 的扩容和缩容策略在设计上是不对称的这一点很多文章没讲透。扩容更激进。默认情况下只要评估结果认为需要扩容HPA 会立即执行没有任何等待时间。如果指标持续超标副本数会快速往上顶。为什么这样设计因为流量突增时每一秒都是在跟用户体验赛跑早一步扩出 Pod就能少丢一些请求。缩容则保守得多。默认配置下HPA 会先连续观察 5 分钟确认指标确实降下来了才开始缩容。而且缩容也不是一步到位默认每分钟最多缩容一部分副本防止流量一抖就把副本数砍没了。这个 5 分钟冷却窗口是可以调的在 behavior 字段里配置 stabilizationWindowSeconds 就能控制后面实战部分会给出完整的配置示例。如果你的业务流量有明显的波峰波谷默认的 300 秒缩容冷却可能太长了。比如午高峰过去后你希望 1 分钟内把多余的副本缩掉那就可以把 scaleDown 的策略改掉。但是一定要想清楚缩容太快意味着如果马上又来一波流量副本还没缩完就又要扩容集群会一直处于抖动状态。我在测试环境试过把缩容冷却改成 60 秒配合压测工具反复制造流量尖峰Pod 数量确实跟着流量走得更紧了但对 API Server 的压力也明显变大毕竟是每 15 秒一轮的评估。生产环境建议还是保守一点优先保证稳定性。2.3 为什么目标值建议用平均值而不是总和这里有一个新手很容易踩的坑HPA 的 target 值有两种写法一种是 averageUtilization按平均利用率算另一种是 averageValue按平均值算。前者针对 CPU、内存这类资源指标计算的是“所有 Pod 的平均利用率百分比”。后者针对具体数值指标比如“每秒请求数”计算的是“所有 Pod 的平均每秒请求数”。有些朋友一开始会想我直接把目标值设置为“每秒 10000 个请求”不就行了流量到了 10000 就扩容。这个想法本身没错但要注意 HPA 计算的时候用的是平均值。如果当前有 5 个 Pod每个 Pod 每秒处理 2000 个请求总共 10000平均值还是 2000跟目标值 10000 一比远没达标HPA 根本不会扩容。所以设置指标目标值之前你得先想清楚这个指标是“总量”还是“均值”。如果是总量指标比如整个服务的 QPS、在线人数建议先在 Prometheus 里做一步计算把总量除以副本数换算成平均值再配置给 HPA 用。千万不要直接把总量填进 target填了等于没填。2.4 API 聚合层需要重点检查HPA 能读到指标依赖的是 K8s 的 API 聚合层。这个聚合层的作用是把 metrics.k8s.io、custom.metrics.k8s.io 这些 API 请求代理到后端的 metrics-server 或 Prometheus Adapter。如果你发现 HPA 一直读不到指标第一件事不是去看 HPA 的 YAML而是先检查聚合层是否正常。常用的排查命令就一条kubectl get apiservices | grep metrics。如果显示的是 False 或者 Unknown说明聚合层跟后端服务的连接有问题得先解决这个HPA 才能正常工作。还有一点集群初始化时经常遇到 Control-Plane 节点状态不健康、apiserver 报错的情况这在排障的时候很容易跟 HPA 指标异常混淆。前面提到过我们 Rocky 环境初始化时也出现过 the api server is not healthy 的提示这种大概率是 apiserver 启动慢或者网络组件还没就绪导致的一般等一两分钟会自动恢复或者检查一下 kubelet 日志。如果 apiserver 本身都不健康HPA 控制器根本没法正常评估指标正常不代表 HPA 就正常这两个层面的问题要分开排查。3. 动手做一次 HPA 实测从 Deployment 到压测验证3.1 准备测试用的 Deployment 和 Service实操环节。先创建一个简单的测试应用就用 nginx 镜像配置好资源请求。注意这一步非常关键如果 Deployment 里的 Pod 没有写 resources.requestsHPA 读不到 CPU 利用率配置了等于没配。我写了一个最小可用的 YAMLapiVersion: apps/v1 kind: Deployment metadata: name: hpa-demo labels: app: hpa-demo spec: replicas: 1 selector: matchLabels: app: hpa-demo template: metadata: labels: app: hpa-demo spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80 resources: requests: cpu: 50m memory: 64Mi limits: cpu: 100m memory: 128Mi请求值设置为 CPU 50 毫核是为了让压测工具更容易触发扩容。生产环境要根据实际压测数据来定这个后面说。还要配套创建一个 Service。HPA 本身不需要 Service但如果你后面要配自定义指标或者用压测工具打流量Service 是少不了的apiVersion: v1 kind: Service metadata: name: hpa-demo spec: selector: app: hpa-demo ports: - port: 80 targetPort: 80创建完以后确认一下状态。kubectl get deploy hpa-demo 应该能看到 1/1 的副本就绪。如果一直起不来用 kubectl describe pod 看下事件八成是镜像拉不下来或者资源请求超过了节点剩余量。3.2 创建 HPA先解释关键字段再给配置接下来创建 HPA。我用的是 autoscaling/v2 版本K8s 1.23 以后这个版本已经是稳定版了1.36 里完全可以直接用。apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: hpa-demo spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: hpa-demo minReplicas: 1 maxReplicas: 5 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 50 behavior: scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 15 scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 100 periodSeconds: 60解释一下每个核心字段。scaleTargetRef 指定 HPA 作用在哪个对象上我这里用的是 Deployment。支持 Deployment、StatefulSet 和 ReplicaSet不能直接用在 DaemonSet 上。minReplicas 和 maxReplicas 是扩缩容的边界。min 建议不要写 0因为一旦缩到 0流量来的时候冷启动时间会很长而且有些自定义指标采集器不支持从 0 到 1 的扩容。如果你确实需要缩到 0那得配合其他方案比如 KEDA这里先不展开。metrics 数组里可以配多个指标。HPA 会逐个评估只要有一个指标超标就会触发扩容缩容则要求所有指标都低于目标值。这一点很关键如果你想实现“CPU 高或内存高就扩容”直接把两个指标都放进去就行不用写复杂的逻辑。behavior 字段是 v2 版本的新特性用来精细控制扩缩容策略。我这里把 scaleUp 的 stabilizationWindowSeconds 设成了 0意思是一旦指标超标立刻扩容不要让稳定窗口挡着。scaleDown 还是保留了 300 秒的稳定窗口防止流量抖动导致副本数反复横跳。稳定窗口这个概念值得多说两句。它的工作原理是这样的缩容时HPA 会记住过去一段时间内算出的期望副本数然后从中取最大值作为最终结果。假如第 300 秒算出来期望 2 个第 315 秒算出来期望 1 个第 330 秒又算出来期望 3 个HPA 会把 2、1、3 都保留在窗口里只有所有值都稳定下来以后才会真正往下缩。这样可以避免“缩一下、又扩回来”的震荡。创建完成后用 kubectl get hpa 看一下状态。正常情况下能看到 TARGETS 列显示了一个值比如 0%/50%。如果显示的是 说明指标还没采集到等一两分钟再查。如果一直 unknown大概率是 metrics-server 的问题用 kubectl top nodes 和 kubectl top pods 先验证一下基础指标链路。3.3 压测验证扩容和缩容全流程HPA 创建好了现在开始压测。我用的压测工具是 Apache Bench也就是 ab系统自带或者装一下就行。也可以用它做并发请求。先看一把 HPA 的初始状态kubectl get hpa hpa-demo正常会是 1/1 副本CPU 利用率 0%。然后用 ab 开始打流量ab -n 20000 -c 50 http://NodeIP:NodePort/这里要注意你的 Service 如果是 ClusterIPab 没法直接从外部访问需要改成 NodePort 或者先在集群里跑一个跳板 Pod。测试环境可以简单一点直接用 NodePort 暴露 Service我实际测试时就是这么干的。压测跑起来后观察 HPA 的变化kubectl get hpa -w大概十几秒后TARGETS 列的 CPU 利用率会开始上升。到 50% 以上后HPA 开始扩容。由于我的 scaleUp 稳定窗口是 0而且策略是每次最多扩 100% 副本数两三个评估周期就能从 1 个扩到 5 个。这里注意一次扩容的副本数不是直接一步到位而是按策略周期逐步增加的。比如从 1 扩到 2再从 2 扩到 4最后到 5每个周期之间隔 15 秒。压测停止后CPU 利用率很快降下来。但缩容会等 300 秒这是稳定窗口在起作用。过几分钟后再看kubectl get hpa副本数会逐渐从 5 缩回 1。如果你嫌 5 分钟太久可以把 scaleDown 的 stabilizationWindowSeconds 调小到 60 秒但前面也说了生产环境不建议太激进。这个实验做完你对 HPA 的整个机制就有了直观感受。它不是什么黑魔法就是一个根据指标平均值做除法、然后向上取整的控制器关键拼图是“指标准不准”和“策略合不合理”。3.4 如何确定合适的资源请求值和目标值一个很常见的问题CPU 请求值设多少合适target 平均值设多少合理先回应一个误区。很多新手以为 resources.requests 里的 CPU 值就是 HPA 的目标值这是不对的。requests 是调度和配额用的告诉 K8s 这个 Pod 最少需要多少资源HPA 的目标值是一个百分比是相对于 requests 的利用率。比如 requests.cpu 是 50mtarget 平均利用率是 50%那 HPA 看到每个 Pod 平均用了 25m 就算达标超过 25m 就开始扩容。如果你的 requests 设得太小比如 10m那么实际负载稍微高一点就会超过 50% 的目标频繁扩容。如果设得太大比如 2000m那么利用率可能永远到不了 50%HPA 永远不扩容。正确的做法是先压测试。用你预估的峰值流量压一段时间观察 Pod 的实际 CPU 和内存用量然后按峰值用量的 1.2 到 1.5 倍来设置 requests。目标值则根据你的冗余需求来定一般建议 50% 到 70%。目标值越低扩容越早稳定性高但成本高目标值越高资源利用率高但风险大流量一抖就可能打满。4. 自定义指标配置让 HPA 听懂业务语言4.1 为什么默认的 CPU 内存指标不够用到了这一节才真正进入标题里“自定义指标配置”的重头戏。CPU 和内存是资源指标它们只能粗略反映 Pod 的“忙不忙”但反映不了业务的真实的状况。一个典型的场景你的服务是处理消息的消费者 Pod 的 CPU 利用率可能一直很低因为它在等消息队列里的数据大部分时间在阻塞。这时候按 CPU 扩容完全没有意义消息堆积再多也不会触发扩容。正确的做法是按队列长度来扩容。消息队列积压了说明消费能力跟不上生产速度就应该加消费者 Pod。这种指标就是自定义指标它需要从 Prometheus、云厂商监控系统或者业务代码里采集然后通过自定义 metrics API 暴露给 HPA。说白了资源指标是 K8s 看到的自定义指标是业务视角的。HPA 要同时听两边的“汇报”才能做出靠谱的扩缩容决策。4.2 自定义指标的完整链路Prometheus Adapter 是核心桥梁要让 HPA 读自定义指标需要在集群里搭一条完整的链路。这条链路分四段。第一段是数据采集。Prometheus 负责从各个 exporter 或者应用暴露的 /metrics 端点采集数据。比如 Redis exporter 会暴露 redis_up、redis_connected_clients 这些指标。最常被引用的场景就是 Redis 集群的监控。如果你在 K8s 里部署了 Redis 集群想让它按连接数或命令数扩容光看 Pod 的 CPU 是完全不够的因为 Redis 很多时候是 IO 密集型CPU 不高但已经快扛不住了。第二段是数据暴露。Prometheus Adapter 定期从 Prometheus 查询指标然后把指标值以 custom.metrics.k8s.io API 的形式提供给 K8s。这一步非常关键Adapter 里配置了“哪些指标可以给 HPA 用”的规则。第三段是 API 注册。Adapter 通过 API 聚合层把自己注册到 K8s 的 API Server 上。你可以用 kubectl get apiservices | grep custom.metrics 验证。第四段是 HPA 消费。HPA 在 metrics 数组里用 type: Object 或 type: Pods 引用自定义指标。这四段链路里最容易出问题的就是第二段也就是 Prometheus Adapter 的配置。官方文档里的示例比较绕新手很容易在写 discoveryRule 和 metricsQuery 时卡住。我下面给一个可以直接跑的简化配置。4.3 一个可以直接复用的 Prometheus Adapter 配置示例先假设你的场景是Prometheus 里有一个指标名叫 http_requests_per_second它按 Pod 维度打标label 里有 namespace 和 pod。这个值是通过业务代码或 Sidecar exporter 输出的。部署 Prometheus Adapter 的步骤分两部分先准备好 Deployment 和 Service再配上 ConfigMap。我提一下关键点完整 YAML 文件建议你从 Adapter 的 GitHub Release 页面下载改里面的 config 就好。ConfigMap 里核心的规则配置是这样rules: - type: Pods metric: http_requests_per_second resources: overrides: namespace: {resource: namespace} pod: {resource: pod} metricsQuery: sum(rate(http_requests_per_second{.LabelMatchers}[1m])) by (.GroupBy)解释一下这段配置在干什么。type: Pods 表示这个指标按 Pod 聚合HPA 最终拿到的是“所有选中 Pod 的平均值”。如果你的指标是挂在 Service 或者 Ingress 上那就用 type: Object。resources.overrides 告诉 Adapter指标里的 namespace label 对应 K8s 的 namespace 资源pod label 对应 Pod 资源。这样 Adapter 才能根据 HPA 选择的 Pod 列表去过滤指标。metricsQuery 是模板化的 PromQL 查询语句。.LabelMatchers 会被替换成 HPA 关联的 Pod 的过滤条件比如 {namespacedefault, podhpa-demo-xxx}。.GroupBy 会被替换成 pod 这个维度表示按 Pod 分组。rate 函数加上 [1m] 是为了做一个平滑防止瞬时抖动导致扩缩容震荡。配好以后重启 Adapter然后验证kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq这个命令会列出当前暴露出来的所有自定义指标。看到 http_requests_per_second 就说明链路通了。然后创建带自定义指标的 HPA。注意这里用的是 type: Pods目标类型用 AverageValueapiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: hpa-demo-custom spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: hpa-demo minReplicas: 1 maxReplicas: 10 metrics: - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: 100这个配置的意思是每个 Pod 的平均每秒请求数超过 100 就扩容。如果当前有 2 个 Pod总 QPS 是 500每个 Pod 平均 250超过 100期望副本数就是 2 乘以 250 除以 100等于 5。逻辑跟 CPU 完全一致只是数据的来源变了。4.4 关于外部指标和定时扩缩容的补充说明自定义指标再往外延伸还有外部指标 external metrics比如云数据库的连接数、消息队列的积压量、第三方 API 的响应时间。外部指标不绑定任何 K8s 对象HPA 直接读全局值。配置方式上external 类型跟 pods 类型最大的区别是数据不按 Pod 维度聚合而是取整个集群级别的数值。如果你用的是公有云比如阿里云、腾讯云它们通常提供了自己的 Metrics Adapter可以直接把云监控里的指标暴露给 HPA。自己搭 Prometheus 的场景External 指标就要靠 Adapter 的 discoveryRule 来从 Prometheus 查询了。再说定时扩缩容。Twitter 有段时间搞过一个 CronHPA阿里云也有类似实现核心思路是在 HPA 之上再套一层时间规则早上 8 点把 minReplicas 改成 10晚上 10 点改回 2。这是应对确定性流量高峰的常见方案。它不是 K8s 社区的标准组件不同云厂商的实现有差异但原理都是在 metadata 里塞一条 cron 规则本质是控制 HPA 的 min 和 max 边界而不是直接控制副本数。如果你有明确的业务时段比如工作日晚高峰、周末白天流量低CronHPA 值得做。但要注意它跟 HPA 的互动方式要确认好。如果 CronHPA 直接改的是 HPA 的 minReplicas那没问题如果它直接改 Deployment 的 replicas第二天 HPA 一评估就又给改回去了两个组件打架副本数会来回跳。我见过不止一次生产事故是这个原因。4.5 Prometheus Adapter 的选型替代方案市面上还有几个能做自定义指标适配的组件简单对比一下。Prometheus Adapter 是目前最主流的方案文档多、社区活跃、规则配置灵活。它是从 OpenShift 的 metrics-server 项目演化出来的功能上足够覆盖大部分场景。但 PromQL 写起来有门槛新手常常在 build-in 的规则集和自己的业务指标之间迷失。kube-metrics-adapter 是另外一个选择它支持从多个数据源读取指标比如 AWS CloudWatch、Google Stackdriver、Prometheus。如果你是多云或混合云架构统一指标源需求强可以考虑它。但它的社区活跃度比 Prometheus Adapter 低遇到问题可查的资料也少。KEDA 是目前社区热度最高的替代品它不只是做指标适配还把 HPA 的触发器体系扩展到了 50 多种包括消息队列、数据库、HTTP 请求等。KEDA 的架构是它自己充当一个 Metrics Adapter同时引入 ScaledObject 这种新的 CRD 来定义扩缩容规则。它的优点是开箱即用的触发器很丰富配置起来比 Prometheus Adapter 直观。我个人建议已经装了 Prometheus 就用 Prometheus Adapter 就好别重复造轮子如果你需要大量跟外部系统对接比如 Kafka 的 lag、RabbitMQ 的队列长度、MySQL 的活跃连接数优先考虑 KEDA。后面我再写一篇 KEDA 的专门文章来展开它。5. 常见问题排查与避坑指南5.1 集群初始化与 HPA 指标的联动问题热词里有一个很典型的维护场景k8s 控制节点 master 初始化时显示 the api server is not healthy。很多人在初始化单节点集群后用过了一段时间发现 HPA 状态是 unknown以为是 HPA 配错了翻来覆去改配置其实根子出在集群组件就没完全就绪。建议排查步骤先用 kubectl get componentstatuses 检查 etcd、scheduler、controller-manager 这些基础组件的状态。再 kubectl get apiservices 看 metrics API 是否注册成功。如果 apiserver 本身报 unhealthy先看 kubelet 日志和容器运行时日志通常等一等组件自动恢复或者在初始化时确保你的 kubeadm 版本跟容器运行时版本兼容。Rocky Linux 上装 1.36 的话注意 containerd 的版本不要太旧我用的是 containerd 1.7 系列配合上没出过兼容性问题。集群不稳定的时候HPA 控制器自己都跑不转指标链路就更别想了。5.2 HPA 常见问题速查表这里把我在实操中遇到的高频问题整理成一个速查表按问题现象、可能原因、解决办法来列。排查的时候照着顺序过一遍基本能覆盖七八成的场景。现象可能原因排查与解决TARGETS 显示 unknownmetrics-server 未部署或异常kubectl top nodes 和 kubectl top pods 验证基础指标检查 metrics-server 的 Deployment 状态和日志TARGETS 一直显示 0%Pod 没有设置 resources.requests给容器补充 requestsHPA 对没有 requests 的 Pod 不会计算利用率HPA 创建时报 no matches for kindAPI 版本写错或资源不存在确认用 autoscaling/v2确认 scaleTargetRef 里的 kind 和 name 正确明明指标很高却不扩容指标类型配置错误比如用总量当均值确认 target 类型确认自定义指标里的平均计算逻辑扩容后马上缩容来回震荡稳定窗口过短或目标值设置不当调大 scaleDown 稳定窗口调高 target 值自定义指标一直找不到Prometheus Adapter 未注册或规则没生效kubectl get apiservices 检查 custom.metrics.k8s.io 是否 True直接 get --raw 拉 API 看指标是否存在集群初始化时 apiserver 不健康kubelet/容器运行时未就绪或网络组件异常看 kubelet 日志确认 containerd 正常用 kubectl get pods -n kube-system 看核心组件状态5.3 关于 PromQL 和指标语义的三个提醒最后一个部分说一些写 PromQL 时容易踩的坑。这些不仅是新手很多老手也会在上面栽跟头。第一rate 和 irate 的选择。rate 是区间内平均每秒增长率irate 是区间内最后两个样本的瞬时增长率。HPA 场景强烈建议用 rate因为 irate 对瞬时抖动太敏感会导致 Pod 数量跟着毛刺上下跳。你可以想象一个房间的温度计rate 是看五分钟内的平均温度变化irate 是看最后一秒的温度跳动。扩缩容这种控制频率不高的决策需要的是平滑的窗口不是瞬时响应。第二指标打标的维度要对齐。很多人在 Prometheus 里看到的指标名是一样的但 label 组合不一样导致 Adapter 过滤后查不到数据。比如 http_requests_total 这个指标有的 exporter 打的是 instance 标签有的打的是 pod 标签Adapter 规则里写的却是 pod 标签那数据就找不到了。排查手法是把 Adapter 生成的 PromQL 手动在 Prometheus 里跑一遍看看结果是否为空。这条经验能省你很多时间。第三Pod 重启会导致指标断点。自定义指标通常按 pod 维度聚合如果 Pod 重建了旧 Pod 的指标序列会在一段时间后消失新 Pod 的指标从零开始累计。这个切换过程里如果 HPA 刚好在评估可能算出异常值比如突然变 0 导致缩容或者突然飙高导致扩容。针对这个问题一是靠 PromQL 里的 [1m] 窗口平滑二是把 HPA 的冷却时间设长一点给指标一个稳定期。5.4 关于命名空间和指标隔离的一个细节再补充一个很多人容易忽略的点。热词里有人提到了 K8s 中的 namespace这里正好可以串起来聊一下。HPA 是按 namespace 隔离的。你在 default 命名空间创建的 HPA只能读 default 里 Pod 的指标。如果你的业务 Pod 分布在多个命名空间要分别创建对应的 HPA。这个机制本身没问题但配合自定义指标时容易出错。比如你有一个指标 http_requests_per_second在 test 和 prod 两个命名空间里都有。你配置 Adapter 的时候没有给 namespace 做映射结果 HPA 在 test 里也可能读到 prod 的数据扩缩容判断就乱套了。解决方法是一是保证 Adapter 规则里有正确的 namespace 映射具体就是 resources.overrides 里的配置要写对二是测试时尽量用一个命名空间隔离问题。我在测试环境就遇到过 HPA 波动大到离谱的情况最后查出来是 Adapter 的指标过滤规则没有限 namespace把其他项目的指标也包含进来了。6. 最后说点实操感悟这篇文章从 HPA 的基础机制一直讲到了自定义指标的完整链路配置。整个体系梳理下来你会发现它其实不复杂关键就是三件事指标链路通不通、目标值设置合理不合理、扩缩容策略稳不稳。根据我自己的实操经验给准备上 HPA 的朋友几个建议。新项目一开始就建议把资源请求和限制写清楚。这是 HPA 能用的前提也是后续容量规划的基准。没有 requests 的 Pod即使你配置好了 HPA它也只能干瞪眼。HPA 的 target 值建议先从保守档开始。如果你不确定业务高峰的形态不要把目标值设得太低宁可多留一点冗余也不要因为频繁扩容把集群搞出新的问题。等跑一段时间积累了真实负载数据以后再逐步调优。自定义指标别一上来就搞很复杂的需求。先从 CPU 和内存玩起理解了 HPA 的评估节奏和表现以后再去接 Prometheus Adapter。我见过太多人一上来就配 Redis 集群的自定义指标结果被 PromQL 折腾到怀疑人生反而觉得 HPA 不好用。其实 HPA 本身没毛病是链路太长加了很多变量排查难度自然就上去了。还有一个小技巧修改 HPA 配置时不要用 kubectl edit 直接改生产环境的对象建议先把 YAML 保存下来改完以后 kubectl apply 上去。这样出了问题能快速回滚也能看清每次变更的具体内容。HPA 本身不复杂但跟它联动的指标链路、业务代码、监控体系都很复杂变更管理要做好。我自己的体会是HPA 只是弹性能力的起点不是终点。副本数能自动调了接下来还有集群节点级的自动扩缩容、数据面的优雅伸缩、有状态服务的扩缩容策略这些课题。但不管后面走多远HPA 这套“采集、评估、执行”的思路都是理解 K8s 弹性体系的基础。希望这篇能帮你把这第一块基石踩实。
阅读完成 · 觉得有帮助?