K8s集群怎么配置HPA基于QPS自动扩容
摘要:# 当流量突袭时,你的K8s能自己“长个儿”吗?聊聊基于QPS的HPA配置 我前两天刚处理完一个线上事故,一个客户的促销页面突然爆火,QPS(每秒查询率)瞬间冲上平常的十倍。结果呢?他们的K8s集群稳如泰山,Pod(容器组)悄没声儿地就自己“长”出来了,…
当流量突袭时,你的K8s能自己“长个儿”吗?聊聊基于QPS的HPA配置
我前两天刚处理完一个线上事故,一个客户的促销页面突然爆火,QPS(每秒查询率)瞬间冲上平常的十倍。结果呢?他们的K8s集群稳如泰山,Pod(容器组)悄没声儿地就自己“长”出来了,平稳扛过了峰值。而另一个客户,同样配置了弹性伸缩,真到流量洪峰时,扩容动作却慢得像树懒——页面该崩还是崩了。
说白了,问题往往不是“没上自动伸缩”,而是“没配对”。很多文档和教程把HPA(Horizontal Pod Autoscaler)讲得像在配空调遥控器,按几个键就行。但真到了线上,你会发现,基于CPU/内存的伸缩经常“反应迟钝”,等它反应过来,业务早就卡成PPT了。
如果你的业务流量波动大,尤其是Web API、网关这类服务,基于QPS的HPA才是那个“懂你”的伙计。 今天,我就抛开那些标准化的操作手册,跟你聊聊怎么把它配“活”,让它真正成为你业务高可用的“自动挡”。
一、 先泼盆冷水:为什么别只盯着CPU?
很多运维兄弟配HPA,第一步就是 kubectl autoscale deployment --cpu-percent=80。这没错,但有个大前提:你的服务得是“计算密集型”的。
什么意思?比如视频转码、大数据分析,CPU利用率上去了,确实代表活多了,该扩容了。
但咱们搞Web服务的,大部分时候是“I/O密集型”——等着读数据库、调下游接口、排队等锁。这时候,流量(QPS)已经打满了,请求都堵在队列里了,CPU可能还躺在30%睡大觉呢。HPA一看:“CPU挺凉快啊,不用扩容。” 结果就是,用户端体验到的就是请求超时、页面白屏。
(我自己就吃过这个亏,早年给一个电商API配了纯CPU伸缩,大促时监控面板一片绿色,CPU才60%,但客服电话已经被打爆了,全是投诉“加不了购物车”。后来一查,应用线程池全满,数据库连接池耗尽,但CPU就是没跑满。)
所以,基于QPS的伸缩,瞄的是“用户感受到的拥堵”,而不是“服务器的劳累程度”。这思路一转,很多问题就清晰了。
二、 核心装备:你得有个“流量计数器”(Metrics Server + Prometheus Adapter)
K8s的HPA本身是个“决策大脑”,它不会自己数请求。它需要有个“眼睛”时刻盯着流量指标。默认的Metrics Server只提供CPU/内存这种基础资源指标,不管QPS。
怎么办?你需要引入 Prometheus 和 Prometheus Adapter。
- Prometheus:负责从你的应用里(比如通过/metrics接口暴露的指标)抓取数据,存起来。现在这几乎是云原生监控的标配了,没装的兄弟先去装一个。
- Prometheus Adapter:这是个关键翻译官。它把Prometheus里存着的、那些五花八门的业务指标(比如
http_requests_total),翻译成K8s HPA能听懂、能直接使用的API格式(Custom Metrics API)。
说白了,流程是这样的: 你的应用(暴露QPS指标) -> Prometheus(收集存储) -> Prometheus Adapter(翻译) -> HPA(读取指标并决策) -> 伸缩你的Deployment。
很多教程到这就停了,但坑才刚刚开始。
三、 动手配置:从“能跑”到“跑得好”
假设你已经装好了Prometheus和Prometheus Adapter(安装过程网上很多,这里不赘述,关键是Adapter的配置)。咱们直接看最核心的几步。
第一步:让你的应用“报数”
首先,你的应用得告诉Prometheus现在有多少QPS。对于Spring Boot、Gin、Express这些主流框架,通常加个监控客户端库(比如 micrometer、prom-client)就行,它会自动暴露一个叫 http_server_requests_seconds_count 之类的指标,里面按路径、方法、状态码统计了请求次数。
你可以在Prometheus里查一下,确认有这个数据:
# PromQL 查询语句
sum(rate(http_server_requests_seconds_count[1m])) by (service)
能看到你的服务每分钟的请求速率(RPS),乘以60就是大概的QPS。
第二步:教Adapter“翻译”
这是最需要动脑子的地方。Adapter的配置核心是一个 config.yaml,你需要告诉它:“把Prometheus里这个 http_requests_total 的指标,映射成K8s里一个叫 http_requests_per_second 的指标。”
一个简化版的配置片段长这样:
rules:
- seriesQuery: 'http_server_requests_seconds_count{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "http_server_requests_seconds_count"
as: "http_requests_per_second"
metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[1m])) by (<<.GroupBy>>)'
关键解读(别跳过):
seriesQuery:在Prometheus里找哪个指标?这里找的是带namespace和pod标签的请求计数指标。name:把它重命名成什么?这里叫http_requests_per_second,HPA后面就用这个名字。metricsQuery:最关键的公式! 它定义了怎么计算最终值。rate(...[1m])是计算过去1分钟的平均每秒请求率(RPS)。sum by是按Pod维度汇总。这个公式直接决定了HPA看到的数值是否准确、平滑。
第三步:创建HPA,盯紧QPS
好了,现在K8s已经能看懂QPS指标了。我们来创建HPA:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: your-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: your-app-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second # 这就是Adapter翻译好的指标名
target:
type: AverageValue
averageValue: 100 # 每个Pod平均承受100 QPS
这个配置的意思是人话就是: 盯着 your-app-deployment 这个部署,保证它永远有至少2个、最多10个Pod副本。HPA会不停地看每个Pod当前的实际QPS(由Adapter提供),如果所有Pod的平均QPS超过了100,它就自动增加Pod数量,把每个Pod的负载降下来;反之,如果平均QPS远低于100,它就减少Pod,省点资源。
这个 averageValue: 100 就是你的目标水位线,需要根据你单Pod的压测能力来定。比如你压测发现单个Pod扛到150 QPS时响应时间就开始飙升,那就可以设成100-120,留点安全余量。
四、 几个让你少踩坑的“私货”经验
配置写完了,但离“好用”还差几步。下面这些是文档里不常提,但真金白银换来的经验:
- 指标“毛刺”与平滑窗口:网络请求常有突发毛刺。如果你用
[1m](1分钟窗口)计算速率,可能会被瞬间尖峰误导,导致HPA频繁无效伸缩。可以适当调大为[2m]或[3m],让曲线更平滑。这对应HPA的--horizontal-pod-autoscaler-sync-period和--horizontal-pod-autoscaler-downscale-stabilization参数(默认都是5分钟和5分钟),理解它们能避免集群“抽风”。 - 冷却时间(Cooldown)是门艺术:扩容上来了,不能马上缩下去,反之亦然。K8s有默认的冷却时间(水平扩缩容默认冷却300秒/5分钟)。对于流量快速波动的场景(比如秒杀),你可能需要调短扩容冷却,让它反应更快;同时保持或加长缩容冷却,防止在波动区间反复横跳。
- 别忘了给Node留余地:HPA只管生Pod,不管Node有没有地方住。如果集群Node资源满了,Pod扩出来也会是
Pending状态。所以要么确保集群有足够的节点资源池(配合Cluster Autoscaler),要么给HPA设的maxReplicas保守一点。 - 监控,监控,还是监控:一定要把HPA的事件和状态监控起来。
kubectl describe hpa是你的好朋友。看看它当前看到的指标值是多少,是不是你预期的?扩缩容历史记录是怎样的?很多时候问题出在指标数据不对,或者水位线设得太离谱。
五、 说点大实话:它也不是万能药
基于QPS的HPA很好,但它解决的是“资源层”的弹性。如果瓶颈在数据库连接池、在下游服务、在某个全局锁,那Pod扩得再多也没用,反而可能把下游打挂。
真正的业务连续性,是立体防护: QPS-HPA负责扛住流量洪峰,WAF和DDoS高防在前头过滤恶意请求,合理的限流熔断(如Sentinel)在应用层做保护,再加上数据库连接池、缓存、异步化等应用架构优化,才能形成一个完整的弹性体系。
行了,配置和门道大概就聊这些。核心思路就是 “让自动化的眼睛,盯在业务最真实的压力上”。别再让你那宝贵的K8s集群,只因为CPU没跑满,就眼睁睁看着业务卡死了。去试试吧,配好了之后,那种看着集群自己从容应对流量起伏的感觉——真挺爽的。

