Сервер окупается тем, что работает, пока вы спите: бэкапы, ротация логов, продление сертификатов, отчёт к девяти утра. В Linux для этого есть два планировщика. Cron — сорок лет и одна строка. Таймеры systemd — два небольших файла, зато логи, догон после простоя и точное время. Это руководство разбирает оба на Ubuntu 24.04; каждое поведение проверено на свежем сервере из нашего образа, включая три ошибки, из-за которых задача cron падает без единого слова.
Нужен пользователь с sudo; руководство по настройке Ubuntu его создаёт.
1. Cron за пять минут
Cron установлен и работает на наших образах Ubuntu и Debian. У каждого пользователя своя таблица задач; редактируйте свою так:
crontab -e
Каждая строка — пять полей времени и команда:
# min hour day month weekday command
30 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
*/5 * * * * /usr/local/bin/check.sh
0 9 * * 1-5 /usr/local/bin/report.sh
@reboot /usr/local/bin/start-tunnel.sh
То есть: в 03:30 каждую ночь, каждые 5 минут, в 09:00 по будням и один раз после каждой загрузки. */5 — каждое пятое значение, 1-5 — диапазон, 1,15 — список. Время — по поясу сервера, на наших образах это UTC; задайте свой один раз по руководству про имя хоста и часовой пояс, и cron будет его использовать.
Что запускалось, смотрите в journalctl -u cron. Каждый запуск записывает пользователя и команду, и именно там видны три ловушки.
Ловушка 1: вывод пропадает. Cron отправляет вывод задачи пользователю на почту. В нашем образе нет почтового сервера, поэтому в журнале появилось (CRON) info (No MTA installed, discarding output), и вывод пропал. Перенаправляйте его сами, как в первом примере: >> /var/log/backup.log 2>&1.
Ловушка 2: знак процента. В crontab % завершает команду и превращает остаток в стандартный ввод. Мы запланировали date +%F > /tmp/today. Журнал показывает, что cron запустил на самом деле: CMD (date +). Файл не появился, и никакой ошибки до нас не дошло. Пишите \% или вынесите команду в скрипт и планируйте скрипт.
Ловушка 3: другое окружение. Cron запускает задачи через /bin/sh, а не bash, в домашнем каталоге и с коротким окружением. На Ubuntu 24.04 PATH оказался лучше своей репутации — полный системный путь, включая /usr/local/bin и /snap/bin, и для root, и для обычного пользователя. Чего по-прежнему нет, так это всего из домашнего каталога: ~/.local/bin, node из nvm, pipx, cargo. Пишите в задаче полные пути или начинайте строку с . $HOME/.profile;.
Задачи, которые принадлежат системе, а не пользователю, кладите в /etc/cron.d/ отдельным файлом с одним дополнительным полем — пользователем:
30 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
Ещё Ubuntu запускает всё из /etc/cron.hourly, cron.daily, cron.weekly и cron.monthly — в 17 минут каждого часа, в 06:25, в 06:47 по воскресеньям и в 06:52 первого числа. Положите туда исполняемый скрипт, и он будет запускаться вместе с остальными.
2. Таймеры systemd
Таймер — это два юнита: служба, которая делает работу, и таймер, который говорит когда. Сначала создайте скрипт, потом юниты.
sudo nano /usr/local/bin/backup.sh
sudo chmod +x /usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run the backup every night at 03:30
[Timer]
OnCalendar=*-*-* 03:30
Persistent=true
AccuracySec=1m
[Install]
WantedBy=timers.target
Включайте таймер, а не службу:
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers backup.timer
list-timers показывает следующий запуск, сколько до него осталось и последний запуск. Логи не нужно настраивать: в journalctl -u backup.service есть каждый старт, завершение и каждая строка вывода с отметками времени. Чтобы выполнить задачу прямо сейчас — sudo systemctl start backup.service; так же стоит проверить скрипт, прежде чем доверять расписанию.
Синтаксис календаря. OnCalendar читается как деньнедели год-месяц-день час:минута:секунда, * — любое значение. Проверьте выражение до использования:
systemd-analyze calendar "Mon..Fri *-*-* 03:00" --iterations 2
Команда печатает нормализованную форму и ближайшие даты. Несколько выражений, которые мы проверили:
| OnCalendar | Значение |
|---|---|
*-*-* 03:30 | Каждый день в 03:30 |
Mon..Fri *-*-* 09:00 | По будням в 09:00 |
*:0/15 | Каждые 15 минут |
weekly | По понедельникам в 00:00 |
*-*-01 04:00 | Первого числа каждого месяца в 04:00 |
*-*-* 03:00 Europe/Kyiv | 03:00 по Киеву; у нас сработал в 01:00 UTC |
Три настройки, которые стоит знать. Persistent=true записывает последний запуск на диск и догоняет запуск, пропущенный, пока сервер был выключен или таймер остановлен. Мы остановили наш таймер «каждые две минуты», дождались, пока пройдёт плановый запуск, и включили его снова: пропущенный запуск сработал в ту же секунду. Без Persistent таймер просто ждёт следующего слота.
AccuracySec — главный сюрприз. По умолчанию systemd собирает таймеры в окне в одну минуту, чтобы реже будить систему, поэтому наш первый запуск пришёл в 07:42:18, а не в 07:42:00. С AccuracySec=1s следующие запуски пришлись ровно на 07:46:00 и 07:48:00, до секунды. Задайте её, когда важна минута; для ночного бэкапа оставьте умолчание.
RandomizedDelaySec=10m делает обратное и нарочно: размазывает старт по окну, чтобы десять серверов не ударили по одному хранилищу бэкапов в одну и ту же секунду.
Для разовой задачи файлы вообще не нужны:
sudo systemd-run --on-active=30m --unit=cleanup-once /usr/local/bin/cleanup.sh
Команда создаёт временный таймер, который сработает один раз и исчезнет; до этого его видно в systemctl list-timers.
3. Что выбрать
| Cron | Таймер systemd | |
|---|---|---|
| Настройка | 1 строка | 2 файла |
| Логи | Только если перенаправить вывод | journalctl -u name.service |
| Пропущенный запуск, пока сервер был выключен | Потерян | Выполнится при загрузке с Persistent=true |
| Задача ещё работает к следующему тику | Запускается вторая копия | Пропускается, пока не закончится первая |
| Точное время | До минуты | До секунды с AccuracySec=1s |
| Статус | Нет | list-timers, systemctl status |
Берите cron для быстрой строки, которую вы узнаете и через год. Берите таймер для всего, где нужно видеть сбой, где запуски не должны перекрываться или где пропущенный из-за перезагрузки запуск всё равно должен выполниться: бэкапы, продление сертификатов, очистка баз данных. Если задача cron может перекрыться, оберните её: flock -n /tmp/backup.lock /usr/local/bin/backup.sh, и вторая копия сразу завершится.
Оба планировщика запускают задачу и останавливаются. Для программы, которая должна работать круглосуточно и подниматься после падения, нужна служба, а не расписание; смотрите, как создать службу systemd, а для Windows — как запустить скрипт 24/7 на Windows Server. Хороший первый таймер — из нашего руководства по бэкапу через restic; автоматические обновления безопасности — это таймер, который Ubuntu уже поставляет, apt-daily-upgrade.timer, один из 17, работающих на свежем сервере.
Комментарии
Пока нет комментариев. Будьте первым.