Heartbeat-Monitore
Heartbeat-Monitore kehren die Richtung um - dein Job meldet uns, dass er lebt. Zwei Modi: Geplant (erwartetes Intervall) und Event-Receiver (ruhend; löst nur aus, wenn Trigger auf das Payload passen).
MODI
- Geplant: Trifft innerhalb des konfigurierten Intervalls kein Beat ein, wechselt der Status auf MISS und Alarme werden ausgelöst.
- Geplanter expliziter Fehler: Rufe dieselbe URL mit ?status=fail auf, wenn der Job lief, aber ein schlechtes Ergebnis erkannte. Dies zeichnet einen KO-Beat auf und löst die Ausfallbehandlung sofort aus.
- Event-Receiver: Der Status bleibt ruhend. Jeder Beat speichert sein Payload; Trigger mit Bedingungen werten es aus. ?status=fail wird im Event-Receiver-Modus ignoriert, da die Trigger-Bedingungen entscheiden, was zählt.
OK beat:
curl -fsS https://keepitalive.dev/heartbeat/{token}
Explicit failed beat:
curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail"GEPLANTE REZEPTE
*/5 * * * * curl -fsS https://keepitalive.dev/heartbeat/{token} >/dev/null0 2 * * * /usr/bin/backup.sh && curl -fsS https://keepitalive.dev/heartbeat/{token}curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail&payload=disk+full"*/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:: 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 5CRON-ZEITPLAENE
Ein einfacher Heartbeat erwartet alle N Sekunden ein Signal - fuer einen Job, der nach Kalender laeuft, ist das die falsche Form: Ein Backup nur an Werktagen wirkt jeden Sonntag 65 Stunden verspaetet. Setzen Sie stattdessen einen Cron-Ausdruck, dann ist die Frist der naechste geplante Lauf plus Toleranz - nichts ist verspaetet, bevor etwas faellig war. Waehlen Sie die Zeitzone, in der der Job laeuft: Der Zeitplan wird dort ausgewertet, und genau das haelt einen 03:00-Job bei 03:00, wenn die Uhren umgestellt werden. Der Ausdruck bestimmt auch die Kosten, denn er legt fest, wie viele Laeufe pro Stunde wir erwarten. Die Ingest-URL bleibt gleich; nur die Erwartung, wann ein Signal faellig ist, aendert sich.
# 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.EXIT-CODES
Hänge den Exit-Status deines Jobs an die URL an, dann meldet sich der Beat selbst: 0 ist Erfolg, alles andere ein Fehler. Das ist besser als eine Verkettung mit && , die bei einem fehlgeschlagenen Job gar nichts sendet - der Monitor geht dann erst später auf DOWN, wenn das Fenster verpasst wird, statt sofort beim Fehler. Der Code wird am Beat gespeichert, sodass eine Trigger-Bedingung nach der Fehlerart verzweigen kann: payload.exit_code == 137 (wegen Speicher beendet) kann einen Ausfall öffnen, während payload.exit_code == 1 nur benachrichtigt. Codes müssen 0-255 sein; ohne konkreten Code kannst du auch /fail verwenden.
# $? 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}/$?LAUFZEIT
Pingen Sie /start beim Start des Jobs und senden Sie am Ende einen normalen Beat; die verstrichene Zeit wird als duration_ms auf dem Beat gespeichert, sodass eine Bedingung bei langsamen Läufen alarmieren kann - payload.duration_ms > 300000 fängt ein Backup ab, das über fünf Minuten brauchte, obwohl es am Ende erfolgreich war. /start ist völlig optional: Ein einzelner abschließender Beat ist für sich genommen eine vollständige Meldung, und Miss-Erkennung sowie Exit-Codes funktionieren ohne ihn. Ihn zu senden bringt genau zwei Dinge: eine erfasste Dauer und die Erkennung eines Laufs, der nie endet. Ein /start allein meldet nichts und ändert den Monitorstatus nicht: Ein Job, der startet und dann hängt, muss weiterhin sein Zeitfenster verpassen. Ein zweiter /start bei noch offenem Lauf wird abgelehnt, denn zwei Starts ohne Abschluss dazwischen bedeuten, dass der erste Lauf nie gemeldet hat; die Ablehnung endet, sobald dieser Lauf überfällig ist. Länger als 24 Stunden offene Läufe werden verworfen.
# /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}/$?FESTSTECKENDE LÄUFE
Wenn ein Job /start aufruft und dann hängt, ist zunächst nichts überfällig — der Start hat nichts aufgefrischt, also sieht der Monitor gut aus, bis der nächste Beat fällig gewesen wäre. Genau diese Lücke ist das Problem beim Messen eines Jobs: ein feststeckender Lauf ist der Fehler, den du fangen wolltest. Ein Lauf ohne abschließenden Beat geht deshalb auf DOWN, sobald er Intervall plus Karenzfenster überschreitet, mit einer Fehlermeldung zur bisherigen Laufzeit. Es gibt keine separate Maximallaufzeit: ein überfälliger Beat und ein feststeckender Lauf sind dieselbe Frage — wie lange warten wir — also beantwortet die bereits gesetzte Karenzzeit beides.
PAYLOADS
Sende JSON per POST an deine Heartbeat-URL, um Systemmetriken oder beliebige Felder zu melden. Die Karte „Letzter Bericht“ auf der Monitor-Detailseite zeigt das neueste Payload nach Möglichkeit als formatiertes JSON an, mit Rohtext als Fallback.
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-WARTUNG
Der Event-Receiver-Modus ist für Deploy-Pipelines nützlich. Erstelle einen Payload-Trigger mit der Bedingung payload.status == "maintenance" und der Vorfallklassifizierung maintenance. Sende ein Wartungs-Payload vor dem Deploy und ein running/ok-Payload in einem finalen Schritt mit if: always(). Der Vorfall öffnet sich, solange der Ausdruck zutrifft, und schließt, wenn das nächste akzeptierte Payload ihn aufhebt.
- 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}}"}'Event-Receiver drosseln akzeptierte Payloads anhand des Monitor-Intervalls. Für sehr kurze Deploy-Jobs nutze das kleinste von deinem Plan erlaubte Intervall oder verzögere das finale Payload, bis das Intervall verstrichen ist. Verstummt die Quelle, geht KEEPitALIVE nicht von einer Wiederherstellung aus.
#!/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\"}"$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"