2025 年底那会儿,我手里攥着二十来台云服务器,分布在阿里云、腾讯云和几家海外 VPS 厂商的机器上,业务是一套对外的 API 服务加两个管理后台。机器多了之后最让人没底的不是性能,是"你根本不知道它什么时候出的问题"。于是我花了两个周末,把 Prometheus 装起来,配好 node_exporter,抓取指标、跑查询语句、写告警规则,Grafana 面板也搭了一整套,磁盘、内存、CPU、连接数一应俱全。Prometheus 那边指标是绿的,面板是漂亮的。一切看起来都挺像那么回事。
如果你正准备给自己的服务器上一套监控和告警,或者正被"指标看着正常但业务已经出问题"这件事困扰,这两家可以先看看:狗云DOGYUN(高性价比云服务器,机型齐全,适合批量铺监控采集节点)、RackNerd(全球 VPS,多地区节点齐全,跨区域监控和灾备切换很方便)。机器先备好,监控架构再动手,能少走很多弯路。

直到三个月后的一次线上事故,我打开 Grafana 才发现,磁盘使用率早就冲到 92% 了,日志文件把根分区塞得满满当当,Nginx 写不进去,报 502。整个过程大概持续了四十多分钟,而我的手机从头到尾一声没响。事后复盘我才把这三个月的事串起来:我搭的不是监控,是一套看起来很像监控的图表。指标采上来了,规则也写了,告警却从来没走到我面前。
这是最基本的一个认知错位,也是我踩得最疼的一个坑。Prometheus 本身只负责两件事:定时抓取目标(scrape),以及按规则(rule)算出一个布尔结果。规则写成 alert: DiskUsageHigh,条件是 disk_used_percent > 85,命中之后 Prometheus 会在自己内部的 /api/v1/alerts 里留下一条记录,同时往 Alertmanager 推一个 alert 事件。它不负责发短信,不负责发企业微信,更不会自己打电话给我。
真正干"通知"这件事的是 Alertmanager,它是独立的一个进程(或者一组),负责路由、分组、抑制、静默,然后把通知发给配置好的 receiver邮件、企业微信群机器人、钉钉、短信网关这些。我的问题就出在这儿:我把 Alertmanager 的配置文件从示例里抄了一份,receiver 段留着没改,路由树只配了 default,default 只有一个 null receiver。规则命中了,事件推过去了,Alertmanager 收到一看没有匹配的 receiver 对应的目标,直接把这条 alert 丢进了黑洞。
更绝的是 Alertmanager 那边不会报错。它的 API 返回 200,Prometheus 的 targets 页面全是 up,Rule 页面也显示 firing 状态正常。你不去主动查 /api/v1/alerts 或者点开 Alertmanager 的 UI 去看,你永远不会发现有一条通知被扔掉了。我在 Grafana 面板上看的是指标,指标确实涨上去了,只是那个数字从来就没变成过一条消息。
所以这里有一条我踩过血的建议:部署完监控之后,第一件事不是加机器,而是拿一个必然触发的方式去验证通知链路。最简单的手法是临时加一条必然成立的规则,比如 vector(1) 恒等于 1,然后 for: 1m,让它一分钟后必然 firing,然后确认手机真的收到。这样链路是通的,你的告警才是真的在跑。
第二件事是阈值。我最初的规则是照着网上抄的:CPU 超过 80% 报警,内存超过 90% 报警,磁盘超过 85% 报警。看上去合理,实际用起来非常吵。有一台机器平时跑批处理,CPU 常年 85% 上下浮动,结果它一天能触发几十次告警。Alertmanager 有分组和抑制机制,可我配的分组间隔太长,重复告警一条一条全推到我手机,很快就形成"条件反射式忽略"。

后来我重新理了一遍思路,告警阈值要跟"后果"挂钩,而不是跟"数字"挂钩。问自己三个问题:
第一,这个指标涨到什么程度,用户的请求会真的失败?磁盘 85% 不一定会出事,磁盘 95% 且日志还在写就一定会出事,那阈值就该设在 90% 加上"日志增长速率"这个条件,而不是一个静态数字。
第二,这个指标的波动是持续的还是瞬时的?瞬时尖峰用一个 for 时长把它平滑掉,比如 for: 5m,意思是"持续 5 分钟都超了才告警",这一条能消灭掉一大半噪音。
第三,这个告警我收到之后真的能立刻做点什么吗?如果不能,那它就不该是一条告警,而应该是一条每周看的报表规则。这是我后来删掉的最多的东西我删掉了将近一半的规则。世界瞬间清净了。
另外强烈建议引入分位数。我现在磁盘和延迟类的告警基本都从平均值换成了 P99。用 Prometheus 的 quantile(0.99, node_filesystem_avail_bytes) 这类表达式,能把那些"平均看着还行、但一部分请求已经卡死"的情况抓出来。平均值这东西在监控里骗人的次数太多了。
还有一类问题不在告警这一侧,而在采集这一侧。node_exporter 装上去之后,targets 页面显示所有 target 都是 up,我就以为采集没问题。其实 node_exporter 挂掉的时候,Prometheus 里这个 target 的 up 依然是 1因为 up 的语义是"我这次请求拿到了 HTTP 200 响应",而 Prometheus 自己会去拉一个 node_exporter 的健康检查页,返回 200 就算 up,跟 exporter 的采集逻辑是不是正常工作完全两回事。

所以更靠谱的做法是配一条 up{job="node"} == 0 的告警,同时再配一条 absent(node_load1{job="node"}),后者能抓到"整个 job 一个指标都没有"的情况。我有一次就是某台机器重启之后 node_exporter 没起来,up 看着是 1,但 node_load1 这类指标整整消失了六个小时。
还有个小坑:Prometheus 默认 5 分钟一个 scrape 间隔,有些瞬时问题在两个采样点之间就结束了,你什么都看不到。关键链路上的指标我会单独开一个 job,把间隔调到 15 秒。存储压力确实会上去,但配合 retention 设置好,问题不大。
最后说说接收端。我原来只在 Alertmanager 里配了一个企业微信群机器人,看起来是唯一出口。后来那次磁盘写满的故障里,我恰恰是刷手机刷到了自己发的排查过程(不是告警),才发现问题的。所以现在的做法是分级:
致命级别的(比如整机不可用、磁盘写入失败),走钉钉加短信双通道,凌晨三点也要把我叫醒;重要但不用立刻起来的(比如内存持续超 90%、证书还有 7 天过期),只发钉钉和邮件;剩下那些趋势性的,就进 Grafana 面板,每天早上看一眼。

顺带提一句,静默(silence)和抑制(inhibit)这两个功能别乱开。抑制规则写好了是降噪,写错了会连带把真正该报的告警一起压掉。我现在的做法是任何抑制规则都必须写上有效期,到期自动失效,避免一个月后自己都忘了当初为什么压掉了这条。
回头看那三个月,问题不在于 Prometheus 不够强,也不在于 Grafana 面板不好看。真正的问题是我把"能看到指标"当成了"能处理故障",中间那条从数据到通知的路,我根本没走通过一遍。现在我给每个新环境定了一条死规矩:监控上线之前,必须先证明它能在半夜把我叫醒。做不到这一点的,就不算上线,只算摆了个仪表盘。
如果你也在做服务器监控这件事,建议先从最小的一套开始:一个采集节点、一条真正有用的规则、一个可靠的接收渠道,把这条链路端到端跑通,再往上加指标和面板。反过来做,就是我那三个月的老路。