Retour au blog

Surveiller les tâches cron : le pattern heartbeat en pratique

· 6 min de lecture

La surveillance HTTP ne vous dira pas qu'un cron nocturne a planté. Le pattern heartbeat inverse le sens : le cron se signale à la surveillance, et s'il n'appelle pas à temps, vous recevez une alerte.

Surveiller les tâches cron : le pattern heartbeat en pratique

Le problème : les tâches en arrière-plan sans endpoint HTTP

Un backend typique a plusieurs chemins :

  • Les requêtes web (HTTP/HTTPS vers le serveur) - vous les surveillez avec un uptime check.
  • Les tâches cron (sauvegarde quotidienne, facturation mensuelle, synchronisation horaire) - elles n'ont pas d'endpoint HTTP, la surveillance externe ne peut pas les suivre.
  • Les workers (Celery, BullMQ, Sidekiq) traitant une file - également sans HTTP.

Quand un cron plante (une faute de frappe dans le crontab, un disque plein, une variable d'environnement (environment variable) manquante, ou une erreur d'import après une mise à jour de dépendance (dependency upgrade)), personne ne vous alerte. Vous l'apprenez le lundi matin, en remarquant qu'aucune facture n'est partie pendant le week-end.

Le pattern heartbeat : le cron pingue la surveillance

Le principe est inversé par rapport à la surveillance normale :

  1. Dans le service de surveillance, vous créez un moniteur heartbeat avec un intervalle attendu (par exemple « toutes les 60 minutes »).
  2. Vous obtenez une URL heartbeat unique : https://epulz.io/heartbeat/abc123xyz.
  3. Dans votre tâche cron, à la fin d'une exécution réussie, vous appelez cette URL (HTTP GET ou POST).
  4. Si le ping n'arrive pas dans le temps attendu (plus une période de grâce), la surveillance vous alerte.

Exemple pratique : heartbeat depuis une tâche cron

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

L'opérateur && est essentiel : le heartbeat n'est envoyé que si backup.sh se termine avec le code de sortie 0. Si le script échoue, le ping n'arrive pas et vous recevez une alerte dans l'heure.

Astuce : Pour une couverture plus complète, ajoutez aussi 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

La surveillance peut alors distinguer « démarré mais non terminé » (le script s'est figé) de « jamais démarré » (la tâche cron ne s'est pas lancée).

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:
    # Le heartbeat n'est pas envoyé - la surveillance vous alerte
    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);
});

Période de grâce : combien de temps accorder avant l'alerte

Un moniteur heartbeat a besoin d'une certaine tolérance. Un cron tourne parfois plus longtemps que d'habitude, le réseau a de la latence et la synchronisation NTP peut être légèrement décalée. La période de grâce est le temps après l'écoulement de l'intervalle attendu pendant lequel la surveillance attend encore avant de déclencher une alerte.

Valeurs pratiques :

  • Cron horaire : intervalle 60 min + grâce 10 min
  • Sauvegarde quotidienne (20 min en moyenne) : intervalle 1440 min + grâce 60 min
  • Rapport hebdomadaire : intervalle 10080 min + grâce 360 min (6 h)

Une grâce trop serrée = de fausses alertes (faux positifs). Trop large = une alerte tardive au moment précis où la tâche plante réellement.

Où le pattern heartbeat aide le plus

  • Les sauvegardes de BDD nocturnes
  • La synchronisation avec des API externes (CRM, comptabilité, paiement)
  • Les calculs de rapports
  • Les tâches de cleanup (suppression d'anciennes sessions, logs, fichiers temporaires)
  • Le cycle de healthcheck des workers de longue durée
  • Les e-mails, newsletters et facturations planifiés

Conclusion

Les tâches en arrière-plan (background) sont souvent plus critiques que le site lui-même, mais restent un angle mort de la surveillance. Le pattern heartbeat ne demande que cinq minutes d'implémentation (il suffit d'ajouter curl à la fin de la ligne cron) et offre la même assurance que la surveillance uptime du frontend.

Rappel rapide : syntaxe cron

La plupart des erreurs de heartbeat sont en réalité des erreurs de cron - le ping ne se déclenche jamais parce que la planification était mauvaise. Les cinq champs sont :

┌── minute (0-59)
│ ┌── heure (0-23)
│ │ ┌── jour du mois (1-31)
│ │ │ ┌── mois (1-12)
│ │ │ │ ┌── jour de la semaine (0-7, 0 et 7 = dimanche)
│ │ │ │ │
* * * * *  commande
Expression S'exécute
*/5 * * * * toutes les 5 minutes
0 * * * * au début de chaque heure
0 3 * * * chaque jour à 03:00
0 3 * * 1 chaque lundi à 03:00
30 2 1 * * à 02:30 le premier jour du mois

Le piège classique : */5 dans le champ heure ne signifie pas « toutes les 5 heures à partir de maintenant » mais les heures divisibles par cinq (0, 5, 10, 15, 20). Si l'intervalle attendu du moniteur et la planification cron réelle ne correspondent pas, vous recevez des alertes fantômes.

Le principe du « dead man's switch »

Le pattern heartbeat est un dead man's switch (homme mort) : l'alarme, c'est le silence. Cela inverse le mode de défaillance habituel. La surveillance normale peut échouer en silence - si le serveur de surveillance lui-même tombe, il cesse simplement d'envoyer des alertes et vous ne le saurez jamais. Le heartbeat est l'inverse : le déclencheur d'une alerte est l'absence d'un signal attendu, donc une tâche qui s'arrête complètement (tout le serveur est éteint, le démon cron est désactivé, la machine a été mise hors service et oubliée) est exactement le cas qu'il détecte le mieux.

Au-delà de cron : autres planificateurs

Le pattern est le même quel que soit ce qui déclenche la tâche : - timers systemd - ajoutez curl à la chaîne ExecStart ou à ExecStartPost=. Associez le timer OnCalendar= à un intervalle de heartbeat correspondant. - Kubernetes CronJob - la dernière étape du conteneur pingue l'URL heartbeat. Si le pod plante ou n'est jamais planifié (mauvais node selector, échec d'image pull), le ping manque et vous recevez une alerte. - Windows Task Scheduler - terminez la tâche avec la commande PowerShell Invoke-WebRequest -Uri $env:HEARTBEAT_URL -TimeoutSec 10. - CI / pipelines planifiés - un GitHub Actions nocturne ou un schedule GitLab peut, en dernière étape, pinguer le heartbeat pour confirmer que le pipeline s'est exécuté et a réussi.

Anti-patterns courants

  • Pinguer au début, pas à la fin. Un heartbeat envoyé avant le travail prouve seulement que la tâche a démarré. Placez-le après le travail, conditionné au succès (&&), pour qu'un crash en cours d'exécution retienne aussi le ping.
  • Ignorer le code de sortie. backup.sh; curl ... (point-virgule) envoie le ping même si la sauvegarde a échoué. Utilisez toujours &&.
  • Avaler silencieusement les échecs de curl. -fsS fait échouer curl bruyamment sur une erreur HTTP, de sorte que les logs de votre tâche montrent que le heartbeat n'a pas pu être livré (un problème réseau sur la machine).
  • Un heartbeat pour dix tâches. Si cinq crons partagent une URL, vous ne savez pas lequel s'est arrêté. Donnez à chaque tâche planifiée son propre heartbeat. Si vous exposez aussi un endpoint HTTP pour le même service, associez le heartbeat à un uptime check classique et à une vérification de port sur le port du worker, afin de couvrir à la fois « le processus tourne » et « l'exécution planifiée a eu lieu ».

Scénario concret : l'échec silencieux de la sauvegarde

Le cas d'école pour un heartbeat ressemble à ceci. Un pg_dump nocturne écrit sur un volume monté. Cela fonctionne pendant des mois. Puis le volume se remplit, pg_dump se termine avec un code non nul, et comme la ligne cron utilisait un simple point-virgule, le heartbeat est quand même envoyé et le tableau de bord reste vert. Trois semaines plus tard, la base de données est corrompue et il n'y a aucune sauvegarde utilisable, car chaque sauvegarde « réussie » depuis que le disque est plein était un fichier de zéro octet. Le pattern heartbeat détecte cela dès la première nuit, mais seulement s'il est câblé correctement : conditionnez le ping avec && pour qu'une sortie non nulle le retienne, et envisagez de vérifier la sortie, pas seulement le code de sortie. Un script de sauvegarde peut se terminer avec le code 0 tout en produisant un fichier tronqué. Une ligne plus défensive vérifie que le dump n'est pas trivial avant de signaler le succès :

/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

Maintenant, le heartbeat n'arrive que si le script a réussi et que le fichier résultant fait plus de 1 Mo. Tout ce qui est plus petit retient le ping, et vous recevez une alerte dans la période de grâce au lieu de trois semaines plus tard.

Commencez à surveiller vos tâches cron

ePulz.io prend en charge les heartbeat checks avec une période de grâce configurable. 7 jours gratuits.

Démarrer la surveillance →

Associés

Partager: Lien copié

Essayez ePulz.io gratuitement - 7 jours sans carte de crédit.

Créer un compte