cron и таймеры systemd
| cron | Unix Version 7 (1979); Vixie cron (1987); сейчас cronie и др. |
|---|---|
| Формат crontab | минута, час, день месяца, месяц, день недели, команда |
| Таймеры systemd | юниты .timer + .service |
| Расписание в systemd | OnCalendar, OnBootSec, OnUnitActiveSec |
| Пропущенные запуски | anacron, Persistent=true |
| В Kubernetes | CronJob |
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.
Советы
- Логируйте результат и время выполнения каждой задачи, а об ошибках — оповещайте (см. Мониторинг и алертинг): тихо сломавшийся ночной бэкап обнаруживается только тогда, когда он нужен.
- Делайте задачи идемпотентными: повторный запуск не должен ничего портить.
- Учитывайте часовой пояс сервера и переходы на летнее время там, где они есть.