Monitorare i job cron: il pattern heartbeat nella pratica
· 6 min di lettura
Il monitoraggio HTTP non ti dirà che un cron notturno è andato in crash. Il pattern heartbeat inverte la direzione: il cron si segnala al monitoraggio e, se non chiama in tempo, ricevi un avviso.
Il problema: i job in background senza endpoint HTTP
Un backend tipico ha diversi percorsi:
- Richieste web (HTTP/HTTPS verso il server) - le monitori con un uptime check.
- Job cron (backup giornaliero, fatturazione mensile, sincronizzazione oraria) - non hanno un endpoint HTTP, il monitoraggio esterno non può seguirli.
- Worker (Celery, BullMQ, Sidekiq) che elaborano una coda - anch'essi senza HTTP.
Quando un cron va in crash (un refuso nel crontab, un disco pieno, una variabile d'ambiente (environment variable) mancante o un errore di import dopo l'aggiornamento di una dipendenza (dependency upgrade)), nessuno ti avvisa. Lo scopri lunedì mattina, quando noti che nel fine settimana non è partita nessuna fattura.
Il pattern heartbeat: il cron fa ping al monitoraggio
Il principio è invertito rispetto al monitoraggio normale:
- Nel servizio di monitoraggio crei un monitor heartbeat con un intervallo atteso (ad esempio "ogni 60 minuti").
- Ottieni una URL heartbeat univoca:
https://epulz.io/heartbeat/abc123xyz. - Nel tuo job cron, alla fine di un'esecuzione riuscita, chiami quella URL (HTTP GET o POST).
- Se il ping non arriva entro il tempo atteso (più un periodo di tolleranza), il monitoraggio ti avvisa.
Esempio pratico: heartbeat da un job cron
# /etc/crontab
0 3 * * * www-data /usr/local/bin/backup.sh && curl -fsS -m 10 \
https://epulz.io/heartbeat/abc123xyz > /dev/null
La chiave è l'operatore &&: l'heartbeat viene inviato solo quando backup.sh termina con exit code 0. Se lo script fallisce, il ping non arriva e nel giro di un'ora ricevi un avviso.
Consiglio: Per una copertura più accurata, aggiungi anche un heartbeat "start":
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
Il monitoraggio può così distinguere "iniziato ma non terminato" (lo script si è bloccato) da "mai iniziato" (il job cron non è partito).
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:
# L'heartbeat non viene inviato - il monitoraggio ti avvisa
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);
});
Periodo di tolleranza: quanto tempo concedere prima dell'avviso
Un monitor heartbeat ha bisogno di una certa tolleranza. Il cron a volte gira più a lungo del solito, la rete ha latenza e la sincronizzazione NTP può essere leggermente sfasata. Il periodo di tolleranza (grace period) è il tempo, dopo lo scadere dell'intervallo atteso, durante il quale il monitoraggio attende ancora prima di far scattare un avviso.
Valori pratici:
- Cron orario: intervallo 60 min + tolleranza 10 min
- Backup giornaliero (in media 20 min): intervallo 1440 min + tolleranza 60 min
- Report settimanale: intervallo 10080 min + tolleranza 360 min (6 h)
Una tolleranza troppo stretta = falsi avvisi (falsi positivi). Troppo larga = un avviso ritardato proprio nel momento in cui il job va davvero in crash.
Dove il pattern heartbeat aiuta di più
- Backup notturni del DB
- Sincronizzazione con API esterne (CRM, contabilità, pagamenti)
- Calcoli di report
- Job di cleanup (eliminazione di vecchie sessioni, log, file temporanei)
- Il ciclo di healthcheck di worker a lunga esecuzione
- E-mail, newsletter e fatturazione pianificate
Conclusione
I job in background sono spesso più critici del sito stesso, eppure restano un punto cieco del monitoraggio. Il pattern heartbeat richiede solo cinque minuti di implementazione (basta aggiungere curl alla fine della riga cron) e offre la stessa sicurezza del monitoraggio uptime del frontend.
Promemoria rapido: sintassi cron
La maggior parte degli errori di heartbeat sono in realtà errori di cron - il ping non scatta mai perché la pianificazione era sbagliata. I cinque campi sono:
┌── minuto (0-59)
│ ┌── ora (0-23)
│ │ ┌── giorno del mese (1-31)
│ │ │ ┌── mese (1-12)
│ │ │ │ ┌── giorno della settimana (0-7, 0 e 7 = domenica)
│ │ │ │ │
* * * * * comando
| Espressione | Esegue |
|---|---|
*/5 * * * * |
ogni 5 minuti |
0 * * * * |
all'inizio di ogni ora |
0 3 * * * |
ogni giorno alle 03:00 |
0 3 * * 1 |
ogni lunedì alle 03:00 |
30 2 1 * * |
alle 02:30 del primo giorno del mese |
La trappola classica: */5 nel campo ora non significa "ogni 5 ore da ora", ma le ore divisibili per cinque (0, 5, 10, 15, 20). Se l'intervallo atteso del monitor e la pianificazione cron reale non coincidono, ricevi avvisi fantasma.
Il principio del "dead man's switch"
Il pattern heartbeat è un dead man's switch (interruttore dell'uomo morto): l'allarme è il silenzio. Questo inverte la modalità di guasto abituale. Il monitoraggio normale può fallire in silenzio - se cade lo stesso server di monitoraggio, smette semplicemente di inviare avvisi e tu non lo scopri mai. L'heartbeat è l'opposto: il trigger di un avviso è l'assenza di un segnale atteso, perciò un job che smette del tutto di girare (l'intero server è spento, il demone cron è disabilitato, la macchina è stata dismessa e dimenticata) è esattamente il caso che intercetta meglio.
Oltre cron: altri scheduler
Il pattern è lo stesso a prescindere da cosa avvia il job:
- timer systemd - aggiungi curl alla catena ExecStart o a ExecStartPost=. Abbina il timer OnCalendar= a un intervallo di heartbeat corrispondente.
- Kubernetes CronJob - l'ultimo passo del container fa ping alla URL heartbeat. Se il pod va in crash o non viene mai pianificato (node selector errato, fallimento dell'image pull), il ping manca e ricevi un avviso.
- Windows Task Scheduler - termina l'attività con il comando PowerShell Invoke-WebRequest -Uri $env:HEARTBEAT_URL -TimeoutSec 10.
- CI / pipeline pianificate - un GitHub Actions notturno o uno schedule GitLab può, come ultimo passo, fare ping all'heartbeat per confermare che la pipeline è stata eseguita ed è passata.
Anti-pattern comuni
- Ping all'inizio, non alla fine. Un heartbeat inviato prima del lavoro prova solo che il job è iniziato. Mettilo dopo il lavoro, condizionato al successo (
&&), così che anche un crash a metà esecuzione trattenga il ping. - Ignorare l'exit code.
backup.sh; curl ...(punto e virgola) invia il ping anche se il backup è fallito. Usa sempre&&. - Inghiottire in silenzio i fallimenti di curl.
-fsSfa fallire curl in modo rumoroso su un errore HTTP, così i log del tuo job mostrano che l'heartbeat non è stato consegnato (un problema di rete sulla macchina). - Un heartbeat per dieci job. Se cinque cron condividono una URL, non sai quale ha smesso di girare. Dai a ogni job pianificato il proprio heartbeat. Se per lo stesso servizio esponi anche un endpoint HTTP, abbina l'heartbeat a un normale uptime check e a un controllo di porta sulla porta del worker, per coprire sia "il processo è in esecuzione" sia "l'esecuzione pianificata è avvenuta".
Scenario concreto: il fallimento silenzioso del backup
Il caso da manuale per un heartbeat è questo. Un pg_dump notturno scrive su un volume montato. Funziona per mesi. Poi il volume si riempie, pg_dump esce con un codice diverso da zero e, poiché la riga cron usava un semplice punto e virgola, l'heartbeat viene comunque inviato e la dashboard resta verde. Tre settimane dopo il database è corrotto e non c'è alcun backup utilizzabile, perché ogni backup "riuscito" da quando il disco si è riempito era un file di zero byte.
Il pattern heartbeat intercetta tutto questo già la prima notte, ma solo se è collegato correttamente: condiziona il ping con && perché un'uscita diversa da zero lo trattenga, e valuta di controllare l'output, non solo l'exit code. Uno script di backup può uscire con codice 0 e produrre comunque un file troncato. Una riga più difensiva verifica che il dump non sia banale prima di segnalare il successo:
/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
Ora l'heartbeat arriva solo se lo script ha avuto successo e il file risultante supera 1 MB. Qualsiasi cosa più piccola e il ping viene trattenuto, e ricevi un avviso entro il periodo di tolleranza invece che tre settimane dopo.
Inizia a monitorare i tuoi job cron
ePulz.io supporta gli heartbeat check con un periodo di tolleranza configurabile. 7 giorni gratis.
Correlati
Prova ePulz.io gratis - 7 giorni senza carta di credito.
Crea account