cron и таймеры systemd

Материал из llmdocs — справочник разработчика
cron и таймеры systemd
cronUnix Version 7 (1979); Vixie cron (1987); сейчас cronie и др.
Формат crontabминута, час, день месяца, месяц, день недели, команда
Таймеры systemdюниты .timer + .service
Расписание в systemdOnCalendar, OnBootSec, OnUnitActiveSec
Пропущенные запускиanacron, Persistent=true
В KubernetesCronJob

cron — классический планировщик Unix, запускающий команды по расписанию: каждую минуту, каждую ночь в 3:00, по понедельникам. Расписание хранится в файлах crontab. Таймеры systemd — современная альтернатива в системах на systemd: расписание описывается юнитом .timer, который запускает парный юнит .service, с журналированием, зависимостями, ограничением ресурсов и догоняющим запуском пропущенных задач.

Типичные задачи по расписанию: резервные копии (Бэкапы: правило 3-2-1), ротация и очистка логов, обновление сертификатов (Let's Encrypt и ACME), выгрузки отчётов, синхронизация данных.

cron

cron появился в Unix Version 7 (1979). Широко распространённую реализацию написал Пол Викси в 1987 году; современные дистрибутивы используют её потомков — cronie и аналоги.[6] Демон crond каждую минуту проверяет таблицы и запускает подходящие задачи.

Формат crontab

# ┌───────── минута (0–59)
# │ ┌─────── час (0–23)
# │ │ ┌───── день месяца (1–31)
# │ │ │ ┌─── месяц (1–12)
# │ │ │ │ ┌─ день недели (0–7, 0 и 7 — воскресенье)
# │ │ │ │ │
  0 3 * * *   /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
*/5 * * * *   /usr/local/bin/healthcheck.sh
  0 9 * * 1-5 /usr/local/bin/report.sh
 30 2 1 * *   /usr/local/bin/monthly-cleanup.sh
@reboot       /usr/local/bin/on-boot.sh

* — любое значение, */5 — каждые 5, 1-5 — диапазон, 1,15 — список. Сокращения @hourly, @daily, @weekly, @reboot заменяют частые варианты.[1] Проверить выражение удобно на crontab.guru.[4]

crontab -e          # редактировать таблицу текущего пользователя
crontab -l          # показать
sudo ls /etc/cron.d /etc/cron.daily   # системные задачи

Подводные камни cron

  • Минимальное окружение. Задача запускается не из вашей интерактивной оболочки: PATH урезан, переменные из ~/.bashrc не загружены. Используйте абсолютные пути или задайте PATH= в начале crontab.
  • Вывод теряется или уходит письмом на локальную почту. Перенаправляйте его в файл или журнал явно.
  • Символ % в crontab означает перевод строки — в командах вроде date +%F его нужно экранировать: \%.
  • Наложение запусков. Если задача длится дольше интервала, запуски накладываются; защищаются блокировкой — flock -n /tmp/job.lock команда.
  • Выключенная машина. Задача, время которой пришлось на выключенный компьютер, не выполнится; для этого существует anacron.

Таймеры systemd

Задача описывается двумя юнитами:

# /etc/systemd/system/backup.service
[Unit]
Description=Ночная резервная копия

[Service]
Type=oneshot
User=backup
ExecStart=/usr/local/bin/backup.sh
Nice=10
IOSchedulingClass=idle
# /etc/systemd/system/backup.timer
[Unit]
Description=Запуск backup.service каждую ночь

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true              # выполнить пропущенный запуск после включения
RandomizedDelaySec=15min     # случайная задержка, чтобы не нагружать всё сразу

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers                       # все таймеры и время следующего запуска
journalctl -u backup.service                # вывод всех запусков
systemd-analyze calendar "Mon..Fri 09:00"   # проверить выражение расписания
sudo systemctl start backup.service         # запустить вручную, вне расписания

Выражения OnCalendar читаются проще cron: daily, weekly, Mon..Fri 09:00, *-*-01 02:30:00 (первого числа каждого месяца).[3] Помимо календаря есть относительные таймеры: OnBootSec=5min (через 5 минут после загрузки), OnUnitActiveSec=1h (через час после предыдущего запуска).[2]

Сравнение

cron Таймеры systemd
Простота одна строка два файла
Логи нужно перенаправлять вручную автоматически в журнал (journald и логи)
Пропущенные запуски только с anacron Persistent=true
Наложение запусков нужен flock служба не запустится повторно, пока работает
Зависимости и ресурсы нет After=, Requires=, лимиты памяти и процессора, песочница
Ручной запуск скопировать команду systemctl start
Переносимость любые Unix-системы, контейнеры только системы на systemd

Для серверов на systemd таймеры обычно надёжнее, а cron остаётся удобным для быстрых пользовательских задач и систем без systemd.

Расписание в контейнерах и облаке

В Docker-контейнерах нет ни cron, ни systemd по умолчанию — задачи по расписанию запускают снаружи (таймером на хосте) или отдельным контейнером с планировщиком. В Kubernetes для этого есть объект CronJob с тем же синтаксисом расписания, что и у cron, и настройками параллелизма и истории запусков.[5] В CI-системах вроде GitHub Actions периодические задания тоже задаются выражениями cron.

Советы

  • Логируйте результат и время выполнения каждой задачи, а об ошибках — оповещайте (см. Мониторинг и алертинг): тихо сломавшийся ночной бэкап обнаруживается только тогда, когда он нужен.
  • Делайте задачи идемпотентными: повторный запуск не должен ничего портить.
  • Учитывайте часовой пояс сервера и переходы на летнее время там, где они есть.

См. также

Источники

  1. crontab(5) — Linux manual page
  2. systemd.timer — manual
  3. systemd.time — Calendar Events
  4. crontab.guru — редактор выражений cron
  5. Kubernetes — CronJob
  6. cronie — GitHub