Powrót do bloga

Monitorowanie zadań cron: wzorzec heartbeat w praktyce

· 6 min czytania

Monitoring HTTP nie wykryje, że nocny cron padł. Wzorzec heartbeat odwraca kierunek: cron zgłasza się do monitoringu, a jeśli nie zadzwoni na czas, otrzymasz powiadomienie.

Monitorowanie zadań cron: wzorzec heartbeat w praktyce

Problem: zadania w tle bez endpointu HTTP

Typowy backend ma kilka ścieżek:

  • Żądania webowe (HTTP/HTTPS do serwera) - monitorujesz je za pomocą uptime checku.
  • Zadania cron (dzienny backup, miesięczne fakturowanie, godzinowa synchronizacja) - nie mają endpointu HTTP, zewnętrzny monitoring nie potrafi ich śledzić.
  • Workery (Celery, BullMQ, Sidekiq) przetwarzające kolejkę - również bez HTTP.

Gdy cron padnie (literówka w crontabie, pełny dysk, brakująca zmienna środowiskowa (environment variable) lub błąd importu po aktualizacji zależności (dependency upgrade)), nikt cię nie powiadomi. Dowiadujesz się o tym w poniedziałek rano, gdy zauważasz, że przez weekend nie wysłano żadnych faktur.

Wzorzec heartbeat: cron pinguje monitoring

Zasada jest odwrócona w stosunku do normalnego monitoringu:

  1. W usłudze monitoringu tworzysz monitor heartbeat z oczekiwanym interwałem (na przykład "co 60 minut").
  2. Otrzymujesz unikalny URL heartbeat: https://epulz.io/heartbeat/abc123xyz.
  3. W swoim zadaniu cron na końcu udanego przebiegu wywołujesz ten URL (HTTP GET lub POST).
  4. Jeśli ping nie dotrze w oczekiwanym czasie (plus okres karencji), monitoring cię powiadomi.

Praktyczny przykład: heartbeat z zadania cron

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

Kluczowy jest operator &&: heartbeat zostaje wysłany tylko wtedy, gdy backup.sh zakończy się z kodem wyjścia 0. Jeśli skrypt zawiedzie, ping nie dotrze i w ciągu godziny otrzymasz powiadomienie.

Wskazówka: Dla dokładniejszego pokrycia dodaj również 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

Monitoring rozróżni wtedy "zaczął i nie skończył" (skrypt zawiesił się) od "wcale nie zaczął" (zadanie cron się nie uruchomiło).

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:
    # Heartbeat nie zostanie wysłany - monitoring cię powiadomi
    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);
});

Okres karencji: ile czasu dać przed powiadomieniem

Monitor heartbeat potrzebuje pewnej tolerancji. Cron czasem działa dłużej niż zwykle, sieć ma opóźnienia, a synchronizacja NTP może być lekko przesunięta. Okres karencji to czas po upływie oczekiwanego interwału, podczas którego monitoring jeszcze czeka, zanim uruchomi powiadomienie.

Praktyczne wartości:

  • Cron godzinowy: interwał 60 min + karencja 10 min
  • Dzienny backup (średnio 20 min): interwał 1440 min + karencja 60 min
  • Raport tygodniowy: interwał 10080 min + karencja 360 min (6 h)

Zbyt ciasna karencja = fałszywe powiadomienia (false positive). Zbyt luźna = opóźnione powiadomienie w chwili, gdy zadanie faktycznie pada.

Gdzie wzorzec heartbeat pomaga najbardziej

  • Nocne backupy DB
  • Synchronizacja z zewnętrznymi API (CRM, accounting, payment)
  • Kalkulacje raportów
  • Zadania cleanup (usuwanie starych sesji, logów, plików tymczasowych)
  • Cykl healthcheck długo działających workerów
  • Zaplanowane e-maile, newslettery, fakturowanie

Podsumowanie

Zadania w tle (background) bywają często bardziej krytyczne niż sam serwis, ale pozostają martwym punktem monitoringu. Wzorzec heartbeat wymaga tylko pięciu minut na implementację (wystarczy dodać curl na końcu wiersza cron) i daje taką samą pewność jak monitoring uptime frontendu.

Szybkie przypomnienie: składnia crona

Większość błędów heartbeatu to w rzeczywistości błędy crona - ping nigdy się nie uruchamia, bo harmonogram był zły. Pięć pól to:

┌── minuta (0-59)
│ ┌── godzina (0-23)
│ │ ┌── dzień miesiąca (1-31)
│ │ │ ┌── miesiąc (1-12)
│ │ │ │ ┌── dzień tygodnia (0-7, 0 i 7 = niedziela)
│ │ │ │ │
* * * * *  polecenie
Wyrażenie Uruchamia się
*/5 * * * * co 5 minut
0 * * * * na początku każdej godziny
0 3 * * * codziennie o 03:00
0 3 * * 1 w każdy poniedziałek o 03:00
30 2 1 * * o 02:30 pierwszego dnia miesiąca

Klasyczna pułapka: */5 w polu godzina nie oznacza "co 5 godzin od teraz", ale godziny podzielne przez pięć (0, 5, 10, 15, 20). Jeśli oczekiwany interwał monitora i rzeczywisty harmonogram crona się nie zgadzają, otrzymujesz fantomowe powiadomienia.

Zasada "dead man's switch"

Wzorzec heartbeat to dead man's switch (czuwak): alarmem jest cisza. To odwraca zwykły tryb awarii. Normalny monitoring może zawieść po cichu - jeśli padnie sam serwer monitorujący, po prostu przestaje wysyłać powiadomienia i nigdy się o tym nie dowiesz. Heartbeat jest przeciwieństwem: wyzwalaczem powiadomienia jest brak oczekiwanego sygnału, więc zadanie, które przestaje działać całkowicie (cały serwer wyłączony, demon cron zablokowany, maszyna wycofana i zapomniana), to dokładnie ten przypadek, który wychwytuje najlepiej.

Poza cronem: inne harmonogramy

Wzorzec jest taki sam niezależnie od tego, co uruchamia zadanie: - timery systemd - dodaj curl do łańcucha ExecStart lub do ExecStartPost=. Sparuj timer OnCalendar= z odpowiadającym interwałem heartbeatu. - Kubernetes CronJob - ostatni krok kontenera pinguje URL heartbeat. Jeśli pod padnie lub w ogóle nie zostanie zaplanowany (zły node selector, błąd pobierania obrazu), ping zniknie i otrzymasz powiadomienie. - Windows Task Scheduler - zakończ zadanie poleceniem PowerShell Invoke-WebRequest -Uri $env:HEARTBEAT_URL -TimeoutSec 10. - CI / zaplanowane pipeline - nocny GitHub Actions lub harmonogram GitLab może jako ostatni krok pingnąć heartbeat i potwierdzić, że pipeline się uruchomił i przeszedł.

Częste anty-wzorce

  • Ping na początku, nie na końcu. Heartbeat wysłany przed pracą dowodzi tylko, że zadanie się zaczęło. Umieść go za pracą, warunkowo wobec sukcesu (&&), aby awaria w trakcie przebiegu również wstrzymała ping.
  • Ignorowanie kodu wyjścia. backup.sh; curl ... (średnik) wysyła ping nawet gdy backup zawiódł. Zawsze używaj &&.
  • Ciche połykanie błędów curl. -fsS sprawia, że curl przy błędzie HTTP zawodzi głośno, więc logi twojego zadania pokażą, że heartbeatu nie udało się dostarczyć (problem sieciowy na maszynie).
  • Jeden heartbeat na dziesięć zadań. Jeśli pięć cronów współdzieli jeden URL, nie wiesz, który przestał działać. Daj każdemu zaplanowanemu zadaniu własny heartbeat. Jeśli dla tej samej usługi wystawiasz też endpoint HTTP, sparuj heartbeat ze zwykłym uptime checkiem i sprawdzeniem portu na porcie workera, aby pokryć zarówno "proces działa", jak i "zaplanowany przebieg się odbył".

Konkretny scenariusz: cicha awaria backupu

Podręcznikowy przypadek dla heartbeatu wygląda tak. Nocny pg_dump zapisuje na zamontowany wolumen. Działa miesiącami. Potem wolumen się zapełnia, pg_dump kończy się z niezerowym kodem, a ponieważ wiersz cron użył zwykłego średnika, heartbeat i tak zostaje wysłany, a dashboard pozostaje zielony. Trzy tygodnie później baza danych jest uszkodzona i nie ma użytecznego backupu, bo każdy "udany" backup od momentu zapełnienia dysku był plikiem zerowym. Wzorzec heartbeat wychwytuje to już pierwszej nocy, ale tylko jeśli jest podłączony poprawnie: uwarunkuj ping przez &&, aby niezerowy exit go wstrzymał, i rozważ sprawdzenie wyniku, a nie tylko kodu wyjścia. Skrypt backupu może zakończyć się kodem 0 i mimo to wyprodukować ucięty plik. Bardziej defensywny wiersz weryfikuje, że dump nie jest trywialny, jeszcze przed zasygnalizowaniem sukcesu:

/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

Teraz heartbeat dociera tylko wtedy, gdy skrypt się powiódł oraz wynikowy plik ma ponad 1 MB. Cokolwiek mniejszego i ping zostaje wstrzymany, a ty otrzymujesz powiadomienie w okresie karencji zamiast trzy tygodnie później.

Zacznij monitorować zadania cron

ePulz.io obsługuje heartbeat checks z konfigurowalnym okresem karencji. 7 dni za darmo.

Uruchom monitoring →

Powiązane

Udostępnij: Skopiowano link

Wypróbuj ePulz.io za darmo - 7 dni bez karty kredytowej.

Utwórz konto