systemd timer zamiast crona – kiedy warto i jak to ustawić

Cron nadal działa i w wielu miejscach jest wystarczający. Problem zaczyna się wtedy, gdy po kilku miesiącach trzeba ustalić, czy zadanie wykonało się poprawnie, gdzie są logi, dlaczego nie ruszyło po restarcie albo kto dopisał przekierowanie do pliku w katalogu, którego już nie ma.

W takich przypadkach warto użyć systemd timer. Nie dlatego, że cron jest zły, tylko dlatego, że timer systemd lepiej pasuje do serwerów, na których i tak diagnozujemy usługi przez systemctl oraz journalctl.

Poniżej praktyczny przykład: prosty backup katalogu /etc uruchamiany codziennie o 02:30.

Kiedy systemd timer ma sens

Timer systemd warto rozważyć, gdy chcesz:

  • mieć status zadania w systemctl,
  • widzieć logi w journalctl,
  • uruchomić zadanie po starcie systemu albo po określonym czasie,
  • nadrobić pominięte wykonanie po wyłączonym serwerze,
  • łatwo powiązać harmonogram z usługą,
  • mieć osobny unit .service i osobny unit .timer,
  • użyć opcji takich jak RandomizedDelaySec albo Persistent.

Cron jest prostszy do jednowierszowych zadań. Timer systemd jest wygodniejszy, gdy zadanie jest ważne i chcesz je normalnie monitorować jak usługę.

1. Skrypt backupu

Najpierw prosty skrypt:

sudo mkdir -p /usr/local/sbin /backup/system
sudo nano /usr/local/sbin/backup-etc.sh

Zawartość:

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/backup/system"
DATE="$(date +%F-%H%M)"

mkdir -p "$BACKUP_DIR"
tar -czf "$BACKUP_DIR/etc-$DATE.tar.gz" /etc
find "$BACKUP_DIR" -type f -name "etc-*.tar.gz" -mtime +14 -delete

Uprawnienia:

sudo chmod 750 /usr/local/sbin/backup-etc.sh
sudo /usr/local/sbin/backup-etc.sh
ls -lh /backup/system

Najpierw uruchom skrypt ręcznie. Timer nie powinien być pierwszym testem działania skryptu.

2. Unit service

Tworzymy usługę, która wykona skrypt:

sudo nano /etc/systemd/system/backup-etc.service

Zawartość:

[Unit]
Description=Backup katalogu /etc

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup-etc.sh

To nie jest usługa działająca cały czas. Type=oneshot oznacza zadanie jednorazowe: uruchom skrypt i zakończ.

Test:

sudo systemctl daemon-reload
sudo systemctl start backup-etc.service
sudo systemctl status backup-etc.service --no-pager
journalctl -u backup-etc.service --no-pager

Jeżeli tutaj jest błąd, nie twórz jeszcze timera. Najpierw napraw usługę.

3. Unit timer

Teraz harmonogram:

sudo nano /etc/systemd/system/backup-etc.timer

Zawartość:

[Unit]
Description=Codzienny backup katalogu /etc

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=10min
Unit=backup-etc.service

[Install]
WantedBy=timers.target

Co tu się dzieje:

  • OnCalendar=*-*-* 02:30:00 uruchamia zadanie codziennie o 02:30,
  • Persistent=true powoduje, że jeżeli serwer był wyłączony w czasie planowanego wykonania, zadanie zostanie nadrobione po uruchomieniu,
  • RandomizedDelaySec=10min rozrzuca start w zakresie do 10 minut,
  • Unit=backup-etc.service wskazuje usługę, która ma zostać uruchomiona.

Manpage systemd.timer opisuje, że timer domyślnie aktywuje usługę o tej samej nazwie. Tu wpis Unit= jest jawny, żeby było czytelnie.

4. Włączenie timera

sudo systemctl daemon-reload
sudo systemctl enable --now backup-etc.timer

Sprawdzenie:

systemctl list-timers --all | grep backup-etc
systemctl status backup-etc.timer --no-pager

W list-timers zobaczysz między innymi następne planowane uruchomienie i ostatnie wykonanie. To jedna z największych przewag nad prostym wpisem w cronie.

5. Logi i diagnostyka

Logi usługi:

journalctl -u backup-etc.service --since "today" --no-pager

Logi timera:

journalctl -u backup-etc.timer --no-pager

Status usługi:

systemctl status backup-etc.service --no-pager

Jeżeli skrypt zakończy się kodem błędu, zobaczysz to w statusie usługi. Przy cronie często trzeba najpierw znaleźć, gdzie ktoś przekierował stdout i stderr.

6. Test harmonogramu bez czekania do nocy

Na czas testu możesz zmienić OnCalendar na coś krótkiego:

OnCalendar=*:0/5

To uruchamia timer co 5 minut. Po teście wróć do docelowej wartości i wykonaj:

sudo systemctl daemon-reload
sudo systemctl restart backup-etc.timer
systemctl list-timers --all | grep backup-etc

Nie zostawiaj testowego harmonogramu na produkcji. Backup co 5 minut szybko zrobi bałagan.

7. Różnica między OnCalendar i OnBootSec

OnCalendar działa według zegara kalendarzowego: konkretna godzina, dzień, tydzień, miesiąc.

Przykłady:

OnCalendar=daily
OnCalendar=Mon *-*-* 03:00:00
OnCalendar=*-*-01 04:00:00

OnBootSec albo OnUnitActiveSec są timerami monotonicznymi, liczonymi od startu systemu albo od ostatniej aktywacji.

Przykład:

OnBootSec=15min
OnUnitActiveSec=1h

To ma sens przy zadaniach technicznych, które mają ruszyć np. 15 minut po starcie i potem co godzinę. Do backupu o konkretnej godzinie zwykle lepszy jest OnCalendar.

8. Typowe błędy

Najczęstsze problemy:

  • plik .timer istnieje, ale nie ma odpowiadającego .service,
  • po edycji unitów nie wykonano systemctl daemon-reload,
  • timer został włączony, ale usługa nie działa przy ręcznym systemctl start,
  • skrypt nie ma uprawnień wykonywania,
  • ścieżki w skrypcie są względne zamiast absolutnych,
  • backup zapisuje się na tym samym dysku, który ma być ratowany,
  • Persistent=true uruchamia zaległy backup w nieoczekiwanym momencie,
  • RandomizedDelaySec powoduje, że zadanie nie startuje dokładnie o wskazanej godzinie,
  • po zmianie harmonogramu nie zrestartowano timera.

Przy zadaniach produkcyjnych używaj pełnych ścieżek i zaczynaj skrypty od:

set -euo pipefail

To nie rozwiąże wszystkich problemów, ale ogranicza ciche błędy.

9. Jak usunąć timer

Wyłączenie:

sudo systemctl disable --now backup-etc.timer

Usunięcie plików:

sudo rm /etc/systemd/system/backup-etc.timer
sudo rm /etc/systemd/system/backup-etc.service
sudo systemctl daemon-reload

Jeżeli używasz Persistent=true, manpage systemd.timer wskazuje też możliwość czyszczenia stanu timera:

sudo systemctl clean --what=state backup-etc.timer

To przydaje się przy usuwaniu albo przebudowie timera, żeby nie został stary znacznik ostatniego wykonania.

Podsumowanie

Cron jest prosty i nadal ma swoje miejsce. Jeżeli jednak zadanie jest ważne, systemd timer daje wygodniejszą diagnostykę: systemctl status, journalctl, list-timers, osobny plik usługi i kontrolę nad zachowaniem po restarcie.

Dla backupów, rotacji plików, cyklicznych kontroli systemu i zadań administracyjnych na Ubuntu Server timer systemd często jest czytelniejszy niż wpis w cronie. Najważniejsze: najpierw sprawdź skrypt ręcznie, potem service, a dopiero na końcu timer.

Źródła

  • systemd.timer manpage: https://man7.org/linux/man-pages/man5/systemd.timer.5.html
  • systemd.service manpage: https://man7.org/linux/man-pages/man5/systemd.service.5.html
  • systemctl manpage: https://man7.org/linux/man-pages/man1/systemctl.1.html
☕ Postaw mi kawę