Налаштування 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