烟雾报警器没有坏,因为确实有烟。
Chris Healey 在 dev.to 上复盘 PulseWatch 那次报警虚惊,最后查到报警是对的:一个 GitHub Actions 工作流,每 15 分钟跑一次,真的消失了一个多小时然后恢复。MISSING 触发,自动转 RECOVERED。设备没坏。这句话几乎可以当结论。
但我要说的不是这个。
文章更靠后的地方有一段冷得多。PulseWatch 内部有一个 watchdog 进程,负责评估各个 monitor。如果这个 watchdog 死了,没有告警能发得出来——同时 Web 应用、进来的 ping、运行历史一切照常。外观看不出任何问题。一个监控服务自己的内核静默死亡,恰好是它被造出来去捕捉的同类事件。Healey 把这个写出来了。
先把架构拆开。watchdog 的存在理由很直白:一个受监控的脚本不能自己报告“我没启动”。它没起来,就不会去 ping 任何东西。所以必须有外部一方去推断“它应该出现过但没出现”,按自己的节奏评估每个监控,把状态归到 OK、MISSING、FAILED、STUCK。设计是对的。同时它天然制造了一个盲区:watchdog 自己成了下一个“必须出现但可能不出现”的一方。
于是引入 Healthchecks.io,架在 PulseWatch 基础设施之外,盯着 watchdog。每个 watchdog 循环开始发 start,跑完发 success,异常发 failure。Healthchecks 知道预期频率,循环没启动就能告警。监控链变成用户任务 → PulseWatch watchdog → 外部 watchdog,三层。
真正值得拆的细节不是“加了外部监控”,而是它被设计成 best-effort 且非阻塞。Healthchecks 挂掉,不会阻止 PulseWatch 去检查用户的监控。这个决定不在宣传文案里出现,但它真正的含义是:链上每个环节独立地可能失败,且失败不能级联。外部 watchdog 死掉,内部 watchdog 继续干活;内部 watchdog 死掉,Healthchecks 告警。递归没有终点——Healthchecks 自己也需要被看——但加深了一层,每一层独立故障。
“谁来看守看门人”在这里不是哲学问题,是架构问题,要拿具体的故障域分离去回应。
时间语义那一节我读得最不舒服。不是 Healey 说错了什么,是对了。15 分钟的 cron 不等于 15 分钟宽限期是安全的。GitHub 文档自己写了,计划任务在高负载时可能延迟,排队任务可能被丢弃。一个写明白的调度器都可能漏任务。所以你拍脑袋给一个 15 分钟预期间隔配 15 分钟宽限期,等于拿 cron 表达式去做告警阈值的依据,而真实观测告诉你调度器本身会迟到、会丢包。监控配置里最少见也最常见的错误就是这个:信调度器的承诺,不信它实际干的事。
宽限期必须基于实际观察到的行为,不是 cron 字符串。预期间隔、宽限期、最大运行时间——这三个参数在监控工具里就是产品本身,它们决定什么算噪声,什么算真失踪。把三者跟 cron 绑死,等于把判断能力外包给一个一直谎报的调度器。
然后是他文档里那些防御性模式,读起来像工程卫生:监控请求设短超时,不覆盖原始任务的错误;Bash 示例保留实际退出码;失败消息走 URL 编码;示例 ping URL 故意用伪造令牌。放在独立开发者写给公众的文档里,它们意味着另一件事:这个人清楚,文档里出现任何真实令牌都可能被滥用,他得保证公共示例是安全的。这些不是功能,是信任。一个监控服务在教你安全集成之前,你先看到了它如何对待自己的失败面。
顺带提一句支持链接。付费独立层承诺优先支持,却一度没有请求方式。SaaS 里太常见了,“优先支持”是纯文案,因为产品从没为“有真人回应”付出过基建。他加了实时链接和真实域名邮箱,测试了完整闭环。听起来不起眼,但这是“能不能信”的证据。一个监控工具连自己的支持闭环都不测,你的报警在它那里发生什么,它大概率也不知道。
到这里真正想问的问题浮出来了:为什么这些零散、不起眼的事出现在同一篇文章里,而且出自同一个人。
Healey 自己观察到一个模式:开发早期的重点是能力——收 ping、检测缺失任务、发告警、显示历史。发布之后重心逐渐转到信任——watchdog 的死亡、不出误报、安全示例、能联系上的真人。这个二分比表面更根本。能力可以伪造,因为它只管关键时刻到来之前那一秒。你能搭一个检测缺失 cron 的服务,展示、获客,整个过程中“自己会不会静默挂掉”根本没暴露,直到有人把真任务挂上来。信任不一样,只能通过面对自己的失败模式来建立,这件事独立于功能清单。
只兜售能力的监控产品危险,因为它有权威没可靠性。用户把任务交给它,它静默死了,用户同时失去任务和告警。Healey 这段时间做的事——看门狗架构、外部监控、时间粒度、安全文档、真实支持——没有一样是新功能。每一项都是同一个问题的答案:无人看护我的那一刻,我怎么失败?
Vinh Nguyen 在评论里提了一个更狠的点。即使 watchdog 每周期发 success,这个信号能证明循环执行了,不能证明它完成了工作。极端情况下评估队列是空的,某个 filter 不再匹配,某个迁移改了状态名,watchdog 照样说一切正常。Healey 的回应是 multi-plan 下零评估可能是正常的,盲目的行数比较反而误报;更强的检查要看那些应该被评估的监控是否已延误,预期要独立于选择逻辑建立。这个交换值得拆:它暴露了信任的递归性在更低一层还会出现。“我正常运行了”不等于“我做了你要我做的事”。最终极的监控要回答“谁来决定什么应当被做”,不是“谁看见了什么发生”。
往下想,这里有一个被集体回避的利益问题。监控品类的收钱方式从第一天就绑在信任上。卖的不是服务器,不是数据库,是“你会在坏掉的时候告诉我”。一个人开发者要真卖掉这个东西,先得证明自己在无人注视的瞬间,不会像那些被他监控的 cron job 一样,悄悄消失,不留一句话。这不是技术能力问题,是产品在这个品类里能不能成立的问题。
大多数监控工具都在回避这件事。表面卖的是告警、面板、时序数据,用户真正掏钱买的是“别让我瞎”。瞎的可能不止一种。任务失败没告警,瞎。告警发出去没人处理,瞎。连告警本身都没在跑,最彻底的瞎。Healey 在能力范围内把最后一种瞎往外推:分离进程,引入外部盲测,定义时间语义,让文档安全,让支持可触达。每一项都不是功能,是责任的架构化。
我不太同意一种叙事,说这是“独立开发者的执着”或“精益创业的好习惯”。太轻了。这是品类的基本成本。监控产品的毛利,必须扣掉一部分去填“你自己也是会死的进程”这个事实。大公司靠冗余,小公司靠外部依赖,但这个成本只能被架构吸收,不能被营销消除。一个小监控服务能不能被人信,分水岭就在这:它有没有把自己的失真风险当产品的一部分处理。
我判断的不是 Healey 做得对不对,他做得对,几乎没有模棱两可。我判断的是这个品类对单人开发者的残酷程度。门槛不在技术,在信任。技术门槛一个人几天能磨平。信任门槛要求你连续面对自己最不想面对的失败模式,每次都选不回避。一旦开始回避,产品就死,不是被竞争对手干掉,是用户根本不敢把重要任务放上来。功能强但没人信的监控服务,比功能弱但可靠的监控服务多得多。前者消失得快,后者慢慢活着,活着的那个永远在做同一件事:加深递归。
评论区的结构和关联 ID 讨论也是这条线。外部告警来了之后,你怎么把一次故障的内部执行上下文拼回来。一个 log 本身不救人,但 log 里带 monitor ID、run ID、时长,诊断就从“某东西断了”变成“这个任务拖了 34 秒,卡在 post-processing”。这是从能力到信任的又一层。Healey 看到了 structlog 的 contextvars 支持的价值,还没接上。真细算该做,但优先级低于备份和恢复验证——监控服务先保证自己的数据库不会某天悄悄没了。数据没了,日志和关联 ID 再漂亮也白搭。
翻译成人话:监控产品的用户要的从来不是更聪明的告警,是更少的“你骗了我”。Healey 结尾问读者还需要看到什么证据,才敢把无人看管的任务交给一个微型监控服务。分两层。第一层是功能证据,文档、时间参数、状态定义、集成示例说不说得清。第二层是信任证据,就是“你死了我怎么知道”的完整答案。大多数小监控服务只给得出第一层,而第一层在真正的故障面前一文不值。
接下来一两年监控工具值得看的方向,不是变得多聪明,而是能不能证明自己不蠢。异常检测、AI 驱动告警降噪、agent 工作流嗅探,这些东西都在信任层没有积累。一个检测不出来的死进程,用最先进的 LLM 分析遥测也晚了,那只死进程根本没有遥测。顺序必须反过来:先成为可靠的、能从停机中醒来的工具,再成为会思考的工具。这一层错了,聪明就是噪声。
我的判断摆在这儿:对监控/可观测性产品来说,可靠性不是功能清单上的一项,是一串持续向深处延展的故障域分离。判断一个监控工具能不能信,唯一有效的问题是:如果你自己死了,你如何被看见?答案必须是具体的、架构的、测试过的,不是一句“我们是云托管”。PulseWatch 走在这条路上,而且走得诚实。
这个判断可能错。错的条件是:监控服务的用户群体对信任没有我以为的那么敏感,人们依然只肯为功能买单,不为失败域分离付溢价。那样的话,Healey 做的事更接近自娱自乐,不是品类准入条件。但只要 unattended workloads 继续膨胀,只要 AI agent 开始在没有人工监督的管道里跑,监控工具对自身的失真就会从开发者的深夜焦虑变成具体的商业责任。到那天,谁先把这层递归说清楚、做出来、写出来,谁在这个品类里就有了一个别人很难用钱买走的东西。我等着看。
