一、先搞懂:为啥好好的Prometheus会被拖垮?
很多做运维或者后端开发的朋友,肯定都遇到过这种糟心事:本来跑的好好的监控系统,突然某天开始告警“采集超时”“数据丢包”,一查Prometheus的面板,指标量直接爆了——原来还能流畅拉数据的监控,现在卡得像旧电脑装了大型游戏,连看个CPU使用率都要等半分钟。
其实核心问题不是Prometheus本身不行,而是指标基数爆炸。啥是指标基数?举个最通俗的例子:你开了一家奶茶店,每天要统计的核心指标是“每杯奶茶的销量”,这个指标的“基数”就是所有卖过的奶茶的集合——比如今天卖了10杯,基数就是10;如果明天搞活动,加了“杯型(大/中/小)”“甜度(全糖/半糖/三分糖/无糖)”“冰度(热/温/冰/去冰)”这三个标签,那每杯奶茶的标签组合就变成了4种杯型×4种甜度×4种冰度=64种,基数直接翻了6倍还多。
换到技术场景里,最容易炸的就是微服务的标签。比如你的服务有100个实例,每个实例的请求量指标加了“接口路径”“HTTP状态码”“用户ID”“地域”这四个标签,每个标签的取值哪怕只有10种,那单个服务的这个指标基数就变成了100×10×10×10×10=100万——这还只是一个服务的一个指标!要是有几十个服务,Prometheus要采集、存储、计算的量,直接就从“能扛”变成“扛不住”了。
更坑的是,很多时候这种基数爆炸是无意识的:比如开发同学为了排查问题,临时给某个指标加了个“trace_id”标签,结果trace_id是唯一的,那这个指标的基数直接就和请求量一样了——一天百万请求,就有百万个基数,Prometheus瞬间就被压垮了。
二、核心解法:用Envoy做“指标守门员”
既然问题出在指标基数上,那解法就很直接:在指标传到Prometheus之前,把不该留的标签去掉,把没用的指标删掉。但问题来了:每个服务自己改代码删标签、关指标,不仅麻烦,还容易漏——比如A服务改了,B服务忘了,还是会炸。
这时候Envoy就派上用场了。很多做微服务的朋友都知道Envoy是个代理,但它的“监控能力”才是解决这个问题的关键:所有服务的流量都要经过Envoy,那Envoy就可以在指标流到Prometheus之前,先做一遍“过滤”——相当于给Prometheus请了个守门员,只放有用的指标和标签进去,把垃圾挡在外面。
而且用Envoy做过滤还有个大好处:不用改任何服务的代码!所有过滤规则都在Envoy的配置里统一管理,改一次所有服务都生效,既省心又规范。
三、具体操作:动态标签聚合+指标裁剪,一步一步来
接下来我们用具体的例子,把这两个操作讲透。首先明确我们用的技术栈:Envoy(v1.28版本)+ Prometheus(v2.47版本),所有配置都是可直接复制的。
3.1 先搞懂两个核心操作的区别
先给大家划个重点,避免搞混:
- 动态标签聚合:不是把标签删掉,而是把标签的取值“合并”。比如原来的标签是“用户ID=123、456、789”,聚合后变成“用户ID=other”——相当于把小的、不重要的取值归成一类,减少基数。
- 指标裁剪:直接把没用的指标删掉。比如某个服务的“调试用的内存占用指标”,平时根本不用看,直接让Envoy把这个指标拦下来,不让Prometheus采集。
3.2 动态标签聚合:把小基数的标签取值归成一类
我们先举个真实的场景:某个电商服务的请求量指标,加了“user_id”标签,这个标签的取值是每个用户的ID,大部分用户的请求量都很少(比如只访问一次的游客),只有少数核心用户的请求量很大。如果直接保留所有user_id,基数会爆炸;但如果直接删掉user_id标签,又没法统计核心用户的请求量。
这时候动态标签聚合就完美解决这个问题:我们只保留请求量前100的user_id,剩下的所有user_id都归成“other”。
具体的Envoy配置如下(先看配置,后面会解释每个部分):
# Envoy的全局配置片段,重点看metrics的配置
metrics:
stats_sinks:
- name: envoy.metrics_service
typed_config:
"@type": type.googleapis.com/envoy.config.metrics.v3.MetricsServiceConfig
# 先做标签聚合
tag_key: user_id
# 聚合规则:只保留请求量前100的user_id,其他归为other
aggregation_strategy:
top_n:
n: 100
# 统计的是每个user_id对应的请求量(envoy_http_requests_total这个指标的数值)
metric_name: envoy_http_requests_total
# 聚合后的标签值用"other"代替
default_value: "other"
我们来解释下这个配置的逻辑:
tag_key: user_id:指定要聚合的标签是user_id;top_n: n:100:只保留该标签下请求量最高的前10个取值;metric_name: envoy_http_requests_total:告诉Envoy,统计的是“每个user_id对应的请求量”这个指标;default_value: "other":剩下的所有user_id,都把标签值改成“other”。
这么一来,原来可能有10万个user_id的标签,现在最多只有101个(100个核心user_id+1个other),基数直接降了1000倍,还保留了核心用户的统计能力。
3.3 指标裁剪:直接删掉没用的指标
再举个场景:某个服务有个调试用的指标“envoy_debug_memory_usage”,平时运维根本不会看,只有开发排查问题的时候才会临时打开,而且这个指标的标签特别多,基数很大。我们要做的就是让Envoy把这个指标直接拦下来,不让它传到Prometheus。
具体的Envoy配置如下:
# Envoy的metrics配置片段,重点看裁剪规则
metrics:
stats_sinks:
- name: envoy.metrics_service
typed_config:
"@type": type.googleapis.com/envoy.config.metrics.v3.MetricsServiceConfig
# 指标裁剪规则:直接删掉指定的指标
metric_filters:
# 要裁剪的指标名称
- name: envoy_debug_memory_usage
# 过滤类型:DROP表示删掉这个指标,不让它传到Prometheus
type: DROP
如果我们要裁剪多个指标,直接加多个metric_filters条目就行。比如还要删掉调试用的CPU指标:
metric_filters:
- name: envoy_debug_memory_usage
type: DROP
- name: envoy_debug_cpu_usage
type: DROP
反过来,如果我们要只保留几个核心指标,其他都删掉,就把type改成ACCEPT,比如:
metric_filters:
# 只保留请求量、延迟、错误率这三个核心指标
- name: envoy_http_requests_total
type: ACCEPT
- name: envoy_http_request_duration_seconds
type: ACCEPT
- name: envoy_http_request_errors_total
type: ACCEPT
3.4 组合操作:同时做聚合和裁剪
实际场景里,我们一般会同时做标签聚合和指标裁剪,比如先把没用的指标删掉,再把有用的指标的标签做聚合。具体的完整配置片段如下:
# Envoy的完整metrics配置片段
metrics:
stats_sinks:
- name: envoy.metrics_service
typed_config:
"@type": type.googleapis.com/envoy.config.metrics.v3.MetricsServiceConfig
# 第一步:先做指标裁剪,删掉没用的调试指标
metric_filters:
- name: envoy_debug_memory_usage
type: DROP
- name: envoy_debug_cpu_usage
type: DROP
# 第二步:再做标签聚合,把user_id的标签归成核心+other
tag_key: user_id
aggregation_strategy:
top_n:
n: 100
metric_name: envoy_http_requests_total
default_value: "other"
四、避坑指南:这几个问题一定要注意
做标签聚合和指标裁剪的时候,有几个很容易踩的坑,我给大家总结一下:
4.1 不要随便聚合核心标签
比如“HTTP状态码”“接口路径”这种标签,是我们排查问题的核心依据,绝对不能随便聚合。比如你把状态码的“404”“500”都归成“other”,那以后出问题了,连错误类型都不知道,根本没法排查。
4.2 聚合规则要定期调整
比如原来的核心用户只有100个,后来业务发展了,核心用户变成了500个,那你就要把top_n的n改成500,不然会把新的核心用户归成“other”,导致统计不准。
4.3 裁剪指标要留“回滚机制”
比如你要裁剪某个指标,先不要直接改成DROP,而是先改成ACCEPT,只保留这个指标,观察一周没问题后,再改成DROP。或者你可以在Envoy的配置里加个开关,需要的时候可以快速恢复某个指标。
4.4 不要过度裁剪
比如有些指标平时不用,但做业务分析的时候会用到,比如“每个地域的请求量”,如果你把这个指标裁剪了,那以后做分析的时候就拿不到数据了。所以裁剪之前,一定要和业务、开发的同学确认清楚,哪些指标是真的没用的。
五、应用场景、优缺点总结
5.1 适用的应用场景
- 微服务架构下,Prometheus采集的指标基数过大,导致性能下降;
- 开发同学临时加的调试标签,导致指标基数爆炸;
- 服务的标签取值太多,比如user_id、trace_id这种唯一取值的标签;
- 需要统一管理所有服务的指标规则,避免每个服务自己改代码的麻烦。
5.2 技术的优缺点
- 优点:
- 不用改任何服务的代码,所有规则统一在Envoy配置里管理;
- 能有效降低指标基数,解决Prometheus性能问题;
- 可以灵活调整聚合和裁剪规则,适应业务变化;
- 不影响服务本身的运行,只对监控的指标流做处理。
- 缺点:
- 对Envoy的版本有要求,比如动态标签聚合的功能需要v1.20以上的版本;
- 如果聚合规则设置不当,会导致监控数据不准,影响问题排查;
- 对于已经存在的基数爆炸问题,需要重新调整规则,有一定的学习成本。
六、总结
指标基数爆炸是Prometheus监控系统的常见问题,很多人遇到这个问题只会想着升级Prometheus的配置、加更多的服务器,其实更高效的解法是从源头控制指标的数量和基数。
用Envoy做动态标签聚合和指标裁剪,相当于给Prometheus请了个专业的守门员,只让有用的指标和标签进去,把垃圾挡在外面。这种方法不仅不用改服务代码,规则统一管理,还能有效解决Prometheus的性能问题。
只要大家掌握了动态标签聚合和指标裁剪的核心逻辑,再结合具体的场景调整规则,就能轻松解决指标基数爆炸的问题,让Prometheus重新流畅起来。
评论
围绕“指标基数爆炸拖垮Prometheus采集链路,Envoy动态标签聚合与指标裁剪的正确打开方式彻底说清”参与讨论