cron半夜悄悄失败了你知道吗?Healthchecks死人开关实战,让静默故障无处藏身


导读:本站cron篇教了一堆定时任务的写法,备份篇配了凌晨三点的备份流水线,vnstat篇加了流量检查,发信篇接了告警邮件——但所有这些方案有个共同的盲区:cron任务失败时它会写日志、会发邮件,可如果任务压根没被触发呢?机器重启后crond没起来、脚本第一行就崩了、或者整台机器失联——这些情况下"失败通知"本身就是不存在的。监控界管这叫死人开关问题:监控者也需要被监控。Healthchecks.io的开源版就是解药:任务每次成功跑完向它报个到,超时没报到就告警。这篇讲透:死人开关的原理、Docker部署、给cron任务接入的两行改造、多个任务的编排、误报治理、以及和站内监控体系的三层叠加。

一、死人开关:反向监控的缺环

先理解为什么现有的告警全有盲区。cron任务失败的三种形态:第一种,脚本执行了但报错——这种好办,脚本里 || echo failed | mail 那套(备份篇讲过)能抓住;第二种,脚本执行了但"成功得不对劲"——比如备份脚本正常退出但备份文件是空的,这在运维里叫"假成功",靠退出码根本发现不了;第三种,最阴险的——任务压根没执行:crond挂了、机器重启后服务没拉起、机器整个失联。第三种形态下,一切"出错时发通知"的机制集体沉默,因为通知的发起者本身就是死掉的。死人开关(dead man's switch)反过来设计:监控系统不等你出事,它持续期待你的"心跳"——每个cron任务跑完ping一下Healthchecks,约定"我每天凌晨三点后一定会报到",到了早上九点还没收到心跳,Healthchecks主动告警。期待落空本身就是信号,这就把"发起者已死"的盲区补上了。火车行业的司机警惕装置(司机每几秒必须操作一次,否则列车自动刹停)就是同一个原理。

二、部署:一个Compose服务加一个SQLite

Healthchecks是Python写的开源项目,Docker部署是官方推荐姿势(Compose篇的栈理念继续扩容):

services:
  healthchecks:
    image: linuxserver/healthchecks
    volumes:
      - ./healthchecks:/config
    ports:
      - "8000:8000"
    environment:
      - SITE_ROOT=https://hc.example.com
      - SITE_NAME=My Healthchecks
      - EMAIL_HOST=mail.example.com
      - EMAIL_PORT=587
      - EMAIL_HOST_USER=hc
      - EMAIL_HOST_PASSWORD=YourPass
      - DEFAULT_FROM_EMAIL=hc@example.com
    restart: unless-stopped

几个要点:SITE_ROOT要填最终访问地址(Gitea篇踩过的坑:这个值会写进ping URL和邮件链接,反代配好后记得改对再建任务,建完改URL会导致旧URL失效);邮件配置直接复用发信篇搭好的Postfix,Healthchecks的告警、周报、登录验证都靠它;数据落在/config卷里的SQLite,个人规模几十个监控任务毫无压力(Gitea篇说过的量级判断,同样适用)。起来之后浏览器访问8000端口注册账号,先别急着建任务——把它套上Nginx反代和HTTPS(反代篇+HTTPS篇的第三次复用),因为ping URL会出现在你的crontab里,明文HTTP等于把任务清单广播给中间人。

三、接入:每个任务两行改造

Healthchecks的接入设计堪称极简主义的典范。在网页上新建一个Check,拿到一个专属UUID的ping URL:https://hc.example.com/ping/你的uuid。然后给cron任务加一行(cron篇的五行口诀再扩一行):

0 3 * * * /usr/local/bin/backup.sh && curl -fsS --retry 3 https://hc.example.com/ping/uuid1 > /dev/null

任务成功才curl报到,失败则沉默——沉默会在宽限期后被判定为异常。任务在网页上设置Schedule为daily、Grace时间为6小时:凌晨三点跑,最晚早上九点还没心跳就告警。备份篇那条流水线每个任务一行一个URL,互不干扰。假成功问题(第二种形态)也有了抓手:backup.sh结尾加一段自检,dump文件存在且大于昨天的一半才返回0,自检通过curl才执行——心跳从"脚本跑完了"升级成"脚本跑对了我验证过",这是死人开关能给你的最大增量。

四、心跳的正确姿势:成功、失败、进度三态

Healthchecks的ping其实有三个端点,用好它们监控精度翻倍:基础URL /ping/uuid报成功;/ping/uuid/fail报失败——脚本捕获自己的退出码,0发success、非0发fail,Healthchecks面板上红绿一目了然,比"沉默等超时"快速得多;/ping/uuid/start报开始——脚本第一行curl这个,面板能算出任务实际跑了多久,突然从5分钟变成2小时,哪怕最终成功也值得看一眼(备份膨胀的早期信号,比日志治理篇的磁盘告警更早一步预警)。一个改造后的脚本骨架:

#!/bin/bash
curl -fsS https://hc.example.com/ping/uuid1/start > /dev/null
/usr/local/bin/do_backup.sh
rc=$?
curl -fsS --retry 3 https://hc.example.com/ping/uuid1/$([ $rc -eq 0 ] && echo "" || echo "fail") > /dev/null
exit $rc

start和finish之间如果脚本被kill,Healthchecks会显示"跑了很久没下文",配合宽限期照样告警。三态心跳+超时判定,把cron任务的黑盒运行状态拆解成了可观测的时间线。

五、编排与治理:从单点到体系

任务多了(个人玩家典型的量级:备份四件套、证书检查、流量检查、日志清理,十个上下),治理要点有三个。第一,命名规范:Check的名字直接用cron注释的原文(backup-db-daily、traffic-check-daily),出告警时邮件里看到名字就知道是哪台机器哪个任务,多机用户建议名字带机器前缀(vps1-backup-db)。第二,分级宽限:备份流水线的四条任务(备份篇那四行cron)是串行依赖的,db没dump成功后面三条全会fail——给它们设同一个Tag,第一个任务宽限6小时、后续任务宽限9小时,连环告警就不会在凌晨连环轰炸。第三,告警渠道分散:Healthchecks本身支持邮件、Telegram、Webhook多渠道,重要任务双通道;Webhook还能打给哪吒或Uptime Kuma类面板做统一展示——但记住发信篇的教训:单一告警通道本身就是单点,邮件加IM双通道是底线。

六、防误报:宽限期的艺术

死人开关最大的敌人是误报,狼来了三次你就开始忽略告警了。误报三大来源和对策:其一,任务时长抖动——周末备份量大跑得久,宽限期按最坏情况设(日常1小时、最坏4小时,就设4小时),宁可告警晚一点也不要误报;其二,时间基准——Healthchecks判断超时用的是服务器本地时间,Docker容器默认UTC,你的cron跑在宿主机时区,两边差8小时会让"每天"的判定窗口错位,compose里加TZ=Asia/Shanghai把容器时区和宿主机对齐(这个坑和Gitea篇的ROOT_URL同属"建站先对齐元数据"系列);其三,计划内变更——你手动改了cron时间或临时停任务,先去面板把这个Check暂停(Pause),否则它必然超时告警。每周扫一眼面板的"最晚心跳"列,所有绿着的就是全世界安好——这个"看一眼全是绿的"的仪式感,是死人开关体系成熟后的日常奖励。

七、监控监控者:套娃的终点

聪明的读者会问:Healthchecks自己挂了怎么办?ping发不出去,任务侧curl报错(但脚本继续跑,注意curl失败不该影响任务本身——所以用 ; curl 而不是 && curl 的场景要分清:报心跳失败但备份已成功,和备份失败,是两个严重级别),而你收不到任何告警,因为告警系统就是那个死掉的东西。最后一层套娃:用外部的另一个监控盯Healthchecks本身。两个选项:免费的Healthchecks.io官方SaaS给你发一封"本服务周报",停发即异常(这本身就是死人开关套死人开关);或者用另一台机器的Uptime Kuma(站内那篇的成果)反代探测hc.example.com的存活,机器A监控机器B、机器B监控机器A的互备结构。这套娃听起来繁琐,但它回答了一个哲学问题:监控体系的可靠性上限,取决于你最外层那个"没人监控的监控者"。个人规模到互备这层就是终点,再往上就是云厂商的地盘了。

八、成本收益账:为什么这个麻烦值得

算笔总账:Healthchecks部署半小时,十个任务的接入每个五分钟,合计一个晚上。收益端:备份篇的恢复演练告诉你"没验证的备份不算备份",Healthchecks把这句话推广到全部定时任务——没报心跳的cron不算在跑的cron。你过去依赖的是"偶而想起去journalctl翻一眼"(cron篇的排障姿势),现在升级为"每个任务的每次执行都有回执、每次缺席都有告警"。凌晨三点的机器在做什么,你早上看一眼全绿的仪表盘就全知道了——这种确定性,正是自动化运维和"配了一堆自动化但心里没底"之间的分水岭。

结语

死人开关是监控体系里最容易被忽略的一块,因为它监控的是"没有事情发生"——而人类天然只注意发生的事。Healthchecks用最朴素的心跳机制补上这个盲区,配合站内已有的体系:哪吒盯机器活着、Uptime Kuma盯服务应答、Healthchecks盯任务真的在跑,三层各管一摊,你的自托管栈从"看起来都在跑"进化到"我知道它们都在跑"。今晚就把备份篇那四行cron接上心跳,明天早上八点,你会收到第一封"一切正常"的邮件——那四个字,值回全部配置时间。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

API按token计费肉疼?Ollama+Open WebUI私有化部署实战,小鸡也能跑起自己的AI大模型

手动docker pull更新到烦了?Watchtower容器自动更新实战,让自托管应用永葆最新

评 论
更换验证码