Heartbeat-мониторинг: настройка cron-мониторинга через ping

8 мин чтения
Обновлено 1 мая 2026

Heartbeat-мониторинг — это reverse-мониторинг (или dead-man's-switch): ваша cron-задача или фоновый процесс сама отправляет ping на наш сервер после успешного выполнения, и если ping не пришёл вовремя — Tracker.ru присылает алерт. Это противоположность обычному URL-мониторингу: там Tracker.ru опрашивает ваш сайт по pull-модели; здесь — push-модель, ваш сервис уведомляет нас сам.

Главное отличие от URL-мониторинга: heartbeat работает для задач, у которых нет публичного эндпоинта. Бэкап в три часа ночи, выгрузка в S3, ETL-пайплайн, очередь обработки — снаружи их не «опросить». Heartbeat закрывает этот класс задач.

Создание монитора

Откройте /my/heartbeat в личном кабинете, нажмите «Создать монитор». Заполните:

  • Имя — для удобства поиска в списке (например, nightly-backup-prod).
  • Период (period) — как часто ваша задача должна слать ping. Минимум зависит от тарифа: Free — 5 минут (300 секунд), Basic — 1 минута (60 секунд), Pro — 1 минута (60 секунд). Реалистичные значения для cron — от 5 минут до 24 часов.
  • Grace — дополнительное время сверх period, в течение которого пропуск ping ещё не считается алертом. Полезно при нестабильной сети или джобе с переменной длительностью.
  • Alert after misses — сколько подряд пропусков должно случиться, прежде чем монитор переключится в down и пришлёт уведомление. 1 — алерт после первого же пропуска (агрессивно), 2–3 — защита от ложных срабатываний.
  • Каналы уведомлений — Telegram, MAX, Email, Webhook. Подтягиваются из общих настроек профиля.

После сохранения Tracker.ru сгенерирует уникальный 32-символьный hex-токен и покажет URL для ping.

Endpoint и формат запроса

Endpoint: GET https://hb.tracker.ru/ping/{token} (или POST для CI/CD-сценариев с body). Token — 32 символа hex (^[0-9a-f]{32}$).

# GET — самый простой вариант
curl -fsSL https://hb.tracker.ru/ping/<token>

# POST — для сценариев с body (опциональное message в form)
curl -fsSL -X POST -d 'message=backup-2026-05-01.sql.gz' \
    https://hb.tracker.ru/ping/<token>

Опциональное сообщение ?msg=. К ping можно прицепить короткое текстовое сообщение через query-параметр ?msg=... (для GET) или message=... в POST-form/body. Лимит — 64 символа (это руны Unicode, а не байты — для кириллицы это до 128 байт UTF-8). Сообщение попадает в last_ping_message монитора и видно в /my/heartbeat/{id}. Удобно класть туда имя бэкап-файла, hostname сервера, exit-code или длительность джоба.

# С сообщением — backup-имя файла
curl -fsSL "https://hb.tracker.ru/ping/<token>?msg=backup-$(date +%F).sql.gz"

Sanitization. Tracker.ru автоматически удаляет из сообщения управляющие символы, null bytes и unicode overrides — это защита от инъекций в Telegram-уведомлениях. HTML escape применяется при выводе, поэтому <script> не сломает рендер.

Rate limit. 30 запросов в минуту на один токен. Если ваш скрипт зациклится и начнёт слать ping каждые 100 миллисекунд, лишние запросы будут возвращать 429 Too Many Requests — но монитор всё равно засчитает первый ping в окне.

Возможные ответы:

  • 200 OK{"ok":true}. Ping принят, монитор обновлён.
  • 401 Unauthorized — включён режим token_and_secret, но header X-Heartbeat-Secret не отправлен или неверный.
  • 404 Not Found — токен не найден (неверный или удалённый монитор).
  • 429 Too Many Requests — превышен rate limit.

Готовые интеграции

Bash + cron

Самый частый сценарий — ночной бэкап или периодический отчёт через crontab -e. Конструкция && curl шлёт ping только при успешном завершении основной команды (exit code 0):

0 3 * * * /usr/local/bin/backup.sh && curl -fsSL https://hb.tracker.ru/ping/<token>

Флаги curl: -f отключает HTTP-ошибки в stdout, -s глушит прогресс-бар, -S оставляет ошибки на stderr, -L следует редиректам. Если cron-демон шлёт stderr на почту, лишних писем от curl не будет.

С логированием результата ping в файл:

0 3 * * * /usr/local/bin/backup.sh && curl -fsSL https://hb.tracker.ru/ping/<token> >> /var/log/backup-ping.log 2>&1

Systemd timer

Для современных Linux-серверов systemd timer часто заменяет cron. Ping добавляется через ExecStartPost в .service-юнит:

# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
ExecStartPost=/usr/bin/curl -fsSL https://hb.tracker.ru/ping/<token>
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup nightly

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

ExecStartPost выполняется только если ExecStart вернул exit code 0 — при падении основной команды ping не уйдёт. Логи доступны через journalctl -u backup.service.

GitHub Actions

Для scheduled-workflow токен кладётся в secrets.TRACKER_PING_TOKEN, ping добавляется как последний step. Если предыдущие steps упали, по умолчанию следующие skip'ятся — ping не уйдёт, и Tracker.ru засчитает пропуск:

name: Nightly backup
on:
  schedule:
    - cron: '0 3 * * *'

jobs:
  backup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run backup
        run: ./scripts/backup.sh
      - name: Heartbeat ping
        run: curl -fsSL https://hb.tracker.ru/ping/${{ secrets.TRACKER_PING_TOKEN }}

Если важно слать ping строго при успехе предыдущего step — добавьте if: success() (это поведение по умолчанию). Не используйте if: always() — это сломает heartbeat (ping будет слаться даже при падении задачи).

Supervisord

Для долгоживущих процессов под supervisord используется eventlistener с подпиской на PROCESS_STATE_RUNNING или периодический at-таск. Простейший вариант — отдельная supervisor-программа с autorestart=false, которая шлёт ping раз в час и завершается:

[program:tracker-heartbeat]
command=/bin/sh -c 'while true; do curl -fsSL https://hb.tracker.ru/ping/<token>; sleep 3600; done'
autostart=true
autorestart=true
user=appuser
stderr_logfile=/var/log/supervisor/tracker-heartbeat.err.log

Это даёт «I'm alive» сигнал самого supervisord. Для конкретных воркеров (Sidekiq, Celery) лучше слать ping из самого джоба после успешной обработки задачи — так монитор привязан к функциональности, а не к процессу.

Поля и параметры монитора

Поле Описание
type reverse (cron шлёт ping), worker (фоновый воркер), agent (системный агент)
status new (только что создан), up (работает), down (пропустил ping), paused
period Ожидаемый интервал между ping в секундах. Минимум: Free 300, Basic/Pro 60.
grace Доп. время сверх period до признания пропуска. В секундах.
alert_after_misses Сколько подряд пропусков до алерта. 1 — агрессивно, 2–3 — защита от флапов.
notify_telegram bool — слать в Telegram
notify_email bool — слать на email
notify_max bool — слать в MAX-мессенджер
reminder_interval Интервал повторных напоминаний пока монитор в down. 0 — без напоминаний.
auth_mode token_only (по умолчанию) или token_and_secret (двухфакторка)
secret 32-символьный hex secret для token_and_secret. Хранится в БД, не показывается повторно.
last_ping_message Последнее сообщение из ?msg= (max 64 символа рун Unicode).

Двухфакторная защита (X-Heartbeat-Secret)

По умолчанию все мониторы создаются в режиме token_only — 32-символьного hex-токена достаточно для большинства сценариев. Перебор такого токена практически невозможен, и даже если третья сторона его узнает, единственное что она сможет — отправить лишний ping (что не вредит).

Двухфакторка нужна, если:

  • токен попадает в публичные репозитории (например, в README.md или примере конфига);
  • ping триггерит side-effect в вашей инфраструктуре, и вы хотите гарантированно подтвердить отправителя;
  • compliance-требования вашего проекта требуют второй фактор.

Включение. В настройках монитора переключите auth_mode на token_and_secret. Tracker.ru сгенерирует 32-символьный secret — сохраните его сразу, повторно показан не будет.

curl -fsSL \
  -H 'X-Heartbeat-Secret: <secret>' \
  https://hb.tracker.ru/ping/<token>

Сравнение secret на стороне Go-воркера — constant-time (subtle.ConstantTimeCompare), что защищает от тайминговых атак. При неверном или отсутствующем header endpoint вернёт 401 Unauthorized, ping не засчитается.

Опциональное сообщение ?msg=

Сообщение в ping видно в /my/heartbeat/{id} в поле «Последнее сообщение», и попадает в текст уведомления в Telegram/MAX/Email при следующем падении. Полезные сценарии:

  • Имя бэкап-файла: ?msg=backup-prod-2026-05-01.sql.gz — видно, что именно сохранилось.
  • Exit-code: ?msg=ok-exit-0 или ?msg=warn-exit-1 — статус последнего запуска.
  • Hostname: ?msg=backup-host-01 — какая нода прислала ping в multi-host setup.
  • Длительность: ?msg=took-42s — performance-метрика для отслеживания деградации.

Лимит — 64 символа рун Unicode (для кириллицы это до 128 байт UTF-8). Что не помещается, обрезается без ошибки.

Уведомления при пропуске

Когда за period + grace (с учётом alert_after_misses) ping не пришёл, монитор переключается в down и в выбранные каналы отправляется уведомление:

🚨Heartbeat пропустил ping: nightly-backup-prod
Период: 1 час, grace: 10 минут
Последний ping: 2026-05-01 02:15:09 (msg: backup-prod-2026-04-30.sql.gz)

При следующем успешном ping монитор переходит в up:

✅Heartbeat восстановлен: nightly-backup-prod
Был в down: 2 ч 15 мин

Если включён reminder_interval, повторные напоминания о пропуске отправляются с указанным интервалом пока монитор в down.

Лимиты и тарификация

  • Бесплатно на всех тарифах. Heartbeat — часть подписки, отдельной платы нет.
  • Количество мониторов: Free — 1, Basic — 5, Pro — 20. Это лимит одновременно активных мониторов.
  • Минимальный period: Free — 5 минут (300 секунд), Basic — 1 минута (60 секунд), Pro — 1 минута (60 секунд). Источник: тарифная матрица в БД.
  • Rate limit: 30 запросов в минуту на один токен. Защита от лупа в скрипте.
  • Max длина ?msg=: 64 символа (рун Unicode, не байт).

Troubleshooting

Ping вернул 404 Not Found. Токен неверный или монитор удалён. Проверьте URL в /my/heartbeat/{id} — он показывается в карточке монитора.

Ping вернул 401 Unauthorized. Включён режим token_and_secret, но header X-Heartbeat-Secret не отправлен или неверный. Проверьте: header передаётся именно в HTTP-запросе (не в query-параметре), значение совпадает с тем, что показано при создании secret. Если потеряли secret — пересоздайте через настройки монитора.

Ping вернул 429 Too Many Requests. Превышен лимит 30 req/min/token. Скорее всего ваш скрипт зациклился и шлёт ping в хот-лупе. Первый ping в окне всё равно засчитан, монитор обновлён — но архитектурно проблему стоит починить.

Монитор всё ещё down после успешного ping. Возможно, фоновое обновление кеша. Подождите 1 минуту и обновите страницу /my/heartbeat/{id}. Если статус не сменился — посмотрите журнал ping-событий: возможно, ping вернул не 200, а 429 (rate limit) или 401 (secret mismatch), и реальное обновление не произошло.

Алерт пришёл, хотя задача отработала. Чаще всего — period слишком короткий относительно реальной длительности джоба. Увеличьте grace или поставьте alert_after_misses=2 для защиты от единичного пропуска.

Связанные