指标与告警

本页目录

指标把大量系统事件压缩为可比较的时间序列,告警则把其中一部分变化转化为人的行动。前者首先是测量问题:统计的对象、时间窗和分母是什么;后者首先是决策问题:什么情况需要谁在多久内处理。

本文讨论测量与推理方法。具体 Prometheus 命令见 Prometheus 操作手册,单个请求的因果分析见 分布式追踪。

从用户问题反推测量口径

“服务是否正常”必须具体化。以查询接口为例,可定义:有效请求中,成功且在 300 ms 内完成的比例。这里至少有四个选择:什么是有效请求;哪些返回算成功;延迟从哪里开始计时;统计窗口多长。

应用入口埋点能看到进入应用的请求,却看不到到达应用前的 DNS、TLS 或网关失败。若目标是端到端可用性,需要结合边缘观测或外部探测。把应用内成功率直接称为用户可用性,会遗漏测量边界外的失败。

4xx 是否计入错误也不是固定答案:错误参数可能不属于服务责任,错误鉴权配置导致所有正常用户被拒绝则是服务故障。应先写清业务口径,再写查询表达式。

Counter、Gauge 与分布

类型记录什么典型用法常见误读
Counter累计事件量,重启可能归零请求数、失败数、处理字节数把累计值当成每秒速率
Gauge当前状态,可升可降队列长度、在途请求、剩余空间对任意 gauge 求 rate 并称为吞吐
Histogram观测值的分布及数量延迟、响应大小只看平均值忽略长尾
Summary数量、总和及可选客户端分位数单实例分布统计把多个实例的分位数直接平均

以 Counter 求速率时,应先对每条序列做 rate,再按服务聚合,让重启归零在实例级被处理。下面示例约定 http_requests_total 带 service、instance、status 标签:

# 每服务请求速率
sum by (service) (rate(http_requests_total[5m]))

# 每服务 5xx 比例;只在该服务有请求时解释结果
sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
/
sum by (service) (rate(http_requests_total[5m]))

应预先初始化需要的错误标签序列,或在规则中显式处理缺失;“没有错误序列”与“错误率恰好为零”不是相同的数据形态。完全无流量时分母为零,也不应伪装为可用性百分之百。

先聚合 Counter,会把一个实例的归零扩散到其他实例

假设三次同时抓取,A 的计数为 100、110、5,B 为 100、120、130。A 在后两次之间重启,重启后的值是 5;B 没有重启。按实例分别处理归零,观察到的增量为 A:10+5=15,B:20+10=30,共 45。

若先相加,序列变成 200、230、135。再把下降当成整个聚合计数器归零,得到 30+135=165,把 B 仍然保有的累计值也算成了新事件。

def observed_increase(samples):
    return sum(current - previous if current >= previous else current
               for previous, current in zip(samples, samples[1:]))

A, B = [100, 110, 5], [100, 120, 130]
assert observed_increase(A) + observed_increase(B) == 45
assert observed_increase([a + b for a, b in zip(A, B)]) == 165

这只是离散样本的归零模型,没有实现 Prometheus rate 的时间外推。抓取间隔里重启前发生但未被观察的事件,也无法由这三个样本完整恢复。例子的目的在于保留每条 Counter 的生命周期边界,再聚合速率,而不是从样本制造精确全量账本。

为什么不能平均 P99

假设一个实例处理 100 个请求,另一个处理 10000 个请求。两者的 P99 即使相同,也不能告诉我们把所有请求混合后的精确 P99;分位数丢掉了分布内部的信息。简单平均还额外忽略了样本权重。

经典 Histogram 保存累计桶计数,可以先聚合桶,再估算整体分位数。桶边界必须保留在聚合中:

histogram_quantile(
  0.99,
  sum by (service, le) (
    rate(http_request_duration_seconds_bucket[5m])
  )
)

这里假设各实例使用可兼容的经典桶边界。估计误差与桶宽有关;若目标阈值是 300 ms,配置一个 0.3 秒桶能直接统计阈值内比例,而不必先估 P99。Native Histogram 的表示与查询形式不同,应按实际指标类型选择。见 Prometheus Histograms and Summaries。

均值仍有价值:总耗时除以请求数描述平均工作量;分位数描述尾部体验;超过目标阈值的比例则直接连接服务目标。三者回答的问题不同。

即使按请求数加权,局部分位数仍无法合并

用最近秩定义说明:P99 取排序后第 ceil(0.99*N) 个样本。实例 A 的 10000 个请求均为 1 ms,实例 B 的 100 个请求均为 1000 ms。局部 P99 分别是 1 与 1000,但合并后的第 9999 个样本仍在 A 的快请求里,因此总体 P99 为 1 ms。

from fractions import Fraction
from math import ceil

def p99(values):
    return sorted(values)[ceil(Fraction(99, 100) * len(values)) - 1]

A, B = [1] * 10000, [1000] * 100
assert p99(A) == 1 and p99(B) == 1000
assert p99(A + B) == 1
simple_average = Fraction(p99(A) + p99(B), 2)
weighted_average = Fraction(p99(A) * len(A) + p99(B) * len(B), len(A) + len(B))
assert simple_average == Fraction(1001, 2)
assert weighted_average == Fraction(1100, 101)
assert simple_average != p99(A + B) and weighted_average != p99(A + B)

这不是说 100 个慢请求无关紧要。若目标是识别局部实例劣化,B 的分布很有价值;若目标是总体用户请求的分位数,就必须按总体分布计算。不同统计软件的分位数插值约定有所不同,但都不能普遍通过平均局部分位数恢复全量分位数。

若采用共同的 300 ms 桶,本例阈值内请求比例可直接由两实例累计桶相加得到 10000/10100≈99.01%。它明确暴露约 0.99% 的请求超标,而总体 P99 仍为 1 ms;阈值达标率和分位数各自保留的信息不同。

整体 P99 是排序位置,不能平均两台机器的 P99 实例 A 10000 次 × 1 ms;P99=1 实例 B 100 次 × 1000 ms;P99=1000 总体 10100 次 第 9999 个仍为 1 ms 简单平均 500.5 ms,加权平均约 10.89 ms,均不是总体 P99。 慢请求仍然存在;使用阈值达标率、分位数和实例维度共同解释。

RED 与 USE:服务结果和资源解释

RED 观察 Rate、Errors、Duration,即服务处理多少、失败多少、耗时多久。USE 观察 Utilization、Saturation、Errors,即资源占用、等待压力和错误。前者帮助发现用户侧症状,后者帮助解释资源瓶颈。

对象使用率饱和/等待错误
CPU忙碌时间比例可运行任务排队、CPU pressure相关硬件或调度异常另行观测
内存工作集与可用空间回收、换页、memory pressureOOM 事件、分配失败
存储设备忙碌等利用指标队列深度、等待时间I/O 错误
线程池活跃线程数待执行任务队列拒绝任务、执行失败

OOM score 是进程选择相关信息,不能直接代替内存饱和度。CPU 使用率高也不一定是故障:若吞吐高、队列稳定、延迟达标,可能只是有效利用资源。反之,单线程瓶颈可能让整机平均 CPU 很低,却让请求严重排队。相关方法见 USE Method 与 Google SRE 的监控信号。

SLI、SLO、SLA 与错误预算

SLI 是按确定口径测出的指标;SLO 是希望在一个窗口中达到的目标;SLA 是对外约定及相关后果,三者不能互相替代。

若 30 天请求成功率 SLO 为 99.9%,允许失败比例就是 0.1%。对一百万个有效请求,预算为一千次失败。这是请求数预算,不能直接换算成“允许宕机 43.2 分钟”:请求流量可能随时间变化。只有使用按时间衡量的可用性口径时,分钟换算才有对应含义。

错误预算:消耗速度决定响应紧迫度 目标容许错误率 0.1% 当前观察错误率 1.0% 燃烧率 = 1% ÷ 0.1% = 10 倍 同一测量口径下,持续 10 倍燃烧会以目标容许速度的 10 倍消耗预算。

定义燃烧率 B = 当前坏事件比例 / (1 - SLO)。若 SLO 为 99.9%,当前坏事件占 1%,则 B=10。若测量口径与流量分布近似稳定,持续这种速度,30 天预算约在 3 天内耗尽。窗口变化和流量突变会影响这个预测。

短窗口敏感但容易被毛刺影响,长窗口稳健但恢复慢。多窗口策略把同一燃烧率条件同时施加到长、短窗口,确认问题既持续存在、又尚未恢复。具体阈值应从预算消耗和响应时间推导,参考 Google SRE:Alerting on SLOs。

14.4 倍的阈值可以从预算消耗反算

假设预算窗口是 30 天,即 720 小时,并用稳定流量近似估计消耗速度。若希望“一小时消耗总预算的 2%”触发高优先级响应,阈值满足 B×1/720=0.02,所以 B=14.4。对于 99.9% 的目标,对应观测错误比例是 1.44%。

from fractions import Fraction
window_hours = 30 * 24
burn = Fraction(2, 100) * window_hours / 1
bad_fraction = burn * Fraction(1, 1000)
assert burn == Fraction(72, 5)  # 14.4
assert bad_fraction == Fraction(9, 625)  # 1.44%

def page(long_burn, short_burn):
    return long_burn > burn and short_burn > burn

assert page(20, 20)
assert not page(20, 0)  # 长窗口仍高,近期已恢复
assert not page(2, 20)  # 短毛刺还没有形成同样的长期消耗

短窗可以选长窗的一小部分,用于确认当前仍在快速消耗;这里仅演示组合逻辑,不规定所有服务都应使用相同阈值。对强烈日周期流量或极低请求量,短窗错误比例可能非常不稳定,时间比例也未必准确代表请求预算比例,需结合实际事件量调整告警策略。Google SRE 预算告警方法

同样的燃烧率,为什么预算消耗不同?

燃烧率 B 是比例之比,不是已经花掉的预算百分比。固定窗口总请求数 N、观察段请求数 n、该段坏事件比例 e,预算贡献为 ne/[N(1−SLO)]=B·n/N。只有流量近似稳定,才可进一步用时间占比近似 n/N。下面保持错误比例不变而改变请求量,可以直接看到 B 不变、预算消耗却改变。

正在呈现知识画面
同样的燃烧率,为什么预算消耗不同?

燃烧率比较坏事件比例与允许比例;实际消耗的请求预算还取决于这段时间处理了多少请求。时间占比不能无条件替代请求占比。

从图表到可行动告警

高优先级通知应对应需要及时介入的影响或迫近风险,包括错误、延迟、数据正确性、容量耗尽等。不能规定“只有 4xx/5xx 才通知”:服务返回 200 但延迟数十秒,同样可能严重违背目标。

告警规则应说明服务和责任人、用户影响、观测窗口、优先级、第一步检查入口与恢复判断。短时间波动可以用 for 等持续条件过滤,但延长 for 也增加发现延迟,不能靠无限延后让报表安静。

还需明确缺失数据语义。抓取失败可能让业务序列消失;序列消失不能自动视为业务恢复。应分别监控目标是否可抓取、规则评估是否正常、通知链路是否通畅。

标签基数:一次分类就是一组时间序列

假设一个指标有 20 个路由、5 个状态类别、50 个实例,完整组合已达 5000 条序列。再把十万个用户 ID 放成标签,潜在组合会迅速放大。即使不是所有组合都出现,持续增长的标签也会增加存储与查询压力。

使用稳定路由模板而不是完整 URL;请求 ID、订单 ID 等适合进入日志或追踪,避免用作无界指标标签。单位、命名和标签设计见 Prometheus Metric and Label Naming。

Prometheus 高可用:每个实例都有状态

Prometheus 双副本:独立采集,查询与通知分别去重 监控目标 Prometheus A 独立 TSDB / WAL Prometheus B 独立 TSDB / WAL 查询层 按副本标签去重 Alertmanager 集群 告警聚合与通知去重 每个副本都有本地状态;采集高可用不等于共享存储或自动全链路去重。

Prometheus 保存本地时间序列、WAL 和规则相关运行状态,不能称为无状态服务。双实例通常独立采集同一目标,查询层按配置识别副本、避免重复计算;告警层则单独处理聚合与通知去重。查询高可用与通知高可用不是同一件事。

远程写入、长期存储与本地保留策略又是独立设计维度。双副本并不自动备份所有历史数据,也不保证远程写入永不积压。存储模型见 Prometheus Storage。具体查询后端的副本标签、去重算法和故障行为应按其文档配置。

参考

关键词:Counter、Gauge、Histogram、P99、RED、USE、SLI、SLO、错误预算、燃烧率、标签基数、Prometheus HA