EQVPS
PanelZacznij

Jak planować zadania na VPS: cron i timery systemd

Kopie zapasowe, sprzątanie i skrypty według harmonogramu. Cron w pięć minut i trzy pułapki, które po cichu psują zadania, a potem timery systemd: logi, nadrabianie po przestoju i dokładny czas. Każde zachowanie poniżej sprawdziliśmy na świeżym serwerze z Ubuntu 24.04.

Serwer zarabia na siebie, pracując, gdy śpisz: kopie zapasowe, rotacja logów, odnawianie certyfikatów, raport na dziewiątą. Linux ma do tego dwa harmonogramy. Cron ma czterdzieści lat i mieści się w jednej linii. Timery systemd to dwa małe pliki, za to logi, nadrabianie po przestoju i dokładny czas. Ten poradnik omawia oba na Ubuntu 24.04; każde zachowanie sprawdziliśmy na świeżym serwerze z naszego obrazu, łącznie z trzema błędami, przez które zadanie cron pada bez słowa.

Potrzebujesz użytkownika z sudo; poradnik konfiguracji Ubuntu go tworzy.

1. Cron w pięć minut

Cron jest zainstalowany i działa na naszych obrazach Ubuntu i Debian. Każdy użytkownik ma własną tabelę zadań; swoją edytujesz tak:

crontab -e

Każda linia to pięć pól czasu i polecenie:

# 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

Czyli: o 03:30 każdej nocy, co 5 minut, o 09:00 w dni robocze i raz po każdym uruchomieniu. */5 to co piąta wartość, 1-5 zakres, 1,15 lista. Obowiązuje strefa serwera, na naszych obrazach UTC; ustaw swoją raz według poradnika o nazwie hosta i strefie czasowej, a cron będzie jej używał.

Co się uruchomiło, sprawdzisz w journalctl -u cron. Każde uruchomienie zapisuje użytkownika i polecenie, i właśnie tam widać trzy pułapki.

Pułapka 1: wyjście znika. Cron wysyła wyjście zadania pocztą do użytkownika. Nasz obraz nie ma serwera pocztowego, więc w dzienniku pojawiło się (CRON) info (No MTA installed, discarding output), a wyjście przepadło. Przekieruj je sam, jak w pierwszym przykładzie: >> /var/log/backup.log 2>&1.

Pułapka 2: znak procentu. W crontab % kończy polecenie, a resztę zamienia na standardowe wejście. Zaplanowaliśmy date +%F > /tmp/today. Dziennik pokazuje, co cron naprawdę uruchomił: CMD (date +). Plik nie powstał i żaden błąd do nas nie dotarł. Pisz \% albo przenieś polecenie do skryptu i planuj skrypt.

Pułapka 3: inne środowisko. Cron uruchamia zadania przez /bin/sh, nie bash, w katalogu domowym i z okrojonym środowiskiem. Na Ubuntu 24.04 PATH okazał się lepszy od swojej reputacji — pełna ścieżka systemowa, z /usr/local/bin i /snap/bin, zarówno dla root, jak i zwykłego użytkownika. Wciąż brakuje wszystkiego z katalogu domowego: ~/.local/bin, node z nvm, pipx, cargo. Podawaj w zadaniu pełne ścieżki albo zaczynaj linię od . $HOME/.profile;.

Zadania należące do systemu, a nie użytkownika, umieszczaj w /etc/cron.d/ jako osobny plik z jednym dodatkowym polem — użytkownikiem:

30 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Ubuntu uruchamia też wszystko z /etc/cron.hourly, cron.daily, cron.weekly i cron.monthly — w 17. minucie każdej godziny, o 06:25, o 06:47 w niedziele i o 06:52 pierwszego dnia miesiąca. Wrzuć tam wykonywalny skrypt, a uruchomi się razem z resztą.

2. Timery systemd

Timer to dwie jednostki: usługa, która wykonuje pracę, i timer, który mówi kiedy. Najpierw utwórz skrypt, potem jednostki.

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

Włączaj timer, nie usługę:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers backup.timer

list-timers pokazuje następne uruchomienie, ile do niego zostało, i ostatnie uruchomienie. Logów nie trzeba konfigurować: w journalctl -u backup.service jest każdy start, koniec i każda linia wyjścia ze znacznikami czasu. Aby uruchomić zadanie od razu — sudo systemctl start backup.service; tak samo warto przetestować skrypt, zanim zaufasz harmonogramowi.

Składnia kalendarza. OnCalendar czyta się jako dzieńtygodnia rok-miesiąc-dzień godzina:minuta:sekunda, * to dowolna wartość. Sprawdź wyrażenie przed użyciem:

systemd-analyze calendar "Mon..Fri *-*-* 03:00" --iterations 2

Polecenie wypisuje postać znormalizowaną i najbliższe daty. Kilka wyrażeń, które sprawdziliśmy:

OnCalendarZnaczenie
*-*-* 03:30Codziennie o 03:30
Mon..Fri *-*-* 09:00W dni robocze o 09:00
*:0/15Co 15 minut
weeklyW poniedziałki o 00:00
*-*-01 04:00Pierwszego dnia każdego miesiąca o 04:00
*-*-* 03:00 Europe/Kyiv03:00 czasu kijowskiego; u nas uruchomił się o 01:00 UTC

Trzy ustawienia, które warto znać. Persistent=true zapisuje ostatnie uruchomienie na dysku i nadrabia uruchomienie pominięte, gdy serwer był wyłączony lub timer zatrzymany. Zatrzymaliśmy nasz timer „co dwie minuty”, poczekaliśmy, aż minie planowe uruchomienie, i włączyliśmy go ponownie: pominięte uruchomienie ruszyło w tej samej sekundzie. Bez Persistent timer po prostu czeka na kolejny termin.

AccuracySec to największa niespodzianka. Domyślnie systemd grupuje timery w oknie jednej minuty, żeby rzadziej budzić system, dlatego nasze pierwsze uruchomienie przyszło o 07:42:18, a nie o 07:42:00. Z AccuracySec=1s kolejne wypadły co do sekundy o 07:46:00 i 07:48:00. Ustaw je, gdy liczy się minuta; dla nocnej kopii zostaw domyślne.

RandomizedDelaySec=10m robi odwrotnie i to celowo: rozkłada start w oknie, żeby dziesięć serwerów nie uderzyło w ten sam magazyn kopii w tej samej sekundzie.

Do jednorazowego zadania pliki w ogóle nie są potrzebne:

sudo systemd-run --on-active=30m --unit=cleanup-once /usr/local/bin/cleanup.sh

Polecenie tworzy tymczasowy timer, który zadziała raz i zniknie; do tego czasu widać go w systemctl list-timers.

3. Co wybrać

CronTimer systemd
Konfiguracja1 linia2 pliki
LogiTylko po przekierowaniu wyjściajournalctl -u name.service
Pominięte uruchomienie, gdy serwer był wyłączonyPrzepadaWykona się przy starcie z Persistent=true
Zadanie jeszcze działa przy kolejnym terminieStartuje druga kopiaPomijane, dopóki pierwsze nie skończy
Dokładny czasCo do minutyCo do sekundy z AccuracySec=1s
StatusBraklist-timers, systemctl status

Wybierz cron dla szybkiej linii, którą rozpoznasz i za rok. Wybierz timer dla wszystkiego, czego awarię musisz zobaczyć, co nie może się nakładać albo co musi się wykonać mimo pominięcia przez restart: kopie zapasowe, odnawianie certyfikatów, sprzątanie baz danych. Jeśli zadanie cron może się nałożyć, owiń je: flock -n /tmp/backup.lock /usr/local/bin/backup.sh, a druga kopia od razu się zakończy.

Oba harmonogramy uruchamiają zadanie i się zatrzymują. Dla programu, który ma działać całą dobę i wstawać po awarii, potrzebna jest usługa, a nie harmonogram; zobacz, jak utworzyć usługę systemd, a dla Windows — jak uruchomić skrypt 24/7 na Windows Server. Dobry pierwszy timer pochodzi z naszego poradnika kopii zapasowych z restic; automatyczne aktualizacje bezpieczeństwa to timer, który Ubuntu już dostarcza, apt-daily-upgrade.timer, jeden z 17 działających na świeżym serwerze.

FAQ

Dlaczego zadanie działa w terminalu, a w cron nie?

Trzy typowe powody. Cron uruchamia zadanie przez sh, nie bash, więc składnia tylko dla bash zawodzi. Narzędzia z katalogu domowego, jak nvm, pipx czy cargo, nie trafiają do PATH cron, więc podawaj pełne ścieżki. A każdy % w linii polecenia ją przerywa: na naszym serwerze date +%F zamieniło się w date +. Zapisz go jako \% albo przenieś polecenie do skryptu.

Gdzie trafia wyjście cron?

Domyślnie na pocztę. Nasz obraz nie ma serwera pocztowego, więc cron zapisuje w dzienniku 'No MTA installed, discarding output', a wyjście przepada. Dopisz do zadania >> /var/log/myjob.log 2>&1 albo przejdź na timer systemd, którego wyjście samo trafia do dziennika.

Czym timer systemd jest lepszy od cron?

Logi w journalctl, brak drugiej kopii zadania, które jeszcze działa, nadrabianie z Persistent=true uruchomień pominiętych, gdy serwer był wyłączony, i status z następnym i ostatnim uruchomieniem. Cron wygrywa zwięzłością: jedna linia zamiast dwóch plików.

Timer zadziałał 18 sekund później. Dlaczego?

Domyślnie systemd grupuje timery w oknie jednej minuty, żeby rzadziej budzić system — to ustawienie AccuracySec. Nasz timer „co dwie minuty” zadziałał o 07:42:18 zamiast 07:42:00. Z AccuracySec=1s zadziałał dokładnie o 07:46:00. Ustaw je, gdy liczy się dokładna minuta.

W jakiej strefie czasowej działają cron i timery?

W strefie serwera, na naszych obrazach to UTC. Ustaw ją raz przez timedatectl, zobacz nasz poradnik o nazwie hosta i strefie czasowej, a cron i timery będą z niej korzystać. Timer może też mieć własną strefę: OnCalendar=*-*-* 03:00 Europe/Kyiv w naszym teście uruchomił się o 01:00 UTC.

Komentarze

Brak komentarzy. Bądź pierwszy.

Zostaw komentarz

Komentarze są moderowane przed pojawieniem się.