EQVPS
ТаблоЗапочни

Как да планираш задачи на VPS с cron и таймери на systemd

Пускай бекъпи, почистване и скриптове по график. Cron за пет минути и трите капана, които тихо чупят задачите, след това таймери на systemd: логове, наваксване след спиране и точно време. Всяко поведение по-долу е проверено на нов сървър с Ubuntu 24.04.

Сървърът си изкарва хляба, като работи, докато спиш: бекъпи, ротация на логове, подновяване на сертификати, отчет в девет. В 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/Kyiv03: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-те, работещи на нов сървър.

Въпроси

Защо задачата работи в терминала, но не и в cron?

Три обичайни причини. Cron пуска задачата със sh, а не с bash, затова синтаксисът само за bash се проваля. Инструментите от домашната директория, като nvm, pipx или cargo, не са в PATH на cron, така че използвай пълни пътища. И всеки % в командния ред я прекъсва: на нашия сървър date +%F стана date +. Пиши го като \% или премести командата в скрипт.

Къде отива изходът на cron?

По подразбиране по имейл. Нашият образ няма пощенски сървър, затова cron записва 'No MTA installed, discarding output' и изходът се губи. Добави >> /var/log/myjob.log 2>&1 към задачата или премини на таймер на systemd, чийто изход сам отива в журнала.

С какво таймерът на systemd е по-добър от cron?

Логове в journalctl, без второ копие на задача, която още работи, наваксване с Persistent=true на пропуснати пускания, докато сървърът е бил спрян, и ред за състояние със следващото и последното пускане. Cron печели с краткост: един ред вместо два файла.

Таймерът ми се задейства 18 секунди по-късно. Защо?

По подразбиране systemd групира таймерите в прозорец от една минута, за да буди системата по-рядко — това е настройката AccuracySec. Нашият таймер „на всеки две минути“ се задейства в 07:42:18 вместо в 07:42:00. С AccuracySec=1s се задейства точно в 07:46:00. Задай я, когато точната минута има значение.

В коя часова зона работят cron и таймерите?

В тази на сървъра, на нашите образи това е UTC. Задай я веднъж с timedatectl, виж нашето ръководство за име на хост и часова зона, и cron и таймерите ще я следват. Таймерът може да носи и собствена зона: OnCalendar=*-*-* 03:00 Europe/Kyiv в нашия тест се пусна в 01:00 UTC.

Коментари

Още няма коментари. Бъди първият.

Остави коментар

Коментарите се модерират преди да се появят.