Новинка Виртуальный VDS сервер в Финляндии от 390р
Выберите продукт

systemd‑таймеры на практике: OnCalendar, RandomizedDelaySec и Persistent

Пособие для админов и девопсов по systemd‑таймерам: как заменить cron, грамотно задать OnCalendar, добавить джиттер через RandomizedDelaySec и не терять окна с Persistent. Разберём форматы, точность, отладку и безопасные unit‑шаблоны.
systemd‑таймеры на практике: OnCalendar, RandomizedDelaySec и Persistent

Если вы уже используете systemd для сервисов, логично поручить ему и расписание задач. Таймеры systemd позволяют точнее, чем cron, управлять запуском, видеть состояние и логи в одном месте, добавлять джиттер для разгрузки пиков и «догонять» пропущенные запуски после простоя. В этой статье разберём три ключевых рычага: OnCalendar для календарных расписаний, RandomizedDelaySec для случайной задержки и Persistent для гарантии выполнения после офлайна.

Зачем переходить с cron на systemd timers

  • Единая модель: сервис оформлен как unit, таймер — как unit; единое управление через systemctl, логи — в journald.
  • Точность и контроль: AccuracySec задаёт окно активации; systemctl list-timers показывает ближайшие запуски.
  • Надёжность: Persistent=true запускает задачу при старте, если она пропущена в офлайне.
  • Масштабируемость: RandomizedDelaySec рассеивает одновременные старты на множестве хостов.

На некоторых платформах общего хостинга systemd‑таймеры могут быть недоступны. Для полного контроля над окружением и планировщиком используйте сервер на VDS.

Анатомия: пара .service + .timer

Таймер не делает работу сам — он активирует соответствующий сервис. Принято, чтобы имена совпадали: backup.service и backup.timer. Сервис обычно Type=oneshot, таймер задаёт расписание и параметры.

Пример .service

[Unit]
Description=Nightly backup to object storage
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
User=backup
Group=backup
# Создать каталоги с правильными правами при старте
StateDirectory=backups
WorkingDirectory=/var/lib/backups
# Блокировка от конкурентных запусков
ExecStart=/usr/bin/flock -n /run/backup.lock /usr/local/bin/backup.sh
# Если бэкап длинный — снимите ограничение времени или увеличьте
TimeoutStartSec=0
Nice=10
IOSchedulingClass=best-effort

[Install]
WantedBy=multi-user.target

Пример .timer с OnCalendar, RandomizedDelaySec и Persistent

[Unit]
Description=Nightly backup timer (03:15 local time)

[Timer]  03:15
Persistent=true
RandomizedDelaySec=10m
AccuracySec=1m
Unit=backup.service

[Install]
WantedBy=timers.target

Комментарии к параметрам:

  • OnCalendar — календарное расписание. Здесь ежедневно в 03:15 по локальному времени.
  • Persistent=true — если хост был выключен в 03:15, задача стартует при включении.
  • RandomizedDelaySec=10m — добавит случайную задержку до 10 минут к моменту срабатывания.
  • AccuracySec=1m — допускает до минуты дрейфа планировщика, снижая тики и экономя ресурсы.
FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды Финляндия

OnCalendar: практические форматы

Календарные выражения в systemd гораздо гибче crontab и читаются как «человеческие» строки. Несколько рабочих паттернов:

  • Каждый день в 03:15: OnCalendar=03:15 или OnCalendar=*-*-* 03:15.
  • По будням в 09:00: OnCalendar=Mon..Fri 09:00.
  • Каждые 15 минут: OnCalendar=*:0/15 или OnCalendar=*-*-* *:0/15:00.
  • Первое число месяца в 04:00: OnCalendar=*-*-01 04:00.
  • Первые понедельники месяца в 04:00: OnCalendar=Mon *-*-1..7 04:00.
  • Ночное окно каждые 5 минут с 01:00 до 03:00: OnCalendar=*-*-* 01..03:0/5.
  • Жёстко в UTC: OnCalendar=Mon..Fri 09:00 UTC.
Совет: проверяйте выражения перед боем. Команда systemd‑analyze calendar "строка" покажет ближайшие срабатывания и быстро выявит ошибки.

Пара нюансов:

  • Локальная зона: без явного UTC расписание считается в локальном часовом поясе хоста.
  • Переходы на летнее/зимнее время: одно срабатывание может сдвинуться или повториться при смене TZ. Для строгой предсказуемости используйте UTC.
  • Леп‑секунды таймер не моделирует — это нормально для большинства задач.

RandomizedDelaySec: зачем джиттер

Если у вас парк серверов, одновременный старт тяжёлых задач в 03:00 создаёт пики нагрузки на CPU, диск и сеть. RandomizedDelaySec добавляет к каждому срабатыванию непредсказуемую задержку в диапазоне от нуля до указанного значения. Рекомендации:

  • Бэкапы, ротация, метрики — 5–20 минут в зависимости от плотности парка.
  • Короткие задания раз в 5–15 минут — 30–120 секунд.
  • Комбинируйте с AccuracySec, чтобы диспетчер не пытался «подогнать секунды».
Важно: делайте задания идемпотентными — повторный запуск не должен ломать данные, даже если окна пересеклись.

Вывод systemd list-timers в терминале: ближайшие и последние срабатывания

Persistent: догоняем пропуски после офлайна

Persistent=true делает таймер устойчивым к простоям. Поведение простое: при загрузке systemd проверяет штамп последнего выполнения и расписание; если за время офлайна были срабатывания, задача запустится сразу после старта (с учётом RandomizedDelaySec и AccuracySec).

Детали, о которых стоит знать:

  • Штампы лежат в каталоге /var/lib/systemd/timers в файлах вида stamp-name.timer.
  • Если пропущено несколько окон, обычно выполняется один «догоняющий» запуск. Если вам нужен реплей «по каждому интервалу», заложите это в логику задания.
  • Работает и для монотонных таймеров (OnBootSec, OnUnitActiveSec): при длительном офлайне после старта будет одна активация.

Точность: AccuracySec и друзья

AccuracySec задаёт максимально допустимое окно неточности активации. Чем оно меньше — тем чаще таймер просыпается и тем выше накладные расходы. Типовые профили:

  • Релизные окна, бэкапы ночью — AccuracySec=1m..5m.
  • Оперативные задачи раз в 1–5 минут — AccuracySec=10s..30s.
  • Критично точный запуск «в секунду» — AccuracySec=1s, но это редкий кейс.

Практические рецепты

1) Ночной бэкап с джиттером и догонкой

[Timer] 
Persistent=true
RandomizedDelaySec=10m
AccuracySec=1m
Unit=backup.service

Такое расписание не сорвётся, если окно выпало на простой, и не создаст пики при множестве серверов. Для детального разбора хранилищ и инструментов посмотрите материал про S3‑бэкапы restic/borg: практика резервного копирования в S3 с restic и borg.

2) Каждые 15 минут, но не синхронно на всех хостах

[Timer] 
RandomizedDelaySec=90s
AccuracySec=15s
Unit=housekeeping.service

Подходит для задач вида «собрать метрики, обновить кеши».

3) Будние дни в 09:00 строго по UTC

[Timer]  09:00 UTC
Persistent=true
RandomizedDelaySec=2m
AccuracySec=30s
Unit=reports.service

Актуально для межрегиональных команд и интеграций, где важна унификация времени.

4) Периодическое задание от старта сервиса

[Timer]  
RandomizedDelaySec=5m
Persistent=true
Unit=cleanup.service

Так запускают сервисы «каждый час после предыдущего выполнения», стартуя через 5 минут после загрузки. Для баз данных пригодится связка с бэкап‑утилитами, например наш разбор pgBackRest: резервное копирование PostgreSQL через pgBackRest.

Тестирование и эксплуатация

  • Загрузить новые юниты: systemctl daemon-reload.
  • Включить и запустить таймер: systemctl enable --now backup.timer.
  • Посмотреть расписание: systemctl list-timers или systemctl list-timers --all.
  • Проверить статус: systemctl status backup.timer и systemctl status backup.service.
  • Логи выполнения: journalctl -u backup.service -S today.
  • Проверить выражение: systemd-analyze calendar "Mon..Fri 09:00".
  • Тест сервиса в обход таймера: systemctl start --now backup.service.
Если таймер включён, но не срабатывает, проверьте системное время и таймзону, выражение OnCalendar и нет ли условий типа Condition* в юните сервиса.

Проверка unit-файлов и журналов systemd при настройке таймеров

Типичные ошибки и как их избежать

  • Включили сервис, а не таймер. В бою нужно enable для .timer, не для .service.
  • Отсутствуют полные пути в ExecStart. В среде systemd PATH может отличаться; используйте абсолютные пути.
  • Параллельные запуски. Помогает flock или логика блокировки в самом скрипте.
  • Неправильная зона времени. Для строгих графиков указывайте UTC в OnCalendar.
  • Слишком маленький AccuracySec без необходимости — повышает нагрузку на таймерный механизм.
  • Длинные джобы убиваются по таймауту. Установите TimeoutStartSec=0 или разумный лимит.
  • Запуск от root «по привычке». Лучше задать User/Group, а также использовать StateDirectory и RuntimeDirectory.
  • Сбои из‑за отсутствия сети при старте. Добавьте Wants=network-online.target и After=network-online.target, если нужна сеть.

Монотонные и календарные таймеры: когда что

Календарные таймеры (OnCalendar) удобны для «человеческих» графиков: конкретные часы, дни недели, месяцы. Монотонные (OnBootSec, OnUnitActiveSec, OnUnitInactiveSec) подходят для «каждые N времени после события». Несколько ориентиров:

  • Ночное окно в 03:00 — календарный.
  • Задание «каждый час после предыдущего запуска» — монотонный.
  • Нужно не терять задания при офлайне — добавьте Persistent=true к любому типу.

Наблюдаемость и обслуживание

  • Периодически проверяйте список таймеров: systemctl list-timers, смотрите поля NEXT и LAST.
  • Алармы на отсутствие последних запусков: можно парсить systemctl show -p LastTriggerUSecMonotonic name.timer в скрипте мониторинга.
  • Версионируйте unit‑файлы в репозитории и деплойте через конфиг‑менеджмент. Это упростит аудит изменений OnCalendar и параметров точности.

Короткая памятка

  • OnCalendar — читаемые расписания, проверяйте через systemd‑analyze.
  • RandomizedDelaySec — рассеивает пиковые старты, ставьте в минутах для ночных окон.
  • Persistent=true — гарантии после простоя, «догоняющий» запуск один раз.
  • AccuracySec — баланс точности и накладных расходов.
  • Делайте задания идемпотентными и защищайте от гонок через flock.
  • Тестируйте сервис отдельно от таймера, логи смотрите в journald.

Подводя итог: связка OnCalendar + RandomizedDelaySec + Persistent закрывает 90% повседневных задач планирования. Остальное — дисциплина в оформлении unit‑файлов, правильные права и наблюдаемость. Настройте один‑два эталонных шаблона таймеров и сервисов, и дальше просто меняйте расписание и команды — управление станет предсказуемым и прозрачным.

Поделиться статьей

Вам будет интересно

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...