服务器监控之后,怎样让告警真正有用?Fleetd 的触发、恢复与通知设计
Fleetd 告警怎样触发、恢复并发送通知?结合 v0.3.0 讲清持续超限、离线、慢指标、去重与邮件合并,以及采集缺失和投递失败的边界。
把服务器和软路由放进一个后台,能看到 CPU、内存和磁盘曲线,已经比挨台登录方便不少。但如果每天还是要主动打开页面,才能知道哪台机器出了问题,监控就只完成了一半。
接下来很自然会加告警:CPU 高了发邮件,磁盘快满了发邮件,机器离线也发邮件。
规则很容易写,难的是收到邮件以后,能不能据此判断该不该处理。如果一次短暂波动就响,长期故障又每几秒重复提醒,最后很可能连真正重要的邮件也不看了。反过来,没有收到邮件,也未必代表机器正常。
这一篇继续 Fleetd 开发记录,按 v0.3.0 的实现聊告警。上一篇讲了客户端怎样运行在 Linux 和 OpenWrt 上,现在看这些上报怎样变成一条告警,又怎样结束。
先说清楚,一条告警到底代表什么
Fleetd 当前覆盖离线、CPU、内存、磁盘空间、inode、失败服务、异常容器和 WireGuard 握手超时。另外还有连接地址变化这类一次性事件。
这些信息看起来都能放进“告警”页面,但含义不同。
磁盘空间紧张,可能很快影响写入;CPU 高,可能只是正在编译;WireGuard 很久没有握手,也可能与对端暂时没有流量有关。看到一个指标越线,还不能直接得出业务已经不可用的结论。
我更希望一条告警先回答三个问题:哪台机器、哪个对象、满足了什么条件。 后面的原因判断和处理动作,再根据机器用途决定。
这也决定了当前 Fleetd 的定位:它提供一组具体、可解释的检查,不把“发出告警”自动升级成“已经定位故障”,更没有因为 CPU 高就自动重启服务。
CPU 高一下,和持续高负载,要分开处理
CPU 和内存的默认阈值都是 90%,持续窗口是五分钟。配置片段如下:
alerts:
offline: 40s
cpu_percent: 90
mem_percent: 90
sustain: 5m
disk_percent: 90
inode_percent: 90
wg_handshake: 5m
这是 v0.3.0 的默认配置片段,不是一组适合所有机器的最佳值。当前这些参数在服务端统一设置,并没有逐台机器的独立阈值配置。
假设有两种情况:一次短任务把 CPU 拉到 98%,随后很快回落;另一台机器的 CPU 在一段时间内都处于高位。下面只是说明规则的例子,不是线上实测记录。
对于前一种情况,持续窗口可以过滤掉单次尖峰。对于后一种情况,才更值得提醒使用者去看是不是任务堆积、异常进程,或者本来就计划进行的高负载工作。
实现上,Fleetd 查询最近窗口内的 CPU、内存样本,取其中的最小值。最小值仍然高于阈值,意味着这一批已收到的样本全部超限。 它不是把平均值与阈值比较,也不是简单累计几次超限。等于 90% 的样本不满足“高于 90%”。
它还会检查最早样本距离当前是否足够远。默认五分钟窗口允许三十秒的覆盖容差,因此不能把它说成精确等待满五分钟才触发。代码也没有要求窗口内必须有多少条样本,或验证每两个样本之间都连续无缺口。
所以,较准确的理解是:根据最近一段时间收到的样本判断持续超限,并留出采样容差。采样缺口仍然是判断的边界。
内存的计算也值得说明:当前使用总内存减去 MemAvailable,再除以总内存。它不直接把 Linux 的所有缓存都当成不可用内存,也不等于某个应用已经泄漏。收到内存告警后,仍需要看具体进程和工作负载。
离线表示上报中断,不等于机器已经关机
客户端通常每十秒上报一次基础指标,服务端按五秒节奏检查告警。默认超过约四十秒没有收到上报,就会在后续检查中判定离线。
这里看的时间是服务端收到报告的时间,而不是直接拿客户端时钟来判断最后一次上报。这有助于避免两台机器时间不同导致的直接误判。
但离线仍然可能有多个原因:客户端停了、机器断电、路由器断网、DNS 出问题,或者到 Fleetd 服务端的路径不可达。仅凭这一条告警,无法区分它们。
默认阈值相当于允许错过若干次正常上报,不会因为某一个采样点没来就立刻报警。实际通知时间还会受到检查调度、数据库和邮件发送影响,不能承诺“断开四十秒整就收到邮件”。
当前还有两处避免误报的处理:
- 已批准、但从未上报过的机器,不直接算一次离线故障。
- 服务端刚启动时,先留出
offline + 15s的宽限期。默认约五十五秒,给客户端重连留时间,再创建新的离线告警。
这也带来运维上的提醒:一台机器刚批准却一直没报过数据,不能因为没有离线邮件,就认为接入已经成功。首次接入仍要确认真实上报。
还有一个更大的边界:如果 Fleetd 服务端自身已经停止,它就不能继续执行检查、给你发送离线邮件。要监控监控系统本身,需要独立的外部检查;当前这套规则不能代替它。
机器离线后,原来的故障不能假装恢复
假设一台机器已经出现磁盘告警,接着网络断了。此时服务端没有新的磁盘数据。
最危险的处理方式,是因为本轮“没有看到磁盘超限”,就把原来的告警关闭。这样会让使用者收到恢复消息,以为问题解决了。
Fleetd 对离线机器会保留已经打开的告警,不用陈旧数据新建或解除其他状态告警;在宽限期之外,再补上离线这一项。
等机器重新上报,才依据收到的数据继续判断。于是,重新上线可以解除离线告警,但磁盘告警仍然可能保留。这两件事应该分开看:通信恢复了,不代表磁盘也腾出了空间。
这套逻辑对小型自托管系统很重要。与其急着报告“恢复”,不如明确表达现在掌握的信息仍然不足。
慢指标的告警,有另一套时间尺度
磁盘、失败服务、容器和 WireGuard 属于客户端的较慢采集项,正常情况下每六轮基础上报采集一次,大约一分钟。服务端虽然每五秒检查,也不能把一分钟更新一次的信息变成五秒实时检测。
| 检查项 | 当前触发依据 |
|---|---|
| 磁盘空间 | 挂载点使用比例高于阈值 |
| inode | 挂载点已用 inode 比例高于阈值 |
| 服务 | 采集到 systemd 的失败单元 |
| 容器 | 处于 restarting、状态含 unhealthy,或非零退出 |
| WireGuard | 曾经握手的对端,握手时间距客户端报告时间超过阈值 |
磁盘和 inode 没有套用 CPU 那个五分钟持续窗口。它们基于当前保存的慢指标判断,超过阈值就可以打开告警。inode 单独检查,是因为文件系统可能还有容量,却因大量小文件耗尽 inode 而无法继续创建文件。
容器也没有简单地把所有停止状态都算故障。当前会忽略以退出码 0 正常结束的容器;一次性任务结束,不一定是异常。相反,非零退出、不健康状态和 restarting 会进入告警。
不过,它没有在这里统计“某一分钟重启了多少次”。采样看到 restarting 就触发,不能把它宣传成已经具备完整的重启频率分析。同样,进程处于 running,也不证明业务接口一定正常。
WireGuard 的条件更需要结合用途理解。它只检查曾经有过握手的对端,跳过从未握手的对端;时间差使用客户端报告时间与握手时间计算,避免直接混用服务端时钟。
长时间没有握手是一个线索,并非端到端可用性的完整证明。对于不要求持续活跃的对端,先理解流量和 keepalive 配置,再决定是否启用这个规则。wg_handshake: 0 可以关闭它,但当前是统一配置,不能假设已经支持针对每个对端分别排除。
“恢复”首先是规则不再成立
Fleetd 每次计算当前应该存在的告警集合,再和数据库里尚未关闭的告警比较。
新出现的打开,已经消失的关闭,仍在集合里的保留。告警按机器和检查对象区分,例如同一台机器的不同挂载点、不同容器,会有各自的记录。
这也让恢复条件比较容易理解:在机器在线、能够进入评估时,只要规则不再满足,原来的告警就可能关闭。
对于 CPU 和内存,窗口内出现一条不高于阈值的样本,就会让“所有样本都超限”不再成立。当前没有另设“恢复后再稳定两分钟”或更低的恢复阈值。开启前有持续窗口,不代表恢复也有相同的等待过程。
慢指标还存在另一种边界。基础报告不包含新慢指标时,服务端会保留上一次的慢指标;但如果新慢指标里的某项因工具缺失或调用失败而变成空结果,原来针对那个对象的告警可能消失。
例如,曾经上报过一个失败服务,后来收集命令失败,新的列表不再包含它。系统还没有为每个采集项完整区分“检查成功且正常”和“检查不可用”,所以不能把每条恢复记录都当成业务已经恢复的独立证据。
这一点延续了上一篇的结论:客户端能运行,不等于每种采集能力都可用。尤其在 OpenWrt 上,当前失败服务采集使用的是 systemd 命令,并没有完整的 procd 服务健康检查。
避免重复告警,和保证通知送达,是两回事
当前 Fleetd 不会每五秒给持续存在的同一条告警再建一条记录。只要它还处于打开状态,本轮就不重复发送新告警。规则消失时,产生恢复变化。
同一次评估产生的打开与恢复变化,会合并成一封邮件。这能减少几台机器同时出现变化时,邮箱瞬间多出一串通知。
但合并范围就是这一轮评估,没有跨几分钟的聚合窗口,也没有“发现一条故障后,每隔多久继续提醒”的催办逻辑。地址变化这类一次性事件走独立的通知路径,也不能一概说全部事件都会与周期检查合并。
还有一个使用时必须知道的限制:告警状态先写入数据库,再异步发送邮件。如果发送失败,当前会记录错误日志,没有持久化的投递队列和自动重试。
因此,后台已有一条告警,不保证邮箱已经收到;反过来,同一条故障没有更多邮件,也可能只是它一直未恢复,而不是系统又确认过一切正常。持续故障期间,邮件失败也不会因为下一轮检查而自动补发同一条打开通知。
当前的去重减少了噪声,但它不能代替可靠投递、重复提醒、负责人确认或升级通知。这些仍然是以后可以补的能力。
邮件只是出口,后台记录仍然要能看
配置 SMTP 后,告警变化可以发到指定邮箱。没有配置主机或收件人时,通知会写到服务端日志,状态仍然可以在后台查看。
服务端语言可设为 language: zh,用于告警和邮件内容;网页界面的语言由浏览器侧选择。两处设置不应混为一谈。
当前监控历史保留七天,已恢复告警保留九十天,由定期清理处理。这也意味着,几个月后还可能看到一条历史故障,却已经没有对应的完整指标曲线。如果需要长期复盘,要另外规划数据保留,不能把现有保存周期当成长周期分析平台。
连接地址变化是一类通知事件:已有连接地址发生变化时记录并提醒,而不是打开一个必须等待“IP 恢复原样”才解除的故障。地址变化可能只是网络出口变更,需要结合上下文判断。
真正要验证的是一条完整通知链
对于自己的几台机器,我会先确认“触发—记录—通知—恢复”是否可理解,再调整阈值。下面是建议的验收场景,不是已经在线上制造过的故障。
| 场景 | 应重点观察什么 |
|---|---|
| 短暂 CPU 尖峰 | 不应仅凭一条高值就触发持续告警 |
| 测试数据持续超限 | 样本覆盖满足条件后出现记录,后续检查不重复打开 |
| 已有故障时停止上报 | 原有故障保留,并在条件满足后追加离线 |
| 恢复上报但故障仍在 | 离线解除,原来的故障仍保留 |
| SMTP 不可用 | 后台记录存在,日志反映投递失败,不能期待自动补发 |
| 采集工具不可用 | 检查缺数据会怎样影响状态,避免把空结果误认为健康 |
阈值与时间窗口可以在隔离测试环境里用可控数据验证,没必要为了验证磁盘告警填满生产根分区,也不应随意断开承担远程接入的路由器。
项目现有测试覆盖了部分慢指标判断,包括磁盘、容器正常与异常退出、WireGuard 握手条件;端到端测试中也有上报数据产生磁盘告警的路径。这些能约束部分规则,但不等于所有触发、恢复和邮件投递场景都已经完整验证。
Fleetd 目前做的是让小规模自托管环境先获得可解释的告警:哪些数据触发了它、为什么没有重复提醒、什么时候关闭,以及通知链可能在哪里中断。
后面值得继续补的,包括采集能力与数据新鲜度的明确表达、可靠邮件投递,以及按机器或用途设置规则。先把这些问题说清楚,比把检查项越加越多更有用。
下一篇回到客户端维护:签名更新包怎样校验,版本如何判断,以及更新失败后有哪些恢复边界。
项目与相关资料: