资讯动态

FastAPI 监控告警实战:Prometheus 阈值与 Alertmanager 路由配置

发布时间:2026/9/16 3:21:31 来源:尧图企业网站定制
你有没有在凌晨三点被手机震醒过我有而且不止一次。第一次印象特别深当时一个 FastAPI 服务的内存告警在半夜触发我睡眼惺忪爬起来打开电脑连上服务器内存已经恢复正常日志里只有几条健康检查记录。折腾了二十分钟最后发现是部署脚本滚动重启容器时产生的瞬时波动。那会儿我恨不得把监控全拆了。后来冷静下来才想明白不是监控不该上是告警规则从头就没设计好。今天就把这几年给 FastAPI 项目折腾告警的经验理一理从指标采集、阈值设置到 Alertmanager 路由都按实战讲。1. 凌晨三点被吵醒这条告警到底要不要响1.1 一次真实的夜间误报那次误报的完整链条是这样的项目上线前我接了一堆默认告警规则里头有一条针对内存使用率的超过 75% 就触发。当时也没细想觉得内存监控必须有。结果部署脚本用的是滚动更新旧容器还没完全退出、新容器已经在拉镜像那一瞬间系统内存会被两个进程同时占着数值瞬间往上飙一下。监控采集周期是 15 秒正好抓到那个尖峰告警就发了。问题在于这条告警既没有for持续时间也没有区分告警级别。Prometheus 规则里如果只写exprAlertmanager 收到告警后马上就会往手机推。尖峰一过指标恢复告警过几分钟自动解决了人却被吵醒一次。这种已经恢复的误报是最伤人的因为它会训练你下次不去看告警。1.2 告警和日志、监控的区别很多团队在告警上踩坑根源是把三件事混成了一件事。我习惯把监控、日志、告警分开看监控是摄像头一直在录日志是黑匣子记录细节告警是安防报警器只在有人闯进来的时候响。要是把摄像头和报警器做成一个东西那结果就是一天到晚响个不停。层级常用工具职责典型例子指标监控Prometheus、Grafana、Zabbix持续记录系统状态CPU、QPS、P95 延迟、内存日志ELK、Loki、Sentry看单次请求细节报错堆栈、慢查询、调用链告警Alertmanager、各类通知机器人有限次数提醒人5xx 错误率持续 5 分钟超过 5%所以给 FastAPI 项目做告警之前先问一句你到底是缺监控能力还是缺一条该响才响的规则很多情况下服务本身已经不缺监控了缺的是把监控数据变成有效告警的那套约束。1.3 大部分 FastAPI 项目第一次告警都错在哪我见过不少项目第一次接告警时的通病基本可以归纳成五类不设for持续时间。只要指标瞬间越过阈值就发结果健康检查、垃圾回收、容器调度全都变成告警来源。阈值拍脑袋。没有基于历史数据反推直接写个感觉差不多的数字导致告警要么天天响要么永远不响。不分告警级别。所有问题都往同一个群里推critical 和 warning 用同一种通知强度时间久了群里全是噪音。没有维护窗口和静默机制。发版、压测、迁移期间不知道用 silence等告警像瀑布一样刷屏。告警文案只给数值不给上下文。短信里只有内存 78%没有服务名、没实例地址、没有排查入口值班的人接到告警还是一脸懵。这五类问题后面都会展开说但记住最核心的一句话告警是减少人类注意力消耗的最后一道网不是第一道。2. 先有指标再有告警给 FastAPI 加 Prometheus 打点2.1 为什么是 Prometheus 而不是自己写心跳脚本很多人给 FastAPI 项目做的第一个告警是一个定时任务每分钟请求一次/health不通就发邮件。这个方案不是不能用但它只能回答服务死没死不能回答服务为什么变慢错误率是不是在上升是不是哪条接口拖垮了整体延迟。Prometheus 是拉模式FastAPI 服务只需要暴露一个/metrics接口Prometheus 定期来抓。这样做有几个实际好处服务不需要主动往外部推数据健康检查的心跳脚本也省了。数据是带时间序列的后续还能做历史回看和阈值反推。采集端挂了只影响监控不会反过来影响业务接口。Alertmanager、Grafana 都是同一套生态规则配置和展示能直接复用。如果你的团队已经在用 Zabbix 或 Grafana也完全可以按同样的思路做下面的方法论是通用的只是配置语法不同。2.2 用 Instrumentator 五分钟接入FastAPI 接入 Prometheus 最简单的办法是用prometheus-fastapi-instrumentator它会把 HTTP 请求量、请求耗时、响应状态这些基础指标都自动暴露出来。pip install prometheus-fastapi-instrumentator然后在 FastAPI 主程序里加两行from fastapi import FastAPI from prometheus_fastapi_instrumentator import Instrumentator app FastAPI(titledemo-service) app.get(/health) def health(): return {status: ok} Instrumentator().instrument(app).expose( app, endpoint/metrics, include_in_schemaFalse, )启动后访问/metrics能看到类似http_requests_total、http_request_duration_seconds这样的指标这就说明打点成功了。整个过程确实五分钟内能完成关键是把/metrics这个端点保护在监控系统内部访问范围内别直接暴露到公网。2.3 自定义业务指标的接入姿势框架自带的指标覆盖的是HTTP 视角但很多业务问题得靠自定义指标才能看见。比如登录接口本身响应正常但第三方支付网关的调用越来越慢再比如用户批量导入任务里失败率升高但接口层完全无感。这个时候就要自己在代码里打点。from prometheus_client import Counter, Histogram task_total Counter(batch_import_total, 批量导入任务总数, [scene]) task_duration Histogram(batch_import_duration_seconds, 批量导入耗时, [scene]) # 在业务代码里 task_total.labels(sceneuser_import).inc() task_duration.labels(sceneuser_import).observe(42.5)用prometheus_client的时候有一个红线要记住label 的值不能是高基数变量也就是不能拿用户 ID、订单号、请求参数这类东西当 label。否则每来一个新值就会产生一个新的时间序列Prometheus 的内存和查询性能会出大问题。路径这类标签也要特别小心如果接口里有动态 ID最好先用路由模板归一化否则一个/users/12345就是一个新序列量一大监控本身先被拖垮。2.4 该盯哪四类指标给 FastAPI 项目定指标我建议直接套 RED 方法也就是请求速率、请求错误、请求耗时这三类再加上系统层面的饱和度一起构成四个关注面。信号指标示例关注点流量http_requests_totalQPS 趋势是否有异常突增或暴跌错误http_requests_total{status~5..}5xx 比例用户请求失败的程度延迟http_request_duration_secondsP50、P95、P99 分层观察饱和度CPU、内存、连接数、消息队列积压服务是否快到容量上限饱和度是最容易忽略的。FastAPI 本身是异步框架CPU 高不一定直接体现在请求延迟上但数据库连接池、Redis 连接数、后台任务队列积压这些指标一旦到顶影响是灾难性的。这类自定义指标没有通用模板必须结合你的实际业务去埋。3. 阈值不是抄来的如何把误报率降下来3.1 我照搬模板配置的翻车现场我早期犯过一个特别典型的错误从网上找了一份 Prometheus 告警规则里面写的是延迟超过 200ms 就报警。我看都没看直接粘进配置然后线上服务就开始规律性报警。查了一下发现这个服务的 P95 延迟平时就在 350ms 上下阈值却设成了 200ms等于每五分钟触发一次告警搞得整个群全是噪音。那次之后我学到一个道理阈值没有银弹同一个数字在不同项目里含义完全不同。一个内部管理系统的 P99 是 1 秒可能都算快一个实时推送服务的 P95 超过 300ms 可能已经算事故。模板只能给思路不能给结论。3.2 三步用历史数据反推阈值正确做法是这样的先把指标裸跑一到两周这段时间不开告警只在 Grafana 里观察。把一周的 P95、P99、5xx 比例拉出来确定常态区间。不用算得多精确能区分正常波动和异常抬升就行。阈值取常态峰值的 1.5 到 2 倍同时加for持续时间过滤瞬时尖峰。举个例子如果常态 P95 在 350ms 到 400ms 之间那我一开始会把 P95 的告警阈值设在 800ms 左右再加for: 10m。也就是说只有在 P95 连续 10 分钟超过 800ms 的情况下才告警。这样做可能会把某些小问题漏掉但告警宁可在初期少一点也不能用噪音把人的注意力磨光。3.3 一条靠谱的告警规则模板下面这段是我给一个 FastAPI 服务实际用过的规则包含延迟、错误率、实例存活三类你可以抄回去改数字。groups: - name: fastapi-demo rules: - alert: FastAPIHighLatency expr: | histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{jobfastapi-demo}[5m])) by (le) ) 2 for: 10m labels: severity: warning service: fastapi-demo annotations: summary: FastAPI 服务 P95 延迟超过 2 秒 description: 当前 P95 {{ $value }}s已持续超过 10 分钟。 - alert: FastAPIHigh5xxRate expr: | sum by (instance) ( rate(http_requests_total{jobfastapi-demo, status~5..}[5m]) ) / sum by (instance) ( rate(http_requests_total{jobfastapi-demo}[5m]) ) 0.05 for: 5m labels: severity: critical service: fastapi-demo annotations: summary: FastAPI 服务 5xx 错误率超过 5% description: 5 分钟内 5xx 比例保持 5% 以上请确认上游服务和数据库状态。 - alert: FastAPIInstanceDown expr: up{jobfastapi-demo} 0 for: 2m labels: severity: critical service: fastapi-demo annotations: summary: FastAPI 实例不可达 description: Prometheus 已经连续 2 分钟无法抓取 {{ $labels.instance }} 的指标。错误率这条是为啥用比例而不是用绝对值因为流量有高低峰。凌晨流量低的时候绝对错误数可能就是几次白天流量高的时候同一数值可能意味着更大比例的用户受影响。用比例才公平。3.4 还没有历史数据的新服务怎么处理新服务没有历史数据很正常别硬憋。我一般先给一个比较宽的阈值比如延迟阈值直接设成正常预期的三倍错误率放宽到 10%for给到 15 分钟。等跑一到两周拿到真实曲线后再逐步收紧。这比一开始就拍一个看起来合理的数字要靠谱得多。还有一点告警要分两级warning 和 critical 不要用一个阈值表达。warning 可以作为趋势预判提醒你开始不对劲了去 Grafana 看看critical 必须代表用户已经明显受影响需要马上人工介入。级别的差异要体现在通知渠道上这个下一节讲。4. Alertmanager 路由与静默让告警在睡觉时间闭嘴4.1 分组、路由、抑制、静默分别管什么Prometheus 负责产生告警Alertmanager 负责决定告警怎么通知人。很多 FastAPI 项目直接把 Prometheus 的告警推到钉钉群跳过了 Alertmanager短期看省事长期看会后悔。因为四个关键能力只有 Alertmanager 才有分组grouping把同一类告警合并成一条通知。比如同一个服务有五个实例同时挂了你不会想收到五条微信你会想收到一条order-service 5 个实例失联。路由routing根据标签把告警分给不同接收人。critical 走即时通知warning 发邮件不同团队认领不同服务。抑制inhibition高级告警触发时自动屏蔽关于同一个服务的低级告警。比如数据库挂了那数据库引起的 5xx 告警就不用再重复轰炸。静默silence在已知的维护时间段内主动把某些规则的告警按住不发。这四个能力是晚上能不能睡好的关键尤其是分组和路由。4.2 一个中小团队能直接用的配置下面是我在中小团队环境里常用的一套简化配置思路是critical 走钉钉或企业微信机器人warning 走邮件同类告警五分钟后还没有人处理才重复通知。route: receiver: default group_by: [alertname, service] group_wait: 30s group_interval: 5m repeat_interval: 4h routes: - matchers: - severity critical receiver: dingtalk-critical repeat_interval: 1h - matchers: - severity warning receiver: email-warning receivers: - name: dingtalk-critical webhook_configs: - url: https://oapi.dingtalk.com/robot/send?access_token替换成自己的Token send_resolved: true - name: email-warning email_configs: - to: opsexample.com send_resolved: truegroup_wait: 30s的意思是告警产生后先等 30 秒再发这段时间里如果又来了同组告警就合并成一条再发。repeat_interval: 4h表示这条告警如果一直没恢复每四个小时提醒一次。这个字段尤其重要没有它一个长时间不恢复的告警可能每分钟都在刷屏。4.3 夜间和周末怎么处理时间路由很多团队的诉求是非工作时间别用低级告警吵我这个 Alertmanager 可以做到但我得先说一句凌晨不准吵的只是 warningcritical 必须要能穿透。否则真出了事故第二天早上从邮件里看到凌晨两点数据库挂了四个小时那告警系统就是摆设。新版 Alertmanager 提供了time_intervals来做时间路由大致思路如下time_intervals: - name: workhours weekdays: [monday:friday] times: - start_time: 09:00 end_time: 18:00 route: routes: - matchers: - severity warning time_intervals: [workhours] receiver: email-warning - matchers: - severity critical receiver: dingtalk-critical不同版本的time_intervals语法略有差异用的时候以官方文档为准。但真正要理解的是它的用途不是让你把服务监控关掉而是把不同级别的告警设计成在合适的时间到达合适的人。warning 级别在非工作时间延后通知critical 永远走即时渠道。4.4 告警文案要能让人直接上手我接手过不少项目告警文案长这样Error rate is high。看到这条消息值班的人连是哪个服务、哪个接口、找谁问都搞不清楚。这等于给了线索但没给案情。一份好的告警文案至少要包含三件事现在哪里出了问题、当前数值是多少、第一步去哪里查。推荐的做法是把排查入口写进annotations里annotations: summary: 用户服务 5xx 错误率超过 5% description: 实例{{ $labels.instance }}当前 5xx 比例{{ $value }}} runbook: http://wiki.internal/runbooks/fastapi-5xxrunbook这个字段很实用点开链接就是团队沉淀的排查文档比如先看数据库连接池、再看依赖的上游接口。有了它半夜被叫醒的人能在最短时间内进入排查状态而不是对着一条干巴巴的告警发呆。5. 恢复通知与月度复盘把半夜报警变成周一清单5.1 send_resolved 一定要开很多人配 webhook 的时候只关心告警怎么发出去忽略了恢复通知。我觉得send_resolved: true必须要开。原因很简单一条告警如果只报坏了不报好了接到告警的人要么反复去刷新面板确认要么等到下一轮重复提醒才知道已经处理完。这会极大消耗注意力。开了恢复通知之后完整的事件闭环是这样的凌晨 2:00 收到 critical 告警2:20 处理完2:25 收到恢复通知这条事情在心里就算翻篇了。要是没有恢复通知第二天早上还得翻聊天记录一条条确认这个后来怎么样了很痛苦。5.2 标签体系与告警统计到了月底复盘的时候告警数据本身就是一个金矿。前提是你在规则里把标签加好。我常用的标签至少包括这四个标签值示例作用serviceorder-service、user-service区分系统envprod、staging区分环境teampayment、infra区分责任团队severitycritical、warning区分处理优先级Prometheus 内置了ALERTS这个指标可以直接查到历史告警的状态变化。类似下面这条查询可以把一段时间内的告警数量拉出来count by (alertname, severity) (ALERTS{severitywarning})月度复盘的时候我会把告警按这张表过一遍目的不是追责而是找规律。等级触发次数平均持续时长需要人工介入的占比critical512 分钟80%warning3740 分钟15%如果 warning 的触发次数特别多但大部分不需要人工介入说明阈值太敏感或者这些指标本身就不该变成告警应该降级成 Grafana 面板上的一个图表或者改成日报推送。这样做的结果就是真正出现在手机上的告警数量越来越少每一条的分量越来越重。5.3 降低夜间告警的几条硬经验最后说说我这些年总结下来的几条实际操作经验照着做基本能把半夜被吵醒的频率降下来给所有告警规则加for最少 2 分钟建议 5 到 10 分钟。这一条可以把一大半瞬时抖动过滤掉。发版和压测前提前在 Alertmanager 里创建 silence而不是去注释告警规则。注释规则容易忘了恢复silence 可以设置过期时间到点自动失效。对已知问题用 silence 暂存而不是默默习惯它。很多团队收到告警后发现哦这个一直这样然后就没下文了。正确做法是先 silence 一周然后排期去修修完把告警规则重新审视一遍。告警里加上 runbook 链接把排查步骤沉淀成文档。值班的人最怕的不是告警而是告警来了不知道下一步干什么。不要为了凑监控覆盖率而告警。某些指标适合做成曲线展示不一定适合变成告警。判断标准就一条这条告警响了我要不要立刻爬起来干活如果不用那它就不该出现在手机通知里。我个人养成了一个习惯每次被告警吵醒处理完后的第一件事不是回去睡觉而是把这次告警的规则打开看一眼问自己三个问题这个告警该不该响文案够不够清楚有没有更早发现的办法这三个问题记录到团队协作文档里下一次虽然还是会被吵醒但至少会被吵得更值。告警这件事说到底没有完美的方案只有不断根据真实情况调整的动态平衡。FastAPI 项目本身接入监控的成本很低难的是设计出让人愿意看、也看得懂的告警规则。从今天这次半夜报警开始把阈值、路由、静默、恢复通知一步步搞定你会发现值班的夜晚安静很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价