systemd timers вместо cron
Я долго держался за cron: он в голове, он везде. Сломало меня то, что задание молча не отработало ночью, а узнал я об этом через неделю. В cron нет ни состояния, ни внятного «покажи последний запуск».
Шаблон
Две единицы. Сервис описывает, что делать:
# /etc/systemd/system/report.service
[Unit]
Description=Ночной отчёт
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=reports
ExecStart=/opt/reports/bin/build --since=yesterday
TimeoutStartSec=30min
Nice=10
IOSchedulingClass=idle
Таймер — когда:
# /etc/systemd/system/report.timer
[Unit]
Description=Запуск ночного отчёта
[Timer]
OnCalendar=*-*-* 03:20:00
RandomizedDelaySec=10m
Persistent=true
AccuracySec=1m
[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now report.timer
Ради чего всё это
Persistent=true— если машина спала или была выключена в 03:20, задание отработает сразу после загрузки. У cron такого нет вообще.RandomizedDelaySec— размазывает одновременные запуски по нескольким хостам. Иначе двадцать машин в 03:20 дружно идут в один и тот же API.- Логи сами попадают в journal, с кодом возврата:
journalctl -u report.service -n 50. systemctl list-timersпоказывает и следующий, и прошлый запуск — то, чего мне не хватало в cron больше всего.
Мелочи, на которые я наступил
Имена должны совпадать. report.timer по умолчанию запускает report.service. Если имена разные, нужен явный Unit= в секции [Timer].
Проверка выражения. Формат OnCalendar не совпадает с cron. Перед тем как включать, стоит прогнать:
systemd-analyze calendar 'Mon *-*-* 06:00:00' --iterations=3
Перекрытие запусков. systemd не запустит второй экземпляр oneshot-сервиса, пока работает первый — он просто пропустит срабатывание. Для cron это была отдельная возня с lock-файлами, здесь поведение по умолчанию правильное.
Часовой пояс. OnCalendar считает по локальному времени хоста. Если хосты в разных зонах, честнее сразу писать OnCalendar=*-*-* 03:20:00 UTC.