Настройка Systemd Timers как замена Cron

Задачи по расписанию тихо пропускают запуск или падают без следа в журнале — и узнаёте вы об этом от заказчика. Мы настраиваем systemd timers под ключ: переносим расписания, добавляем журналирование и оповещения об отказах. Контроль исполнения сокращает время на разбор инцидентов.
Часто задаваемые вопросы

Настройка systemd timers: где ломается классический cron

Планировщик задач в Linux чаще всего сводится к строке в crontab: cron запускает команду по расписанию и на этом заканчивается. Он не знает, чем закончился прошлый запуск, что делать при сбое и был ли перезапуск машины в окно задачи. Поэтому вопрос «почему не запускаются задачи по расписанию» обычно начинается с ручного перебора системных журналов в поисках единственной упавшей строки.

Дальше — четыре типовых сбоя, которые всплывают на любом парке серверов. Ошибка уходит в никуда: без настроенного получателя вывод команды оседает в локальной почте root, куда месяцами никто не смотрит. Перезагрузка или недоступность узла в момент запуска означает, что задача просто не выполнится — cron не хранит состояние и не догоняет пропущенное. Между заданиями нет связей: выгрузка стартует раньше, чем завершилась подготовка данных, и оба процесса об этом не подозревают. Журналы разбросаны — часть в почте, часть в системных файлах, часть потеряна безвозвратно.

Настройка systemd timers снимает это на уровне архитектуры: таймер и полезная нагрузка описываются парой unit-файлов, а не одной строкой. Состояние запуска хранит сам systemd, результат предыдущего прогона виден по конкретному юниту, директива OnFailure= задаёт обработчик падения, а After= и Requires= выстраивают порядок между заданиями. Ключ Persistent=true возвращает пропущенный запуск сразу после включения машины, поэтому ночной перезапуск больше не «съедает» задачу.

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

Что меняется после переноса задач: измеримый эффект

В cron единственный признак сбоя — строка в почте root или запись в syslog без статуса выхода. systemd ведёт каждую задачу как отдельный юнит со своим состоянием, поэтому доля потерянных запусков уходит в ноль: Persistent=true догоняет пропущенное окно после включения машины, а не теряет его молча. Уже на этом шаге видно, что задача либо отработала с кодом 0, либо оставила след в журнале — третьего состояния нет.

Зависимости между заданиями перестают быть договорённостью «подождать две минуты» внутри скрипта. Связки After= и Requires= дают явный порядок запуска: выгрузка не стартует, пока не завершилась подготовка данных, а падение одного шага не позволяет следующему работать по неполным данным. Повторный запуск при сбое настраивается штатно — Restart=on-failure с паузой между попытками и OnFailure= для оповещения, вместо самодельных циклов в bash.

Единый журнал запусков убирает поиск причины по разным логам. journalctl -u имя.timer за секунды отдаёт код выхода, вывод stderr и время старта, поэтому сбой обнаруживается не утром по жалобе, а в течение минут после окна. На практике время до обнаружения сбоя сокращается с нескольких часов до десятков минут.

Предсказуемость окна выполнения задаётся OnCalendar и RandomizedDelaySec: расписание читается как обычная строка, а разброс старта снимает одновременную нагрузку на диск и сеть. Дрейф, который копился у cron при долгих задачах, не возникает. В сумме число ручных вмешательств в месяц падает с восьми-десяти до одного-двух — обычно это разбор внешней причины, а не перезапуск задачи руками.

Варианты расписания: сравнение подходов и ограничений

Выбор расписания упирается не в синтаксис, а в гарантии, которые нужны задаче: зависимость от результата предыдущего запуска, поведение после простоя узла, единый журнал и разброс по времени старта.

У cron нет состояния между запусками — пропущенный из-за выключенной машины запуск просто теряется, а ошибка уходит письмом в никуда. У systemd timers есть календарные выражения, случайная задержка старта и добор пропущенного запуска, но нет распределённой блокировки: если задача должна отработать один раз на несколько узлов, планировщик её продублирует.

Вариант Когда подходит Ограничения
Оставить текущий планировщик Простые задачи без зависимостей и требований к журналу Нет добора пропусков, связи с другими юнитами и состояния
Полный перевод на системный планировщик Нужны зависимости, единый журнал, добор пропущенных запусков Надо описать каждую задачу юнитом; на нескольких узлах возможно дублирование
Гибридная схема Критичные задачи переводим, остальное работает как раньше Два места правды — нужен сводный реестр расписаний
Внешний оркестратор заданий Запуск распределённый, важен порядок и повтор при сбое Отдельная система, которую нужно сопровождать и обновлять

Гибридное расписание — рабочий компромисс на время перехода: переносим задачи по одной, сверяем журнал с прежним поведением и только после этого убираем строку из старого файла расписания. Так настройка systemd timers не превращается в разовый рискованный переезд всего узла.

Этапы внедрения: как мы переводим задачи на systemd

Перед заменой планировщика нужно увидеть полную картину: сколько заданий реально работает, какие зависят друг от друга и что произойдёт при пропуске окна запуска. Поэтому перевод идёт по фиксированному порядку — от инвентаризации до передачи в эксплуатацию, с проверкой на каждом шаге.

  1. Инвентаризация расписания задач. Выгружаем записи из системных и пользовательских таблиц планировщика, добавляем запуски из скриптов приложений и сверяем всё с логами. Часть заданий давно падает, часть дублируется, у части потерян владелец — это выясняется именно здесь.
  2. Схема зависимостей заданий. Строим граф: что идёт строго после чего, что можно параллелить, где пропуск окна допустим. Так фиксируются связки вида «бэкап после выгрузки», которые нельзя переносить по одному.
  3. Перенос описаний. Задание разделяется на «что запускаем» и «когда»: отдельный сервисный юнит и таймер. Расписание описывается календарным выражением, включается догон пропущенных окон после простоя — если задание это допускает.
  4. Настройка прав. Прописываем пользователя, рабочий каталог, переменные окружения и ограничения по ресурсам. Задание перестаёт зависеть от чьей-то сессии и уже не утащит за собой весь хост.
  5. Проверка на сбоях. Принудительные запуски, разбор логов, имитация перезагрузки и долгого простоя, поведение при недоступной базе. Нужно убедиться, что задание либо выполняется, либо явно сообщает о сбое.
  6. Передача расписания в эксплуатацию. Отдаём карту заданий, журналирование и оповещения о неуспешных запусках, инструкцию по добавлению и отключению задач. Расписание видно в одном месте и меняется без правки чужих скриптов.

Конфигурации, схемы и регламенты, которые остаются у вас

После переноса расписаний на systemd-таймеры у команды остаётся не только рабочая конфигурация, но и комплект документов, по которому её можно сопровождать без автора. Без него через полгода никто не вспомнит, почему задание стартует в 03:17, от чьего имени и что делать, если оно упало.

  • Описания заданий и юнитов — что делает каждый таймер, какой скрипт вызывает, какие параметры получает и чем календарное выражение отличается от прежней строки cron.
  • Схема зависимостей заданий — что обязано завершиться раньше: монтирование хранилища, поднятие базы, выгрузка из внешней системы.
  • Правила доступа — от чьего имени идут запуски, какие группы и права нужны, где лежат учётные данные, чем ограничены возможности юнита.
  • Документация по расписанию задач — сводная таблица «задание — периодичность — окно выполнения — владелец», включая редкие и календарные запуски.
  • Регламент реагирования на сбой — как приходит уведомление, кто и в какой срок смотрит журнал, когда задание перезапускается автоматически, а когда блокируется до разбора.
  • Порядок изменения расписания — как добавить или отключить задание, чтобы правка не задела соседние таймеры и не создала двойной запуск.
  • Контрольные проверки — команды, которыми дежурный подтверждает: таймер активен, следующий запуск запланирован, прошлый завершился успешно.

Материалы оформляем рядом с конфигурацией, чтобы описание и юнит менялись одним изменением, а не жили в разных местах. Сопровождающая команда получает точку входа: развернуть расписание с нуля, найти владельца задания, разобрать сбой по журналу.

Кейс: как задачи по расписанию перестали теряться

Что было до

Ночные регламентные задачи — выгрузки, пересчёт витрин, обслуживание индексов — жили на классическом cron в трёх окружениях. Запуск при этом никто не отслеживал: если машина перезагружалась в момент окна, задача пропускалась молча, и утренние отчёты собирались на устаревших данных. За месяц терялось 6-9 % ночных запусков, поиск причины сбоя занимал в среднем 45 минут, а дежурный перезапускал задачи руками до 15 раз в неделю.

Что изменили

Каждую задачу описали парой юнитов — таймер и сервис — и раскатали из общего шаблона через Ansible, чтобы расписание версионировалось вместе с конфигурацией, а не жило в чужой таблице на сервере. Ключевым стал Persistent=true: пропущенное из-за выключенной машины окно догоняется сразу после включения, но при штатной работе дублирования нет. Связка OnFailure= с юнитом-обработчиком отправляет уведомление в дежурный канал, а вывод задачи пишется в системный журнал с привязкой к юниту — историю прогонов видно без отдельного агента.

Что стало

Метрика До После
Пропущенные ночные запуски 6-9 % 0
Поиск причины сбоя 45 минут 2-3 минуты
Ручные перезапуски до 15 в неделю единичные случаи

Пропуски закрылись механикой, а не призывом «смотреть внимательнее»: пропущенный запуск догоняется, упавший сразу виден в журнале и в уведомлении. Диагностика свелась к фильтру по имени юнита, поэтому инцидент разбирается до начала рабочего дня. Переход обратим: задачи остаются отдельными юнитами, и любую из них можно вернуть на прежнее расписание, не задевая остальные.

Чем отличается запуск по расписанию от постоянного сервиса

Юнит systemd — это единица управления, и его тип определяет жизненный цикл: один процесс держит состояние постоянно, другой выполняется и завершается. Отсюда и разница между сервисом и таймером — сервис отвечает на вопрос «что должно работать», таймер — «когда запустить то, что завершается». При настройке systemd timers это разделение важнее синтаксиса расписания.

Постоянный сервис ведёт процесс: следит за ним, поднимает после падения, собирает вывод в журнал. Таймер не держит процесс в памяти — он активирует привязанный unit по календарному расписанию или по интервалу после загрузки. Саму работу оформляют oneshot-сервисом: код не крутится в фоне, а вызывается ровно на время выполнения.

Поведение после перезагрузки — ключевое отличие от старого способа запуска по минутам. С включённым догоном пропущенных запусков система выполняет задание сразу после старта, если машина в этот момент была выключена; без него пропуск теряется молча. У постоянного сервиса перезагрузка — это рестарт процесса, и состояние, не сохранённое на диске, восстанавливать приходится вручную.

Ещё два параметра, которых нет у классического cron: окно случайного разброса и точность запуска. Они размазывают нагрузку, когда десятки однотипных задач стартуют в одну и ту же минуту.

Выбор простой: если процесс живёт и держит соединение, кэш или очередь — это сервис; если задача конечна (выгрузка, бэкап, ротация логов) — таймер плюс oneshot. Смешанный вариант тоже рабочий: постоянный сервис, который сам активирует таймер по своему событию.

Технические сомнения перед переносом расписаний

Перенос расписаний не требует останавливать текущие задания: cron и таймеры работают параллельно, пока мы переводим скрипты по одному. Основная работа — не переписать логику, а воспроизвести окружение запуска: cron поднимает задание с урезанным PATH и без рабочего каталога, поэтому в unit явно задаём Environment=, WorkingDirectory= и User=. Пока строка запуска даёт тот же результат в обоих планировщиках, старое задание остаётся включённым.

Скрипты писались под cron — их придётся переписывать?

Нет. Скрипт вызывается как есть, меняется только обвязка. Частая причина «после переноса сломалось» — не логика, а другой рабочий каталог и переменные окружения, поэтому первым переводим низкорисковое задание и сверяем результат с прежним.

Задания запускаются от разных пользователей — как это переносится?

Роли описываются в unit: User= и Group=, плюс права на каталоги вывода и журналов. Там же решается случай «скрипт писал в домашний каталог» — путь задаётся явно, а не наследуется от того, кто запустил задание.

Как отлаживать ночные сбои, если команда спит?

Каждый запуск попадает в журнал с отметкой времени, кодом возврата и выводом — смотрим историю по конкретному заданию, а не гадаем. Режим догона выполняет пропущенный запуск после включения сервера, так что ночной сбой не превращается в день без данных.

А если задание зависит от сети или смонтированных ресурсов?

Зависимости описываются явно, поэтому запуск не стартует раньше, чем поднялась сеть или подключилось хранилище. Для заданий, бьющих в один ресурс, добавляем разброс по времени — пик нагрузки на сервер размазывается.

Обсудим задачу: что нужно знать перед стартом работ

Чтобы оценка переноса расписания была точной, а не «по аналогии», нам достаточно инвентаризации текущих задач и понимания окружения. Если часть данных уже под рукой, разбор идёт быстрее, а спорные места обсуждаем предметно.

  • где сейчас живут расписания: crontab пользователей, файлы в /etc/cron.d и /etc/cron.*, задачи во внутренних планировщиках приложений;
  • перечень задач: что запускается, с какой периодичностью, сколько длится обычно и в пике;
  • зависимости и порядок: что обязано завершиться до старта следующего, где нужны блокировки от параллельного запуска;
  • окружение: дистрибутивы, права на создание unit-файлов, запуск под непривилегированным пользователем, доступ к секретам;
  • логи и оповещения: куда пишется вывод сейчас, кто его читает, куда должны уходить сообщения об ошибках;
  • поведение при пропуске: если узел был выключен в момент запуска — догонять задачу или пропустить;
  • окна для работ: когда перенос не задевает бизнес-процессы.

Этого хватает, чтобы вернуться с планом внедрения расписания и разметкой unit-файлов и таймеров. Наши инженеры отдельно разберут частные случаи — пересечения по времени, запуск от разных пользователей, перенос без потери пропущенных запусков — и предложат схему с откатом на прежнее расписание.

Обсудим задачу настройки расписания: опишите перечисленное, и мы вернёмся с оценкой работ и планом.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139