Мониторинг таймеров systemd
Таймеры systemd — хорошая замена cron: каждый запуск попадает в журнал, пропущенные запуски можно догнать после перезагрузки, а задача не стартует дважды по ошибке. Но сообщать кому-то о неудачном запуске они не умеют. Упавший сервис ждёт в systemctl --failed, пока кто-нибудь не заглянет. Это исправляют два небольших дополнения: пинг после каждого успешного запуска и обработчик, который сообщает об ошибках.
Адрес пинга — в одном месте
Положите адрес проверки каждой задачи в небольшой файл окружения с именем её сервиса. Его читают и сам сервис, и обработчик ошибок ниже:
# /etc/heartcron/backup.service
PING_URL=https://ping.heartcron.com/<uuid>
Сервис
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly database backup
OnFailure=heartcron-fail@%n.service
[Service]
Type=oneshot
EnvironmentFile=/etc/heartcron/backup.service
ExecStartPre=-/usr/bin/curl -fsS -m 10 --retry 3 -o /dev/null ${PING_URL}/start
ExecStart=/usr/local/bin/backup.sh
ExecStartPost=/usr/bin/curl -fsS -m 10 --retry 3 -o /dev/null ${PING_URL}
ExecStartPreсообщает о старте. Знак-в начале говорит systemd игнорировать ошибку этой строки, так что недоступный сервис мониторинга никогда не помешает бэкапу.- Для сервиса
Type=oneshotстрокаExecStartPostвыполняется, только еслиExecStartзавершился успешно. Пинг об успехе не уйдёт после неудачного запуска. OnFailureзапускает другой юнит, когда этот падает.%nпередаёт ему полное имя юнита —backup.service.
Обработчик ошибок
Один юнит-шаблон обрабатывает ошибки всех задач. Он читает адрес из файла с именем упавшего юнита и отправляет /fail, так что алерт приходит сразу, а не после запаса времени:
# /etc/systemd/system/heartcron-fail@.service
[Unit]
Description=Report a failure of %i to Heartcron
[Service]
Type=oneshot
EnvironmentFile=/etc/heartcron/%i
ExecStart=/usr/bin/curl -fsS -m 10 --retry 3 -o /dev/null ${PING_URL}/fail
Таймер
# /etc/systemd/system/backup.timer
[Unit]
Description=Run the database backup every night
[Timer]
OnCalendar=*-*-* 03:17:00
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true запускает пропущенный бэкап сразу после загрузки, если в 03:17 машина была выключена. Включите таймер и проверьте, когда он сработает:
systemctl daemon-reload systemctl enable --now backup.timer systemctl list-timers backup.timer systemd-analyze calendar "*-*-* 03:17:00"
То же расписание в Heartcron
Heartcron использует cron-выражения, поэтому переведите OnCalendar:
| OnCalendar | Cron |
|---|---|
*-*-* 03:17:00 | 17 3 * * * |
Mon..Fri 09:00 | 0 9 * * 1-5 |
hourly | 0 * * * * |
*:0/15 | */15 * * * * |
monthly | 0 0 1 * * |
Таймеры работают в системном часовом поясе; укажите такой же в проверке. Сомневаетесь, что значит выражение? Вставьте его в расшифровщик cron-выражений.
Проверьте
systemctl start backup.service # ручной запуск должен отправить /start и пинг об успехе
journalctl -u backup.service -n 50
Потом один раз заставьте backup.sh завершиться с ошибкой: обработчик должен отправить алерт через несколько секунд.