Zurück zum Blog

Cron-Jobs überwachen: das Heartbeat-Pattern in der Praxis

· 6 Min. Lesezeit

HTTP-Monitoring sagt Ihnen nicht, dass ein nächtlicher Cron-Job ausgefallen ist. Das Heartbeat-Pattern dreht die Richtung um: Der Cron-Job meldet sich beim Monitoring, und wenn er sich nicht rechtzeitig meldet, bekommen Sie eine Benachrichtigung.

Cron-Jobs überwachen: das Heartbeat-Pattern in der Praxis

Das Problem: Hintergrund-Jobs ohne HTTP-Endpoint

Ein typisches Backend hat mehrere Pfade:

  • Web-Requests (HTTP/HTTPS zum Server) - die überwachen Sie mit einem Uptime-Check.
  • Cron-Jobs (tägliches Backup, monatliche Abrechnung, stündliche Synchronisation) - sie haben keinen HTTP-Endpoint, externes Monitoring kann sie nicht beobachten.
  • Worker (Celery, BullMQ, Sidekiq), die eine Queue abarbeiten - ebenfalls ohne HTTP.

Wenn ein Cron-Job ausfällt (ein Tippfehler im Crontab, eine volle Festplatte, eine fehlende Umgebungsvariable (environment variable) oder ein Import-Fehler nach einem Dependency-Upgrade), benachrichtigt Sie niemand. Sie erfahren es am Montagmorgen, wenn Ihnen auffällt, dass über das Wochenende keine Rechnungen verschickt wurden.

Das Heartbeat-Pattern: der Cron-Job pingt das Monitoring an

Das Prinzip ist gegenüber dem normalen Monitoring umgekehrt:

  1. Im Monitoring-Dienst erstellen Sie einen Heartbeat-Monitor mit einem erwarteten Intervall (zum Beispiel "alle 60 Minuten").
  2. Sie erhalten eine eindeutige Heartbeat-URL: https://epulz.io/heartbeat/abc123xyz.
  3. In Ihrem Cron-Job rufen Sie am Ende eines erfolgreichen Laufs diese URL auf (HTTP GET oder POST).
  4. Wenn der Ping nicht innerhalb der erwarteten Zeit (plus Grace-Periode) eintrifft, benachrichtigt Sie das Monitoring.

Praktisches Beispiel: Heartbeat aus einem Cron-Job

# /etc/crontab
0 3 * * * www-data /usr/local/bin/backup.sh && curl -fsS -m 10 \
  https://epulz.io/heartbeat/abc123xyz > /dev/null

Entscheidend ist der Operator &&: Der Heartbeat wird nur dann gesendet, wenn backup.sh mit Exit-Code 0 endet. Schlägt das Skript fehl, kommt der Ping nicht an und Sie erhalten innerhalb einer Stunde eine Benachrichtigung.

Tipp: Für eine gründlichere Abdeckung fügen Sie auch einen "Start"-Heartbeat hinzu:

curl -fsS -m 10 https://epulz.io/heartbeat/backup-start-xyz > /dev/null
/usr/local/bin/backup.sh && \
  curl -fsS -m 10 https://epulz.io/heartbeat/backup-done-xyz > /dev/null

Das Monitoring kann dann "gestartet, aber nicht beendet" (das Skript hängt) von "gar nicht gestartet" (der Cron-Job lief nie) unterscheiden.

Python: requests + try/except

import os, requests
HEARTBEAT_URL = os.environ["HEARTBEAT_URL"]

def sync_data():
    # ... your logic ...
    pass

try:
    sync_data()
    requests.get(HEARTBEAT_URL, timeout=10)
except Exception as e:
    # Der Heartbeat wird nicht gesendet - das Monitoring benachrichtigt Sie
    raise

Node.js: async / await

const HEARTBEAT_URL = process.env.HEARTBEAT_URL;

async function nightlyJob() {
  await processInvoices();
  await fetch(HEARTBEAT_URL, { signal: AbortSignal.timeout(10000) });
}

nightlyJob().catch(err => {
  console.error(err);
  process.exit(1);
});

Grace-Periode: wie viel Zeit vor der Benachrichtigung einräumen

Ein Heartbeat-Monitor braucht eine gewisse Toleranz. Ein Cron-Job läuft gelegentlich länger als üblich, das Netzwerk hat Latenz und die NTP-Synchronisation kann leicht verschoben sein. Die Grace-Periode ist die Zeit nach Ablauf des erwarteten Intervalls, während der das Monitoring noch wartet, bevor es eine Benachrichtigung auslöst.

Praktische Werte:

  • Stündlicher Cron: Intervall 60 Min. + Grace 10 Min.
  • Tägliches Backup (durchschnittlich 20 Min.): Intervall 1440 Min. + Grace 60 Min.
  • Wöchentlicher Report: Intervall 10080 Min. + Grace 360 Min. (6 h)

Zu knappe Grace = falsche Benachrichtigungen (False Positives). Zu großzügige = verspätete Benachrichtigung genau dann, wenn der Job tatsächlich ausfällt.

Wo das Heartbeat-Pattern am meisten hilft

  • Nächtliche DB-Backups
  • Synchronisation mit externen APIs (CRM, Buchhaltung, Payment)
  • Report-Berechnungen
  • Cleanup-Jobs (Löschen alter Sessions, Logs, temporärer Dateien)
  • Healthcheck-Zyklus langlaufender Worker
  • Geplante E-Mails, Newsletter, Abrechnung

Fazit

Hintergrund-Jobs sind oft kritischer als die Website selbst, bleiben aber ein blinder Fleck des Monitorings. Das Heartbeat-Pattern braucht nur fünf Minuten zur Implementierung (einfach curl ans Ende der Cron-Zeile setzen) und bietet die gleiche Sicherheit wie das Uptime-Monitoring des Frontends.

Kurze Erinnerung: Cron-Syntax

Die meisten Heartbeat-Fehler sind in Wahrheit Cron-Fehler - der Ping feuert nie, weil der Zeitplan falsch war. Die fünf Felder sind:

┌── Minute (0-59)
│ ┌── Stunde (0-23)
│ │ ┌── Tag des Monats (1-31)
│ │ │ ┌── Monat (1-12)
│ │ │ │ ┌── Wochentag (0-7, 0 und 7 = Sonntag)
│ │ │ │ │
* * * * *  Befehl
Ausdruck Läuft
*/5 * * * * alle 5 Minuten
0 * * * * zu Beginn jeder Stunde
0 3 * * * täglich um 03:00
0 3 * * 1 jeden Montag um 03:00
30 2 1 * * um 02:30 am ersten Tag des Monats

Die klassische Falle: */5 im Feld Stunde bedeutet nicht "alle 5 Stunden ab jetzt", sondern durch fünf teilbare Stunden (0, 5, 10, 15, 20). Wenn das erwartete Intervall des Monitors und der reale Cron-Zeitplan nicht übereinstimmen, bekommen Sie Phantom-Benachrichtigungen.

Das Prinzip "Dead Man's Switch"

Das Heartbeat-Pattern ist ein Dead Man's Switch (Totmannschalter): Der Alarm ist die Stille. Das kehrt den üblichen Fehlerfall um. Normales Monitoring kann lautlos ausfallen - wenn der Monitoring-Server selbst ausfällt, hört er einfach auf, Benachrichtigungen zu senden, und Sie erfahren es nie. Der Heartbeat ist das Gegenteil: Auslöser einer Benachrichtigung ist das Ausbleiben eines erwarteten Signals, sodass ein Job, der komplett aufhört zu laufen (der ganze Server ist aus, der Cron-Daemon ist deaktiviert, die Maschine wurde ausgemustert und vergessen), genau der Fall ist, den er am besten erfasst.

Jenseits von Cron: andere Scheduler

Das Muster ist gleich, egal was den Job auslöst: - systemd-Timer - fügen Sie curl der ExecStart-Kette oder dem ExecStartPost= hinzu. Koppeln Sie den OnCalendar=-Timer mit einem passenden Heartbeat-Intervall. - Kubernetes CronJob - der letzte Schritt des Containers pingt die Heartbeat-URL. Fällt der Pod aus oder wird er gar nicht geplant (falscher Node-Selector, Image-Pull-Fehler), fehlt der Ping und Sie bekommen eine Benachrichtigung. - Windows Task Scheduler - beenden Sie die Aufgabe mit dem PowerShell-Befehl Invoke-WebRequest -Uri $env:HEARTBEAT_URL -TimeoutSec 10. - CI / geplante Pipelines - ein nächtlicher GitHub-Actions- oder GitLab-Schedule kann als letzten Schritt den Heartbeat pingen, um zu bestätigen, dass die Pipeline lief und auch durchlief.

Häufige Anti-Patterns

  • Ping am Anfang, nicht am Ende. Ein vor der Arbeit gesendeter Heartbeat beweist nur, dass der Job gestartet ist. Setzen Sie ihn hinter die Arbeit, abhängig vom Erfolg (&&), damit ein Absturz mitten im Lauf den Ping ebenfalls zurückhält.
  • Den Exit-Code ignorieren. backup.sh; curl ... (Semikolon) sendet den Ping auch dann, wenn das Backup fehlschlug. Verwenden Sie immer &&.
  • curl-Fehler stillschweigend schlucken. -fsS lässt curl bei einem HTTP-Fehler laut scheitern, sodass die Logs Ihres Jobs zeigen, dass der Heartbeat nicht zugestellt werden konnte (ein Netzwerkproblem auf der Maschine).
  • Ein Heartbeat für zehn Jobs. Wenn fünf Cron-Jobs sich eine URL teilen, wissen Sie nicht, welcher aufgehört hat. Geben Sie jedem geplanten Job seinen eigenen Heartbeat. Wenn Sie für denselben Dienst auch einen HTTP-Endpoint bereitstellen, koppeln Sie den Heartbeat mit einem regulären Uptime-Check und einem Port-Check auf dem Worker-Port, damit Sie sowohl "der Prozess läuft" als auch "der geplante Lauf fand statt" abdecken.

Konkretes Szenario: das stille Backup-Versagen

Der Lehrbuchfall für einen Heartbeat sieht so aus. Ein nächtliches pg_dump schreibt auf ein gemountetes Volume. Es funktioniert monatelang. Dann läuft das Volume voll, pg_dump endet mit einem Exit-Code ungleich null, und weil die Cron-Zeile ein einfaches Semikolon verwendet, wird der Heartbeat trotzdem gesendet und das Dashboard bleibt grün. Drei Wochen später ist die Datenbank beschädigt und es gibt kein brauchbares Backup, weil jedes "erfolgreiche" Backup, seit die Platte voll war, eine Null-Byte-Datei war. Das Heartbeat-Pattern fängt das schon in der allerersten Nacht ab, aber nur, wenn es richtig verdrahtet ist: koppeln Sie den Ping mit &&, damit ein Exit ungleich null ihn zurückhält, und ziehen Sie in Betracht, die Ausgabe zu prüfen, nicht nur den Exit-Code. Ein Backup-Skript kann mit Code 0 enden und trotzdem eine abgeschnittene Datei erzeugen. Eine defensivere Zeile prüft, dass der Dump nicht trivial ist, bevor sie Erfolg signalisiert:

/usr/local/bin/backup.sh && \
  test "$(stat -c%s /backups/db.sql.gz)" -gt 1000000 && \
  curl -fsS -m 10 https://epulz.io/heartbeat/abc123xyz > /dev/null

Jetzt trifft der Heartbeat nur ein, wenn das Skript erfolgreich war und die resultierende Datei über 1 MB groß ist. Alles Kleinere und der Ping wird zurückgehalten, und Sie bekommen eine Benachrichtigung innerhalb der Grace-Periode statt drei Wochen später.

Beginnen Sie, Ihre Cron-Jobs zu überwachen

ePulz.io unterstützt Heartbeat-Checks mit konfigurierbarer Grace-Periode. 7 Tage kostenlos.

Monitoring starten →

Verwandt

Teilen: Link kopiert

ePulz.io kostenlos testen - 7 Tage, ohne Kreditkarte.

Konto erstellen