楠风测开 · 测试工程师复习日记 Day14
📊 Prometheus + Grafana 实践:指标口径、告警链路与 AI 异常检测
前言
朋友们好,我是楠风。
Day 11---13,我们复习了 Linux 命令、AI 辅助写 Shell 和性能瓶颈排查。今天把这些观察能力接入一套持续工作的监控体系。
先看一个教学场景:创建订单接口持续返回 500,CPU 却只有 20%。看板能看到错误,告警群里没有消息。
如果规则写成"CPU 超过 80%,并且错误率超过 5%",这个结果就不奇怪。资源没有打满,接口故障仍然可能发生。
这篇围绕已有 Prometheus、Grafana 和 Alertmanager 环境,讨论指标怎么定义、规则怎么写、通知怎么验收,以及 AI 检测任务接在哪里。配置中的阈值用于演示,需要按业务调整。
一、先把监控链路画清楚
text
Linux node_exporter ─┐
├→ Prometheus ─→ Grafana 看板
应用 /metrics ──────┘ │
规则评估
↓
Alertmanager
↓
接收渠道 / 适配服务
↓
负责人排查与恢复
node_exporter 提供主机资源指标,应用另行暴露请求量、耗时和结果。Prometheus 抓取、存储并评估规则;Grafana 查询这些数据;Alertmanager 负责告警分组、去重和路由。这里的通知不依赖 Grafana 转发。Prometheus 架构
Grafana 的数据源地址必须能从 Grafana 所在环境访问。容器内的 localhost 指容器自身,不能直接照搬宿主机访问地址。数据源配置
看板至少摆上请求速率、错误比例、P95 延迟和资源使用情况。面板共用服务筛选与时间范围,异常发生时才能对照。日志里的服务名、时间和请求 ID 则用于继续定位原因。
二、动手前先约定指标口径
以 POST /orders 为例,约定在请求结束时递增一次计数器:
text
http_requests_total{
service="order-demo",
route="/orders",
method="POST",
status="500"
}
本文假设抓取任务为 order-demo,同一实例不会被重复采集。多个实例可以汇总,但不能混入其他环境的数据。
这里有三个细节:
- 路由使用模板。 例如
/orders/{id},别把每个订单 ID 都放进标签;请求 ID 留在日志里。 - 失败和总量保持同一口径。 同一服务、路由、方法和时间窗口,才有可比较的错误率。
- 初始化已知状态序列。 应用启动时把已知的 200、500 等状态计数初始化为 0 并持续暴露;否则"没有错误序列"与"错误数为零"不同。官方埋点建议
接口返回 200 但订单没创建,属于另一类业务失败。HTTP 指标之外,还需要业务结果埋点或探测,不能让状态码替业务下结论。
汇总多个实例时,还要避免"平均错误率"。假设实例 A 收到 100 次请求、失败 20 次,实例 B 收到 900 次请求、全部成功,整体错误率是 20 ÷ 1000,也就是 2%;把两个实例的比例直接平均,会得到 10%。
整体比例回答的是服务总体情况。它也可能掩盖单个实例的问题,因此保留实例级看板,必要时另设实例规则,才能从服务现象继续定位。
三、写一条能够解释的告警
目标先用自然语言表达:订单创建接口最近 5 分钟的 5xx 比例超过 5%,平均流量至少为每秒 1 次,且条件连续成立 1 分钟。
规则文件 order-alerts.yml:
yaml
groups:
- name: order-api
rules:
- alert: OrderApiHigh5xxRate
expr: |
(
sum by (service, route, method) (
rate(http_requests_total{
job="order-demo", service="order-demo",
route="/orders", method="POST", status=~"5.."
}[5m])
)
/
sum by (service, route, method) (
rate(http_requests_total{
job="order-demo", service="order-demo",
route="/orders", method="POST"
}[5m])
)
> 0.05
)
and on (service, route, method)
(
sum by (service, route, method) (
rate(http_requests_total{
job="order-demo", service="order-demo",
route="/orders", method="POST"
}[5m])
) >= 1
)
for: 1m
labels:
severity: warning
annotations:
summary: "订单创建接口5xx比例持续偏高"
description: "检查请求指标、应用日志和依赖服务"
Prometheus 的 rule_files 需要加载这个文件,主配置还要指向 Alertmanager;Alertmanager 再按接收器配置通知渠道。
这里先对每条计数器序列求 rate,再汇总到相同标签维度,既统一分子分母,也保留对计数器重置的处理。不能拿每个状态码的原始速率直接相除。rate 用法
两处设计取舍要写进测试用例:
第一,流量门槛减少小样本波动,也会排除低流量故障。支付等低频关键操作,需要额外的业务失败规则或主动探测。
第二,[5m] 是计算窗口,for: 1m 是表达式持续成立的时间。它既不是"第一次 500 后一分钟发消息",也不能机械地算成六分钟;还涉及采集、评估与通知调度。告警规则说明
CPU 异常单独设规则。是否响应接口故障,不以 CPU 同时升高为前提。
四、通知与恢复需要分别验收
建议在可控测试环境按"正常请求 → 注入故障 → 撤掉故障"运行,记录开始报错、条件成立、告警触发、消息送达四个时间点。
看到 firing 只说明规则已触发。还要检查 Alertmanager 的路由、静默状态、接收器与下游服务。通用 webhook 的请求格式不一定匹配群机器人,必要时需要适配服务;恢复通知也应在接收器中明确配置。Alertmanager 配置
接收人拿到消息后,应该知道哪个环境、哪个服务、哪个接口出问题,以及先看哪里。可以在通知模板中带上标签、触发值、看板地址和排查入口。只有"服务异常"四个字,即使送达,也会把定位工作重新推给接收人。
撤掉故障后继续发送成功请求,等待历史失败样本逐步离开窗口,再观察解除与恢复消息。
告警消失不能单独证明业务恢复。 零请求、采集失败和目标被移除也要区分:up == 0 能检查已有抓取目标的失败,目标缺失则需结合预期目标与 absent_over_time 等方法另设规则。缺失序列检测
五、🤖 AI 异常检测接在哪里
固定阈值先覆盖明确故障,再让模型发现偏离历史表现的时间段。例如 P95 从平时的 100ms 升到 250ms,尚未到 500ms 阈值,是否值得关注要结合流量和历史变化判断。
可以先用简单动态基线做对照,再试验 Isolation Forest。AI 帮忙写出均值、标准差计算代码,并不会让统计规则自动变成机器学习模型。
为每个服务建立同一时间窗口的样本,特征可以是请求速率、5xx 比例、P95 延迟与关联主机 CPU 使用率。单位、采样时间和聚合口径必须固定;服务到主机的映射不明确时,先去掉 CPU 特征。
下面是已准备好数据后的核心流程:
python
from sklearn.ensemble import IsolationForest
# 两个数组均为 [样本数, 特征数],列顺序和单位完全一致。
# normal_train 来自较早、已核对正常的时间段。
# later_eval 来自后续独立时间段,包含标注的正常与故障样本。
model = IsolationForest(random_state=42)
model.fit(normal_train)
flags = model.predict(later_eval)
predict 的 -1 表示模型判为异常,1 表示模型判为正常。这个判断不是故障根因,也不是已经校准的故障概率。Isolation Forest 说明
接入时需要实现数据整理、定时推断和指标暴露:
text
Prometheus 查询 → 时间对齐与数据校验 → 定时推断
→ 暴露异常标记、最近成功评分时间 → Prometheus 采集
→ 规则评估 → Alertmanager → 人工复核
查询返回多条序列时,按标签匹配,不能直接取第一个结果。缺失和无穷值应单独处理,不能补零后当作正常样本;没有有效请求时,P95 也可能无法计算。
模型服务还要暴露"最近一次成功评分的时间戳"。只看进程心跳,可能漏掉"程序活着但评分卡住";只看异常标记,又可能把残留的旧结果当成当前状态。评分超时、采集失败、指标缺失都需要覆盖。
上线前按时间切分数据,别把同一故障前后相邻样本随机混进训练集与验证集。用正常高峰、发布变更、故障演练、采集缺失和恢复过程评估,并与现有阈值规则对照:
- 同一观察周期内,产生多少次需要人工查看的误报?
- 已标注的故障事件,有多少漏掉?
- 从故障开始到首次有效告警,延迟多少?
比较误报时,还要先统一计数方式:连续十分钟判为异常,是一次异常事件,还是十个采样点?把相邻异常合并为事件后再统计通知数量,才更接近人的处理负担。漏报则对照独立标注的故障事件,不能让模型自己定义"哪些是真故障"。
业务上线新功能或流量结构变化后,旧训练数据可能不再代表当前正常状态。保留特征定义、模型版本和训练时间段,定期复核;夜间与白天的差异也需要显式设计,模型不会自动理解业务日历。
先让模型结果进入观察看板,人工复核后再决定是否参与通知。多指标同时变化能提供排查线索,仍需要日志或实验来确认原因。
六、📋 留一份验收清单
| 场景 | 核对重点 |
|---|---|
| 正常流量 | 指标持续更新,规则不误触发 |
| CPU 正常但接口持续报错 | 达到规则条件后,通知送到正确接收人 |
| 短暂错误与低流量 | 验证持续时间及流量门槛的实际影响 |
| 撤掉故障 | 错误比例回落,告警解除,另验恢复通知 |
| 采集失败与目标移除 | 分别触发对应检查,不当成恢复 |
| AI 评分卡住 | 成功评分时间停止更新,任务异常被发现 |
每次演练保存规则版本、故障时间和通知记录。后续调整阈值或模型,再用相同场景比较,才能知道改动补上了什么、又引入了什么遗漏。
下期预告
Day15 · MySQL 基础 + AI SQL 优化
继续沿着订单接口往下查:返回 200,数据库里却没有新增订单,该怎样写 SQL 校验?再让 AI 辅助改写查询,用结果与执行计划验证改动。
🌱 楠风测开 · 把测试方法用到具体问题里。