Help CenterOpen app

心跳监控

心跳监控反转了方向 - 由你的作业告诉我们它还活着。两种模式:Scheduled(预期间隔)和 Event-Receiver(休眠;仅当触发器匹配负载时才触发)。

模式

  • Scheduled:如果在配置的间隔内没有心跳到达,状态翻转为 MISS 并触发告警。
  • Scheduled 显式失败:当作业运行了但检测到不良结果时,用 ?status=fail 调用同一 URL。这会记录一次 KO 心跳并立即触发 down 处理。
  • Event-Receiver:状态保持休眠。每次心跳存储其负载;带条件的触发器据此评估。Event-Receiver 模式会忽略 ?status=fail,因为由触发器条件决定何为重要。
Scheduled 心跳端点
OK beat:
curl -fsS https://keepitalive.dev/heartbeat/{token}

Explicit failed beat:
curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail"

SCHEDULED 配方

每 5 分钟一次 cron 心跳
*/5 * * * * curl -fsS https://keepitalive.dev/heartbeat/{token} >/dev/null
作业成功后的心跳
0 2 * * * /usr/bin/backup.sh && curl -fsS https://keepitalive.dev/heartbeat/{token}
显式上报失败
curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail&payload=disk+full"
隔离网络中继:从可达网关 ping 内网主机
*/1 * * * * if ping -c 1 -W 2 192.168.1.50 >/dev/null 2>&1; then \
  curl -fsS "https://keepitalive.dev/heartbeat/{token}"; \
else \
  curl -fsS -X POST "https://keepitalive.dev/heartbeat/{token}?status=fail" \
    -d "unreachable"; \
fi
Windows 任务计划程序(.bat)
:: C:\scripts\keepalive_heartbeat.bat
@echo off
curl -fsS https://keepitalive.dev/heartbeat/{token} >nul 2>&1

:: schtasks /create /tn "KEEPitALIVE" /tr C:\scripts\keepalive_heartbeat.bat /sc minute /mo 5

CRON 计划

普通心跳每 N 秒等待一次信号,这不适合按日历运行的任务:仅工作日运行的备份在每个周日看起来都迟到了 65 小时。改为设置 Cron 表达式后,期限变成“下一次计划运行加上宽限时间”——在有东西到期之前,不存在迟到。请选择任务实际运行的时区:计划在该时区求值,这样夏令时切换后 03:00 的任务仍在 03:00。表达式同时决定费用,因为它确定我们每小时预期多少次运行。接收 URL 不变,改变的只是我们何时期待信号。

让监控与 crontab 保持一致
# Give the monitor the same schedule the job runs on.
0 3 * * 1-5      # 03:00, weekdays only
*/15 * * * *     # every 15 minutes
0 3 1 * *        # 03:00 on the 1st of the month
@daily           # midnight

# Pick the zone the job runs in, not the server's: that is
# what keeps 03:00 at 03:00 when the clocks change.

退出码

把任务的退出码附加到 URL 后面,心跳就会自行上报结果:0 表示成功,其他值表示失败。这比用 && 串联更好——任务失败时 && 什么都不会发送,监控要等到错过时段才会转为宕机,而不是在任务出错的那一刻。退出码会保存在该心跳上,因此触发器条件可以按失败类型分流:payload.exit_code == 137(因内存被终止)可以开启一次中断,而 payload.exit_code == 1 只发送通知。退出码必须在 0-255 之间;如果手头没有退出码,也可以使用 /fail。

上报退出码
# $? is the exit status of the job that just ran
0 2 * * * /usr/bin/backup.sh; curl -fsS https://keepitalive.dev/heartbeat/{token}/$?

# Send the job output too, so the alert says why it failed.
# Capture it first: in a pipeline $? is the last command's status,
# not the job's.
0 2 * * * out=$(/usr/bin/backup.sh 2>&1); \
  curl -fsS --data-binary "$out" https://keepitalive.dev/heartbeat/{token}/$?

运行时长

作业开始时请求 /start,结束时发送普通信号,耗时会以 duration_ms 记录在该信号上,因此可以对缓慢的运行发出告警——payload.duration_ms > 300000 能捕获耗时超过五分钟的备份,即使它最终成功。/start 完全是可选的:仅一个结束信号本身就是完整的报告,缺失检测和退出码在没有它的情况下同样有效。发送它只带来两样东西:记录的运行时长,以及对永不结束的运行的检测。单独的 /start 不报告任何内容,也不改变监控状态:启动后卡住的作业仍必须错过它的时间窗口。当一次运行仍处于打开状态时,第二个 /start 会被拒绝,因为两次启动之间没有结束意味着第一次运行从未报告;一旦该运行超时,拒绝即解除。打开超过 24 小时的运行会被丢弃。

为任务计时
# /start is optional. Without it you still get miss detection
# and the exit status - just no duration and no stuck-run alert:
0 2 * * * /usr/bin/backup.sh; curl -fsS https://keepitalive.dev/heartbeat/{token}/$?

# With it, the run is timed and a job that never finishes is caught:
0 2 * * * curl -fsS https://keepitalive.dev/heartbeat/{token}/start; \
  /usr/bin/backup.sh; \
  curl -fsS https://keepitalive.dev/heartbeat/{token}/$?

卡住的运行

如果任务访问了 /start 然后卡住,此刻还没有任何东西算迟到——开始并不会刷新任何计时,所以在下一次心跳到期之前,监控看起来一切正常。这个空档正是为任务计时的关键问题:卡住的运行恰恰就是你想捕捉的故障。因此,未发送结束心跳的运行一旦超过间隔加宽限时间就会转为宕机,并在错误中说明已经运行了多久。没有单独的最大运行时长设置:心跳迟到和运行卡住本质是同一个问题——我们要等多久才判定失败——所以你已经设置的宽限值同时回答了两者。

负载

向你的心跳 URL POST JSON 以上报系统指标或任意字段。监控详情页的“最新报告”卡片会尽可能将最新负载渲染为美观的 JSON,并回退为原始文本。

简单的 JSON 负载
curl -fsS -X POST https://keepitalive.dev/heartbeat/{token} \
  -H 'Content-Type: application/json' \
  -d '{"job":"nightly-backup","status":"ok","rows":42850}'

EVENT RECEIVER:CI/CD 维护

Event Receiver 模式适用于部署流水线。创建一个负载触发器,条件为 payload.status == "maintenance",事件分类为 maintenance。在部署前发送 maintenance 负载,并在 if: always() 的最终步骤发送 running/ok 负载。当表达式匹配时事件开启,当下一个被接受的负载清除它时关闭。

GitHub Actions 维护周期
- name: Start maintenance
  run: |
    curl -fsS -X POST "$KEEPITALIVE_HEARTBEAT_URL" \
      -H "Content-Type: application/json" \
      -d '{"status":"maintenance","source":"github-actions","sha":"${{github.sha}}"}'

- name: Deploy
  run: ./deploy.sh

- name: End maintenance
  if: always()
  run: |
    curl -fsS -X POST "$KEEPITALIVE_HEARTBEAT_URL" \
      -H "Content-Type: application/json" \
      -d '{"status":"running","source":"github-actions","sha":"${{github.sha}}"}'

事件接收器按监控间隔对被接受的负载进行限流。对于非常短的部署作业,使用你套餐允许的最小间隔,或将最终负载延迟到间隔已过之后。如果源变得静默,KEEPitALIVE 不会假定已恢复。

Linux / macOS - 系统指标
#!/bin/bash
URL="https://keepitalive.dev/heartbeat/{token}"
CPU=$(top -bn1 | grep "Cpu" | awk '{print $2}')
RAM=$(free -m | awk '/Mem/{printf "%.1f", $3/$2*100}')
DISK=$(df -h / | awk 'NR==2{print $5}')
curl -fsS -X POST $URL \
  -H "Content-Type: application/json" \
  -d "{\"hostname\":\"$(hostname)\",\"cpu\":\"$CPU%\",\"ram\":\"$RAM%\",\"disk\":\"$DISK\"}"
Windows PowerShell - 系统指标
$url = "https://keepitalive.dev/heartbeat/{token}"
$os  = Get-CimInstance Win32_OperatingSystem
$body = @{
  hostname = $env:COMPUTERNAME
  cpu      = "{0:N1}%" -f (Get-CimInstance Win32_Processor).LoadPercentage
  ram      = "{0:N1}%" -f ((1 - $os.FreePhysicalMemory / $os.TotalVisibleMemorySize) * 100)
} | ConvertTo-Json
Invoke-RestMethod -Uri $url -Method POST -Body $body -ContentType "application/json"
下一篇通知