用 Healthchecks 检查定时任务有没有漏跑

让 cron 任务向 Healthchecks 报开始、成功或失败。保留任务退出码,只有导出、检查及远端上传完成后才报告成功。

网站仍能打开,凌晨备份却可能停了几天。页面监控看不到 cron 漏跑、导出失败或上传未完成。Healthchecks 能接收定时任务的执行信号,超过约定时间没收到也会报警。

Healthchecks 接收脚本发来的开始、成功和失败信号。监控是否可靠,首先取决于你把“成功”定义在哪里:只有生成文件,还是已经验证并上传到独立存储。

每个任务使用独立检查项

先建立或准备一个可用的 Healthchecks 实例,再为每台机器上的明确任务创建检查项,例如“每天导出数据库并完成远端上传”。数据库、媒体与清理任务执行周期不同,应分别配置;几台服务器共享一个地址,会让一台正常汇报掩盖另一台故障。

固定凌晨两点运行的任务,用 cron 模式并核对时区;按间隔运行的任务,配置 Period 与 Grace Time。监控端、宿主机和容器的时区不能只看表达式相同,应对照下一次实际执行时间。宽限时间包含正常运行与通知波动,按观察到的任务时长安排。

监控尽量放在不会与任务主机一起故障的位置。同机部署时,任务与监控可能一起停止,通知也发不出去;已有独立机器先复用,需要另建节点时再比较月付 VPS,同时给监控自身增加外部检查。

只运行任务监控时,先按检查项、通知渠道和数据库保留量选独立的小实例,位置与被监控机器分开。可比较 Evoxt 的独立监控节点,或查看 雨云 RCS 的地区与小型配置。使用已有托管监控服务时并不需要再买服务器;选择自建后,先模拟漏跑,检查实际接收者能否收到通知,再决定长期账期。

检查地址里的标识能用于伪造正常信号,按凭据保存。不要放在公开仓库和截图里;下文 HC_URL 由受限配置提供,填写自己实例生成的完整地址。

包装脚本怎样保留任务退出码

假定真正的任务在 /usr/local/sbin/backup-database,使用 Bash 创建包装脚本。任务程序应在导出、检查与远端上传都完成后才返回 0。

#!/usr/bin/env bash
set -uo pipefail
HC_URL="${HC_URL:?set HC_URL before running}"

ping_check() {
  curl --fail --silent --show-error --max-time 10 --retry 2 \
    "$HC_URL$1" >/dev/null
}
report_exit() {
  status=$?
  trap - EXIT
  if [ "$status" -eq 0 ]; then suffix=""; else suffix="/fail"; fi
  if ! ping_check "$suffix"; then
    printf 'notification failed; job status=%s\n' "$status" >&2
  fi
  exit "$status"
}
trap report_exit EXIT

if ! ping_check "/start"; then
  printf 'start notification failed\n' >&2
fi
/usr/local/sbin/backup-database
job_status=$?
exit "$job_status"

基础地址表示成功,/start 表示开始,/fail 表示失败。退出处理一进入就保存 $?,后面的 curl 和日志不覆盖任务结果。开始通知失败仍执行备份;结束通知失败留下日志,包装脚本仍返回任务原始状态。

这样可以区分“任务失败”和“通知网络失败”。后者可能让监控因缺失信号报警,收到消息后先查真实产物,不要自动重复执行有副作用的任务。--max-time 限制单次请求,有限重试仍会增加总耗时,需要计入宽限时间。

产物存在不等于能恢复

下面的管道如果没有正确处理状态,数据库导出失败而 gzip 成功时,也可能得到成功退出:

pg_dump appdb | gzip > appdb.sql.gz

真正的备份脚本使用适当的 pipefail,先写 .partial 文件,成功后再改成正式文件名。检查非空和压缩格式,只能发现一部分文件问题;还要检查远端对象,并定期恢复到独立数据库验证主要表和应用读取。

Healthchecks 无法替你判断一个文件是否包含完整数据。若任务在上传前就打印“完成”并返回 0,监控会忠实记录这个错误的成功定义。不要将后台上传进程启动当成上传结束,也不要让旧文件的存在遮住本次导出失败。

终端正常,为什么 cron 失败

终端运行成功而定时任务失败,先查 PATH、工作目录、文件权限与配置加载。包装程序和备份程序使用必要的绝对路径;检查地址由执行账户可读的可信配置加载,日志写到可写目录,不依赖交互式 shell 自动加载环境。

如果单次备份可能超过调度间隔,还要防止重复运行。采用适合系统的锁或调整计划,避免两次任务覆盖同一输出;检查项独立并不能解决主机上的并发问题。

先用专用演练任务测试正常完成、非零退出、只发开始不结束、完全漏跑四种情况。再模拟通知不可达,确认备份退出状态保留、日志可见,告警能抵达实际接收者并有恢复消息。演练不必通过破坏正式数据库或删除备份制造故障。

迁移服务器时,先验证新机器手工执行,再等一次自动计划真正完成。旧机器任务停用后检查监控关系,避免新旧机器交替发信号。一次手工补跑让告警消失,只能说明这次汇报成功,不能证明明天的 cron 已经修好。