用 Prometheus metric_relabel_configs 在抓取阶段丢弃高基数标签
用 metric_relabel_configs 在抓取阶段丢弃高基数标签
问题场景
某个业务的 /metrics 里带了请求级别的标签:trace_id、request_id、user_id,还有 url 这种把路径参数直接拼进去的标签。每个不同取值都会产生一条新序列,序列数会随流量线性膨胀。随之而来的典型现象是:Prometheus 常驻内存一路上涨,/api/v1/series 响应越来越慢,面板查询从百毫秒级掉到秒级,WAL 与数据块也明显变大。
这类标签对聚合没有贡献——sum by (path, code) 根本用不到 trace_id。比起事后在 PromQL 里用 without / ignoring 处理,更划算的做法是在样本写入 TSDB 之前就把它们丢掉。metric_relabel_configs 就是承担这件事的阶段。
两个 relabel 阶段的分工
配置里有两个同名但位置不同的 relabel 段:
relabel_configs:抓取之前,作用于 target 的标签(__address__、__meta_*等),用来筛目标、改地址、拼标签。metric_relabel_configs:抓取响应解析完成、写入 TSDB 之前,作用于每一条样本的标签集合(包含__name__),用来丢指标、丢标签。
下面只讨论第二个。
最小可运行示例
global:
scrape_interval: 15s
scrape_configs:
- job_name: demo-app
metrics_path: /metrics
static_configs:
- targets: ["10.0.0.11:8080"] # 占位地址,替换成自己的实例
metric_relabel_configs:
# 1) 按标签名丢弃:注意 regex 匹配的是标签名,不是标签值
- action: labeldrop
regex: "trace_id|request_id|session_id|user_id"
# 2) 按指标名丢弃整条指标
- action: drop
source_labels: [__name__]
regex: "debug_.*|go_gc_.*"
# 3) 按标签值丢弃:只保留已知的几个取值
- action: drop
source_labels: [status]
regex: "unknown|other|none"
如果需要白名单模式,把第 2 步换成正则范围足够窄的 keep:
- action: keep
source_labels: [__name__]
regex: "http_requests_total|process_resident_memory_bytes|up"
几个容易踩的点:
- relabel 里的正则是整串锚定的,
trace_id只匹配名为trace_id的标签,不会匹配trace_id_xxx。 - 规则从上到下顺序执行,后面的规则看到的是前面处理后的结果。把
keep白名单写在drop后面,效果会和预期不一致。 labeldrop是把这个标签名从该次抓取的所有样本上摘掉,同一 job 内无法只针对某一条指标;只想处理特定指标时,用source_labels: [__name__, trace_id]组合条件配合drop更精准。up、scrape_duration_seconds这类抓取元数据由 Prometheus 自身生成,不受metric_relabel_configs影响。
验证:序列数与时延
热加载需要开启 lifecycle 参数:
prometheus --config.file=prometheus.yml --web.enable-lifecycle
改完配置后触发 reload:
curl -s -X POST http://127.0.0.1:9090/-/reload
统计某条指标的序列数(返回数组长度即序列条数):
curl -sG http://127.0.0.1:9090/api/v1/series \
--data-urlencode 'match[]={__name__="http_requests_total"}' \
| jq '.data | length'
对比查询耗时,取接口返回的统计字段而不是凭感觉:
curl -sG http://127.0.0.1:9090/api/v1/query \
--data-urlencode 'query=sum by (path, code) (rate(http_requests_total[5m]))' \
--data-urlencode 'stats=all' \
| jq '.data.stats.timings'
建议在负载相近的时间点各测一次:一次在 reload 之前,一次在 reload 之后跑过若干个抓取周期再测,分别记录序列数与 evalTotalTime。需要注意热加载只是改变写入行为,历史数据里的旧序列仍然存在,要等 --storage.tsdb.retention.time 到期清理才会消失,所以序列数对比最好限定在新写入的时间窗口上,不要拿含历史数据的区间直接比较。
边界与限制
- 抓取端仍然要拉取并解析完整响应,网络带宽与解析的 CPU 开销不会因此下降,节省的是存储、内存和查询时的序列基数。
metric_relabel_configs只能依据标签名、标签值和指标名判断,无法按指标取值的大小做丢弃。- 丢标签等于合并序列,聚合结果的维度会变少,已有的 recording rule、告警表达式和面板需要用新的标签集重新核对。
histogram的le、summary的quantile不能被删除,否则指标语义完全失真。- 正则每次抓取都会执行,避免写成
.*这类过于宽泛的匹配,也别把大量无关规则堆在同一个 job 上。 - 丢弃标签前先确认该标签没有用于路由、分片或外部对齐,例如用
instance做多副本去重时要保留它。
总结一下:把高基数标签的处理从查询侧前移到抓取侧,配置量很小,但需要配合序列数与查询耗时的前后对比来确认效果,并对聚合语义的变化做一次完整回归。
- 点赞
- 收藏
- 关注作者
评论(0)