Планировщик запускает задачу и молча уходит. Если ночной бэкап упал на середине, узнают об этом обычно в тот момент, когда файлы понадобились. Классический cron умеет писать письмо при ненулевом коде возврата, но письма теряются в спаме, а полное отсутствие запуска он не отслеживает. Healthchecks решает эту проблему иначе: задача сама отмечается на сервере, а сервер поднимает тревогу, если отметки не было вовремя или пришёл код ошибки.

Модель работы: пинг вместо ожидания

Для каждой проверки создаётся уникальный идентификатор. Задача обязана отправить HTTP-запрос на адрес вида https://hc.example.com/ping/<uuid>. Успешный пинг продлевает ожидание, отсутствие пинга дольше периода плюс льготного интервала даёт статус «просрочено», а пинг с ненулевым кодом — статус «упало». Логика обратна обычному мониторингу: сервер не опрашивает вашу машину, поэтому достаточно исходящего HTTPS и не нужен открытый порт.

Расписание задаётся либо простым периодом в секундах, либо cron-выражением. Простое расписание не принимает значения меньше минуты, что отсекает случайные опечатки. Льготный интервал добавляет запас: задача на два часа не должна считаться упавшей через минуту после старта.

Свой экземпляр: минимальный compose

Сервис с открытым кодом можно развернуть рядом с бэкапами и не зависеть от внешнего адреса. Ниже конфигурация с PostgreSQL: SQLite подойдёт для десятка проверок, но при параллельных пингах лучше база посерьёзнее.

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: hc
      POSTGRES_USER: hc
      POSTGRES_PASSWORD: change-me
    volumes:
      - db:/var/lib/postgresql/data
  web:
    image: healthchecks/healthchecks
    env_file: .env
    ports:
      - "8000:8000"
    depends_on:
      - db
volumes:
  db:

В .env минимум таких значений: SECRET_KEY, DEBUG=0, ALLOWED_HOSTS=hc.example.com, SITE_ROOT=https://hc.example.com, DB=postgres, DB_HOST=db, DB_NAME=hc, DB_USER=hc, DB_PASSWORD. Для писем добавляют EMAIL_URL. Если образ не выполнил миграции сам, их запускают вручную.

docker compose up -d
docker compose run --rm web ./manage.py migrate
docker compose run --rm web ./manage.py createsuperuser
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8000/

Ответ 200 на главной означает, что приложение поднялось. Дальше обязателен HTTPS: пинги идут с секретным идентификатором в пути, и по открытому каналу его легко подсмотреть. Прокси с сертификатом ставится перед контейнером, а SITE_ROOT должен совпадать с внешним адресом, иначе ссылки в уведомлениях будут вести в никуда.

Обёртка для cron

Правильнее не разбрасывать curl по crontab, а сделать один скрипт: он отправляет сигнал старта, выполняет задачу, сохраняет вывод и передаёт реальный код возврата. Тогда в интерфейсе видно и время работы, и текст ошибки, а не просто факт падения.

#!/bin/sh
set -u
URL="$HC_PING"
OUT="$(mktemp)"
curl -fsS -m 10 --retry 3 "$URL/start" >/dev/null 2>&1
/usr/local/bin/backup.sh >"$OUT" 2>&1
rc=$?
curl -fsS -m 10 --retry 3 --data-binary @"$OUT" "$URL/$rc" >/dev/null 2>&1
rm -f "$OUT"
exit "$rc"

Обратите внимание на --retry 3 и -m 10: сеть может моргнуть, и терять из-за этого отметку об успешном бэкапе обидно. Код возврата передаётся в пути, поэтому 0 означает успех, любое другое число — сбой с сохранённым текстом. Тело запроса ограничено, поэтому болтливый вывод лучше обрезать через tail -c 10000.

В crontab нужны две детали: flock от параллельного запуска и явный PATH, потому что окружение cron почти пустое.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""
30 3 * * * flock -n /run/backup.lock /usr/local/bin/backup-ping.sh

flock -n не станет ждать: если предыдущий запуск ещё идёт, второй просто выйдет. Это лучше, чем два rsync одновременно.

Таймер systemd как альтернатива

Если cron заменён таймерами, обёртка та же, меняется только запуск. Юнит с OnCalendar удобнее читать, а RandomizedDelaySec разводит одновременные задачи.

[Unit]
Description=Nightly backup with health check

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-ping.sh
Nice=10
IOSchedulingClass=idle

[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
RandomizedDelaySec=300

[Install]
WantedBy=timers.target

Persistent=true догоняет пропущенный запуск после выключенной машины. Это создаёт нюанс: если сервер пролежал сутки, задача стартует сразу после загрузки, и проверка не должна успевать объявить тревогу раньше времени — спасает льготный интервал.

Проверка через API и разбор ложных тревог

Список проверок и их статусы доступны по API с ключом из настроек проекта. Это удобно для собственного дашборда и для быстрой проверки после переезда.

curl -fsS -H "X-Api-Key: $HC_KEY" https://hc.example.com/api/v3/checks/ | jq '.checks[] | {name, status, last_ping}'
curl -fsS -o /dev/null -w "%{http_code} %{time_total}\n" https://hc.example.com/ping/$HC_UUID
journalctl -u backup-ping.service -n 50 --no-pager

Ложные срабатывания почти всегда объясняются тремя причинами: задача идёт дольше периода, сеть до сервера проверок нестабильна, либо пинг отправляется после exit. Первое лечится увеличением периода и льготного интервала, второе — повторами в curl, третье — порядком строк в скрипте. Полезно раз в месяц смотреть на график длительности: постепенный рост предупреждает о распухшей базе или диске задолго до падения.

Что стоит отслеживать кроме бэкапа

  • Свежесть репликации и наличие свободного места на целевом хранилище.
  • Ротацию журналов: задача, которая не удалась, но и не упала, тоже проблема.
  • Обновление сертификатов и срок действия домена.
  • Синхронизацию с внешним диском, который кто-то мог отключить.
  • Проверку восстановления: тестовое развёртывание из копии раз в квартал.

Чек-лист внедрения

  • У каждой критичной задачи свой идентификатор, а не один общий на всё.
  • Период и льготный интервал соответствуют реальному времени работы.
  • Скрипт отправляет старт, вывод и настоящий код возврата.
  • Настроен хотя бы один независимый канал уведомлений.
  • Проверено, что при отключённой сети тревога приходит, а при восстановлении снимается.

Итог

Healthchecks закрывает слепую зону cron: он замечает и падение, и молчание. Внедрение сводится к трём шагам — создать проверку, обернуть задачу скриптом с пингом и настроить уведомления. Свой экземпляр в compose даёт независимость от чужого сервиса, но требует HTTPS и внимания к секретному идентификатору. Главное правило простое: пинг должен отправляться в любом исходе, иначе мониторинг будет рассказывать только о хороших днях.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте