Appearance
指标体系
指标是 Prometheus 体系里最基本的数据单元。在配置 Exporter 和写 PromQL 之前,先理清指标本身的结构——指标名、标签、类型和命名习惯。
一个指标长什么样
在 Prometheus 里,一条指标数据(称为"样本")由四个要素组成:
text
node_memory_MemAvailable_bytes{instance="192.168.10.11:9100", job="node"} 6011232256拆开看:
| 部分 | 值 | 含义 |
|---|---|---|
| 指标名 | node_memory_MemAvailable_bytes | 这行数据测量的是"Linux 可用内存字节数" |
| 标签 | instance="192.168.10.11:9100" | 来自哪台机器 |
| 标签 | job="node" | 属于哪组采集任务 |
| 值 | 6011232256 | 当前可用大约 6GB |
同一个指标名加上同一组标签,就是一条"时间序列"——时间轴上每过一个采集间隔会增加一个新样本点。标签里任何一个值变了,就变成另一条序列。比如 instance="192.168.10.11:9100" 和 instance="192.168.10.12:9100" 是两条不同的序列,因为 instance 不同了。Graph 视图里两条序列各自一条线,Table 视图里两条序列各自一行。
标签和基数
标签是 Prometheus 设计里很关键的一环。标签可以按不同维度过滤和聚合:
promql
# 只看 node job 的
up{job="node"}
# 加起来看 node job 一共几个目标
sum(up{job="node"})但不是所有信息都适合放进标签。标签每多一种取值组合,就多出一条时间序列。如果把用户 ID 放进标签,几万个用户就是几万条序列,Prometheus 的内存、磁盘和查询性能都会被撑爆——这个叫标签基数爆炸。
一个简单的判断方法:这个字段有多少种可能的取值。
| 适合作为标签 | 不适合作为标签 |
|---|---|
| 环境(env:prod/staging/dev)—— 3 种取值 | 用户 ID —— 几万到几百万种取值 |
| 服务名(service)—— 几十种取值 | 请求 ID —— 每次请求都不同 |
| 实例(instance)—— 几十到几百个 | 订单号 —— 和用户 ID 类似,增长很快 |
| 状态码(status:200/404/500)—— 不到 10 种 | 错误堆栈 —— 每次异常内容都不一样 |
请求 ID 和错误堆栈适合放在日志和链路追踪里。指标上需要关联到某次请求时,可以用 exemplar(在指标数据上附带一个 trace_id),而不是把 trace_id 变成指标标签。
四种指标类型
Prometheus 把指标分为四种类型。区分它们的关键是每种类型用什么函数、能不能直接看当前值。
Counter:计数器,只增不减
Counter 记录"到目前为止一共多少次"。请求总数、错误总数、网卡累计接收字节数、CPU 累计运行秒数——这些都是 Counter。
Counter 有两个特点:只增不减(重启后会归零);绝对值本身意义不大——"网卡一共收了 15TB"不说明当前是否在跑满带宽。所以 Counter 通常不直接看当前值,而是算它的增长速率:
promql
# 看最近 5 分钟每秒增长多少——这就是 Mbps
rate(node_network_receive_bytes_total{job="node"}[5m])[5m] 的意思是"取最近 5 分钟的所有样本点",rate() 根据这些样本点的增长来算平均每秒速率。因为 Counter 会因进程重启重新从 0 开始计数(术语叫"reset"),rate() 内部会自动处理这种归零——它看到当前值比上一个值小,就知道发生了一次重置,会按实际增量来算而不是直接相减。
Gauge:仪表盘,可增可减
Gauge 是"当前是多少",和温度计一样,可以升可以降。当前内存用量、当前连接数、当前队列长度、磁盘可用空间——这些都是 Gauge。
Gauge 通常可以直接查当前值,也可以算平均值、最大值:
promql
# 直接看当前可用内存
node_memory_MemAvailable_bytes{job="node"}
# 看所有机器的平均 CPU 使用率
avg(node_cpu_usage_percent)Gauge 没有"归零"的问题,它本来就随时在变。
Histogram:直方图,统计分布
Histogram 用多个"桶"(bucket)来记录数据分布。比如 HTTP 请求耗时,不是只记一个平均值,而是统计:
text
耗时 ≤ 0.1s 有几个请求
耗时 ≤ 0.5s 有几个请求
耗时 ≤ 1s 有几个请求
耗时 ≤ 2s 有几个请求
耗时 ≤ 10s 有几个请求
...直到 Infinity(兜底桶)从这些桶里可以算出 P50、P90、P95、P99——比平均值有用得多。平均值 200ms 可能是"大部分请求 50ms,偶尔几个请求 5 秒"的结果,平均值把问题抹平了。
Histogram 每个桶本身也是一个 Counter(只增不减),所以也需要用 rate() 来算增长。从 Histogram 算 P95 的典型写法:
promql
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))Summary:在客户端侧算好的分位数
Summary 也在记录分位数,但分位数在客户端侧(被监控的应用自己)算好,直接暴露出来。P95 延迟用 Summary 时直接就是一个指标值。缺点也很明显——两台机器的 Summary 没法重新聚合:两台机器的 P95 取平均不代表整体的 P95。Histogram 没有这个问题,因为桶数据可以跨实例加和再算分位。
Prometheus 生态里 Histogram 用得更多,新接入的指标也优先选 Histogram。
| 类型 | 特点 | 例子 | 通常怎么用 |
|---|---|---|---|
| Counter | 只增不减,重启归零 | 请求总数、错误数、网卡字节累计 | rate()、increase() 算变化速率 |
| Gauge | 可增可减 | 当前内存、连接数、磁盘可用 | 直接查当前值,或 avg/max |
| Histogram | 分桶记录分布 | HTTP 耗时、RPC 延迟 | histogram_quantile() 算分位 |
| Summary | 客户端侧分位 | 部分 SDK 暴露的分位值 | 直接看,但跨实例无法聚合 |
RED 和 USE:两个选指标的角度
接入一个新服务时,可以从两个角度来想关注什么指标。
服务对外提供的接口,看 RED:
| 维度 | 含义 | 例子 |
|---|---|---|
| Rate | 每秒请求数 | QPS 曲线是平稳、突增还是骤降 |
| Errors | 错误率 | 5xx 比例、业务失败比例 |
| Duration | 耗时分布 | P95/P99 有没有变长 |
机器自身的资源,看 USE:
| 维度 | 含义 | 例子 |
|---|---|---|
| Utilization | 使用率 | CPU 使用率、磁盘使用率、内存使用率 |
| Saturation | 饱和度(排队) | CPU load、队列长度、等待中的请求数 |
| Errors | 错误 | 磁盘读写错误、网卡丢包、TCP 重传 |
RED 回答"服务是不是在好好干活",USE 回答"机器是不是还能撑住"。接口开始超时时,两边一起看:RED 确认范围和趋势(是单个服务还是全局?是突然的还是缓慢变坏的?),USE 确认资源瓶颈(CPU 满了吗?磁盘 IO 饱和了吗?连接数打满了吗?)。
采集间隔和保留周期
采集间隔决定指标的精细程度。间隔越短,尖刺越不会被抹平——一个持续 10 秒的 CPU 高峰用 15 秒间隔抓取能抓到,用 5 分钟间隔可能就漏了。但间隔越短,存储压力越大。
| 场景 | 常见间隔 | 说明 |
|---|---|---|
| 主机基础指标 | 15s 或 30s | 资源瓶颈通常不会在几秒内出现又消失 |
| 业务接口指标 | 15s 或 30s | 和主机保持一致便于对比 |
| 低频批任务 | 1m | 本身就几分钟才跑一次 |
| 黑盒拨测 | 按接口重要性 | 核心接口可能 10s,低频接口 1min |
保留周期决定能回看多久。实验环境 7~15 天就够了。生产环境如果想回看更久(比如做月度报表、年度容量规划),只靠本地 Prometheus 保存几个月会很占磁盘和查询性能——更常见的做法是 Prometheus 本地保留短期的(几天到几周),远期数据交给 VictoriaMetrics、Thanos、Mimir 这类远端存储。
指标命名习惯
Prometheus 生态里指标名有固定习惯:
| 命名规则 | 例子 |
|---|---|
| 单位和含义写进名字里 | node_memory_MemAvailable_bytes(bytes) |
Counter 类以 _total 结尾 | http_requests_total |
Histogram 桶以 _bucket 结尾 | http_request_duration_seconds_bucket |
_seconds 表示时间 | scrape_duration_seconds |
单位写进指标名能让 Grafana 面板自动识别合适的单位格式,也能避免同一条曲线里混着 s 和 ms 导致误读数值。
从指标到告警
不是所有指标都要变成告警。指标可以有一大堆——排查和观察时需要越丰富越好。告警要往回收——只触发需要人介入的情况。
判断一条告警值不值得设,可以问三个问题:异常能不能被指标稳定表达(还是偶尔出现一次就没了)、持续一段时间会不会恶化(还是自动恢复)、需要有人处理吗(还是系统会自动处理)。三条都满足,告警更有可能有用。缺一条,这条告警就可能变成群聊里的噪声。