Grafana 上一个服务的曲线停在两小时前,Prometheus 的 Status → Targets 里它却还是绿的,up 一直是 1,抓取从没失败过。
另一头,某个 exporter 的 scrape_duration_seconds 连续半小时顶在 10s,离 up 翻成 0 只差一次网络抖动,Targets 页面上却看不出任何异样。
这两个问题的答案都不在 exporter 的指标里,而在 Prometheus 每次抓取时自动写下的那批内置指标里:up、scrape_duration_seconds、scrape_samples_scraped......这些指标不需要部署任何东西。
本篇目标:把抓取链路自带的 8 个内置指标完整拆一遍。
一、这批指标从哪来
1、不是 exporter 给的
机制 :每次抓取结束,Prometheus 都会以 target 的 job、instance 标签为基准,额外生成一批样本写入 TSDB。数据源不是 target 的 /metrics 响应体,而是抓取过程本身。
拿一个还没接任何 exporter 的全新 job 试一下,up 已经在那了:
ini
up{job="nginx"}
up{instance="nginx-gateway:9113", job="nginx"} 1
这批指标和 exporter 指标定位差异:
| 对比项 | exporter 暴露的指标 | 内置抓取指标 |
|---|---|---|
| 来源 | target 的 /metrics 响应体 |
Prometheus 抓取过程本身 |
| 是否要部署 | 要,且按组件各装各的 | 不用,开箱即有 |
| 覆盖范围 | target 内部的状态 | 抓取链路的健康与规模 |
| exporter 挂了会怎样 | 指标跟着消失 | up 变 0,数据仍在 |
| 典型用途 | 业务与资源告警 | 采集链路排障与可用性 |
注意:exporter 挂了,它自己的指标就消失,想用 exporter 的指标监控"采集还在不在",逻辑上是死循环。内置指标是唯一站在 Prometheus 视角看链路的数据,也是其不可替代的原因。
2、8 个指标一张表
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
up |
Gauge | 上次抓取是否成功。2xx 且响应体可解析才记 1,其余一律 0。监控体系自己的第一条告警 |
scrape_duration_seconds |
Gauge | 上次抓取耗时,覆盖发起请求到解析完响应体的全过程 |
scrape_samples_scraped |
Gauge | 上次抓取解析出的样本数(metric relabel 之前的近似值) |
scrape_samples_post_metric_relabeling |
Gauge | relabel 之后真正入库的样本数 |
scrape_series_added |
Gauge | 上次抓取中首次出现的序列数 |
scrape_body_size_bytes |
Gauge | 响应体未压缩大小,无法获知时为 -1 |
scrape_timeout_seconds |
Gauge | 该 target 配置的抓取超时,随配置变化 |
scrape_sample_limit |
Gauge | 该 target 的单次样本上限,0 表示不限 |
三个共同点,后面的 PromQL 都建立在这上面:
- 全是 Gauge,描述"上一次抓取" 。没有
_total后缀,不算累计,不需要rate(),直接画原始值。 - 全部携带 target 的标签 。
job、instance、以及在 scrape config 里加的env等都带,按 target 一套。 - 失败的抓取同样留痕 。
up记 0,scrape_duration_seconds仍会记录这次尝试的耗时,所以"连不上"和"连上但拿不到合法数据",duration 曲线上是两种形状。
注意:
- 官方明确
metric_relabel_configs不作用于这批自动生成的序列。即无论在 relabel 里写什么,up和scrape_*都会原样保留,它们是链路的"黑匣子",保证监控监控系统这件事不依赖任何被监控方的配合。 - 前 5 个默认生成:
up、scrape_duration_seconds、scrape_samples_scraped、scrape_samples_post_metric_relabeling、scrape_series_added。后 3 个需要开启--enable-feature=extra-scrape-metrics:scrape_body_size_bytes、scrape_timeout_seconds、scrape_sample_limit。
3、在哪能看到
三个入口,按快到慢排:
- Status → Targets 页面 :Last Scrape、Duration、Samples 三列,就是
up、scrape_duration_seconds、scrape_samples_scraped的最新值。看单点快,看趋势不行。 - PromQL 直接查 :
scrape_samples_scraped拉出来就是每个 target 的样本数,趋势和对比都在这。 - HTTP API :
/api/v1/targets返回每个 target 的元信息与最近几次抓取结果,写巡检脚本时用。
验证 :拿一个熟悉的 job 对一下数字,Targets 页面上的 Samples 和 scrape_samples_scraped 的最新值应该一致,Duration 和 scrape_duration_seconds 应该一致。若对不上先排查是否写错了标签。
二、核心指标逐个拆
1、up:整个体系的地基
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
up |
Gauge | 上次抓取是否成功。1=HTTP 2xx 且响应体能解析出合法样本;0=连接拒绝、超时、非 2xx、解析失败、样本超限,全部算它 |
up 把五种完全不同的失败压成了一个 0。其价值在于覆盖面无可挑剔,链路上任何一环断掉都会翻 0;但同时也是其局限,翻 0 之后断在哪,要靠 scrape_duration_seconds 的形状来分:
duration低 +up0:秒拒。进程挂了、端口不通、DNS 解析失败。duration高 +up0:超时。对端卡住,耗到了 timeout 才失败。
两个 up 覆盖不了的场景:
- 业务健康 。
up验证的是"抓到了合法的指标数据",不是"服务正常"。应用已经半死,只要/metrics还能返回 200,up就稳定是 1,无法判断服务是否真的活着。 - 序列消失 。删掉一个 job 的配置、或服务发现把 target 清空后,
up序列直接消失而不是变 0。判断"整个 job 不见了"要用absent()。
ini
# 每个 target 近 5 分钟的可用性(0~1)
avg_over_time(up[5m])
# 整个 job 的可用性
avg by (job) (avg_over_time(up[5m]))
# up 抖动:15 分钟内翻转超过 4 次
changes(up[15m]) > 4
# 整个 job 消失(配置误删、服务发现空了)
absent(up{job="nginx"})
分析要点:
- Targets 页面全绿不等于
up稳定。页面是"最后一次抓取"的快照,抖动只能看曲线。 for: 2m的 TargetDown 比for: 0m实用得多。抓取本身有周期,瞬时失败可能是目标重启,连续失败才是链路断了。
2、scrape_duration_seconds:抓取链路的性能指标
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
scrape_duration_seconds |
Gauge | 上次抓取总耗时:TCP 连接、TLS 握手、发请求、等响应、收 body、解析,全程都算 |
scrape_timeout_seconds |
Gauge | 该 target 的抓取超时。全局默认 10s,且不允许大于 scrape_interval,配置校验直接报错 |
scrape_duration_seconds 单独看没有意义,除以 scrape_timeout_seconds 才有,比值是"离失败还有多远"的水位。duration 顶满 timeout,抓取即失败,up 翻 0。但是,scrape_timeout_seconds 不是默认开启指标,从 v2.10 开始通过在启动命令增加 --enable-feature=extra-scrape-metrics 参数开启。
javascript
# 抓取耗时水位:距离超时还有多远(0~1)
scrape_duration_seconds / scrape_timeout_seconds
# 找出最慢的 5 个 target
topk(5, max by (instance, job) (scrape_duration_seconds))
# 耗时突变:相对自身 1 小时前涨了 3 倍
scrape_duration_seconds / (scrape_duration_seconds offset 1h) > 3
分析要点:
- duration 高不一定是 exporter 慢。很多 exporter 的
/metrics是同步查被监控对象的:sql_exporter 要执行 SQL,blackbox 要发完整探测。被监控对象慢,duration 就高,根因常常在被监控的一侧。 - 想把"抓取开销"和"target 响应"分开,可以拿 Prometheus 机器直接
time curl一下 target 的/metrics,两头一比,慢在哪一段就出来了。
3、两个样本数:scraped 与 post_metric_relabeling
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
scrape_samples_scraped |
Gauge | 从响应体解析出的样本数,近似值;是 relabel 处理前的口径 |
scrape_samples_post_metric_relabeling |
Gauge | 经过 metric_relabel_configs 之后真正入库的样本数 |
两者的关系是一条流水线:scraped →(metric_relabel_configs)→ post → 入库。差值就是 relabel 主动丢掉的部分。
通过
scrape_samples_scraped查看 样本数scrape_samples_scraped 是 Prometheus 采集目标时抓取到的样本总数,用来衡量每个 target 暴露了多少数据点,是判断采集数据量大小和发现异常采集目标的核心指标。
- 作用:统计单次抓取从目标上拉取到的 sample(数据样本)数量,反映该目标暴露的指标规模。
- 类型:gauge(仪表盘类型),每个被抓取的目标都会自动生成这个指标。
- 标签 :通常带有
job(任务名)和instance(实例地址)标签,例如scrape_samples_scraped{job="kubernetes-apiservers", instance="10.xxx.182.60:6443"}表示该 apiserver 实例暴露了 29410 个样本。- 自生成:该指标由 Prometheus 在抓取时自动附加,无法通过 metric relabel 配置过滤掉。
常见用途
- 监控采集规模 :通过
topk(5, scrape_samples_scraped)找出样本量最大的 target,定位采集压力来源。- 计算摄取率 :结合时间窗口计算每秒摄取速率,公式为
sum_over_time(scrape_samples_scraped[5m]) / 300,用于评估 Prometheus 的写入压力。- 对比采集前后 :与
scrape_samples_post_metric_relabeling对比,后者是 relabel 后剩余的样本数,两者差值即被过滤掉的样本量。⚠️ 注意: 单个 target 的
scrape_samples_scraped值过大,通常意味着该目标暴露了过多指标,会增加 Prometheus 的内存和存储开销。建议结合scrape_series_added观察新增序列数量,判断是否需要优化采集配置或调整抓取间隔。
该指标族的三个用途:
- 用途一:规模与成本。 TSDB 的写入量 ≈ 所有 target 的 post 之和 ÷ 抓取间隔。容量规划先看它。
- 用途二:找基数大户。 一个 target 的标签基数失控(把 user_id、url、trace_id 塞进标签),它一个就能顶别人一百个。
- 用途三:发现空响应。
up是 1 但样本数是 0,说明链路通、内容空,这也是开篇第一个问题的直接证据。
ini
# 样本大户 top5
topk(5, scrape_samples_scraped)
# relabel 砍掉的比例(长期偏高要回头审规则)
1 - scrape_samples_post_metric_relabeling / scrape_samples_scraped
# 抓到了却没数据:up 为 1 且样本为 0
scrape_samples_scraped == 0 and up == 1
# 样本量环比:对比 1 天前,识别基数漂移
scrape_samples_scraped / (scrape_samples_scraped offset 1d)
分析要点:
- post 比 scraped 小是正常设计,relabel 本来就要丢无效指标。但砍掉比例长期大于 50%,要回头确认是不是误杀。
- 样本数是解析时的近似计数,和 TSDB 里的序列数不严格相等。看趋势足够,对账不必较真。
4、scrape_series_added:序列增长探测器
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
scrape_series_added |
Gauge | 上次抓取中"第一次见到"的序列数 |
scrape_samples_scraped 看的是"这次抓了多少样本",scrape_series_added 看的是"这次有多少条序列是以前没见过的"。样本数大不一定可怕,序列数持续新增才是 TSDB 索引和内存压力的根源。
一条序列 = 指标名 + 完整标签集。比如:
bash
http_requests_total{job="api", instance="10.0.0.1:8080", method="GET", path="/user/1"}
http_requests_total{job="api", instance="10.0.0.1:8080", method="GET", path="/user/2"}
这是两条序列。只要 path 里塞了用户 ID、URL、trace_id,序列数就会爆炸,而 scrape_samples_scraped 可能只是线性增长,scrape_series_added 会直接告诉你"新序列还在源源不断地出现"。
bash
# 最近一次抓取新增序列最多的 target
topk(10, scrape_series_added)
# 按 job 汇总新增序列
sum by (job) (scrape_series_added)
# 新增序列突增:比 1 小时前高 5 倍
scrape_series_added / (scrape_series_added offset 1h) > 5
# 新增序列持续偏高
scrape_series_added > 1000
分析要点:
- 短暂新增可能是发布、扩容、新标签、新实例上线;持续新增通常意味着标签基数失控。
- 和
scrape_samples_scraped联用:- 样本多、新增少:大盘稳定,成本高但可预测。
- 样本不算多、新增多:基数爆炸,最危险。
scrape_series_added是近似值,且序列可能因删除、重启、out-of-order 等因素再次出现。适合看趋势和突发,不适合精确计费。- 如果某个 target 的
scrape_series_added长期大于 0,说明它的标签组合还在膨胀,应该去查 exporter 或业务代码的标签设计。
5、extra-scrape-metrics 三兄弟:body、timeout、sample_limit
这三个指标默认不生成,需要 Prometheus 启动参数开启:
ini
--enable-feature=extra-scrape-metrics
大规模 target 下要评估这点额外开销,但通常远小于它们带来的排障价值。
5.1 scrape_body_size_bytes
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
scrape_body_size_bytes |
Gauge | 最近一次成功抓取响应体的未压缩大小。抓取失败通常为 0;因超出 body_size_limit 失败时为 -1 |
该指标回答的是"这个 target 的 /metrics 到底有多大"。压缩传输小不代表 Prometheus 内存压力小,因为 Prometheus 拿到 body 后还要解压、解析。
bash
# 响应体最大的 target
topk(5, scrape_body_size_bytes)
# body_size_limit 被触发
scrape_body_size_bytes < 0
# 平均每个样本占多少字节(需要先过滤 0 样本)
scrape_body_size_bytes / scrape_samples_scraped > 1000
and scrape_samples_scraped > 0
分析要点:
scrape_body_size_bytes大,通常和高基数、大文本、debug 信息暴露过多有关。- 如果
up=0且scrape_body_size_bytes=-1,优先怀疑body_size_limit配小了,或者 exporter 暴露了异常巨大的响应。 - 压缩率很高的 target,网络传输可能不大,但 Prometheus 解析开销仍然高。
5.2 scrape_timeout_seconds
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
scrape_timeout_seconds |
Gauge | 该 target 配置的抓取超时。默认 10s,且不能大于 scrape_interval |
scrape_duration_seconds 是"实际花了多久",scrape_timeout_seconds 是"最多允许花多久"。只看 duration 无法判断离失败有多近,因为不同 job 的 timeout 可能不同。
bash
# 抓取耗时水位:越接近 1 越危险
scrape_duration_seconds / scrape_timeout_seconds
# 查看非默认 timeout 的 target
scrape_timeout_seconds != 10
分析要点:
- 用绝对值告警
scrape_duration_seconds > 5容易误报,因为有的 job timeout 是 30s,有的只有 5s。水位比值更通用。 scrape_timeout_seconds随配置变化,适合和 duration 放在同一张图里对照。- 如果水位长期大于 0.8,即使
up还是 1,也应该提前处理,否则一次网络抖动就会翻 0。
5.3 scrape_sample_limit
| Prometheus 指标名 | 类型 | 含义说明 |
|---|---|---|
scrape_sample_limit |
Gauge | 该 target 的单次样本上限,0 表示不限 |
sample_limit 是保护 Prometheus 不被单个 target 拖垮的硬限制。超过限制时,整次抓取会失败,up 翻 0。scrape_sample_limit 让你在失败前看到自己离上限还有多远。
bash
# 配置了 sample_limit 的 target
scrape_sample_limit > 0
# 样本数接近上限(超过 80%)
scrape_sample_limit > 0
and scrape_samples_scraped / scrape_sample_limit > 0.8
分析要点:
- 超限会导致抓取失败,而且失败时
scrape_samples_scraped可能不完整,事后不好判断到底超了多少。 - 提前看水位,比等
up=0再查要主动得多。 - 调高
sample_limit不是唯一解。先看是 relabel 没过滤干净,还是 exporter 暴露了不该暴露的高基数指标。
三、告警与记录规则建议
以下供参考,阈值按环境调整。
yaml
groups:
- name: prometheus-scrape
rules:
- alert: TargetDown
expr: up == 0
for: 2m
labels:
severity: warning
annotations:
summary: "Target {{ $labels.instance }} 抓取失败"
description: "job={{ $labels.job }} instance={{ $labels.instance }} 已连续 2 分钟 up=0。"
- alert: TargetAbsent
expr: absent(up{job="nginx"})
for: 5m
labels:
severity: critical
annotations:
summary: "job {{ $labels.job }} 的 up 序列消失"
- alert: ScrapeSlow
expr: scrape_duration_seconds / scrape_timeout_seconds > 0.8
for: 10m
labels:
severity: warning
annotations:
summary: "Target {{ $labels.instance }} 抓取耗时接近超时"
description: "抓取水位已超过 80%,离 up=0 只差一次抖动。"
- alert: ScrapeEmpty
expr: up == 1 and scrape_samples_scraped == 0
for: 10m
labels:
severity: warning
annotations:
summary: "Target {{ $labels.instance }} 抓取成功但样本为 0"
description: "链路通但内容空,检查 /metrics 或 relabel 规则。"
- alert: ScrapeSeriesAddedBurst
expr: sum by (job) (scrape_series_added) > 10000
for: 5m
labels:
severity: warning
annotations:
summary: "job {{ $labels.job }} 新增序列突增"
description: "可能存在标签基数爆炸。"
- alert: ScrapeBodySizeLimitExceeded
expr: scrape_body_size_bytes < 0
for: 5m
labels:
severity: warning
annotations:
summary: "Target {{ $labels.instance }} 响应体超过 body_size_limit"
- alert: ScrapeSampleLimitClose
expr: scrape_sample_limit > 0 and scrape_samples_scraped / scrape_sample_limit > 0.8
for: 10m
labels:
severity: warning
annotations:
summary: "Target {{ $labels.instance }} 样本数接近 sample_limit"
注意:使用 scrape_timeout_seconds、scrape_sample_limit、scrape_body_size_bytes 的规则,必须开启 --enable-feature=extra-scrape-metrics,否则表达式无数据。
四、小总
Prometheus 每次抓取,都会自动写下这批内置指标。它们不来自 exporter,不依赖被监控方配合,是监控采集链路自身的第一手数据。
可按三层理解:
- 可用性层 :
up。抓取成没成,第一条告警。 - 性能层 :
scrape_duration_seconds、scrape_timeout_seconds。抓取多慢,离失败多远。 - 规模层 :
scrape_samples_scraped、scrape_samples_post_metric_relabeling、scrape_series_added、scrape_body_size_bytes、scrape_sample_limit。抓了多少、入库多少、新增多少、响应多大、离限制多近。