Modul 4 — Skriptování a automatizace
20. Cron a plánované úlohy
Syntaxe crontabu, plánování opakovaných úloh, logování výstupu a časovače systemd jako moderní alternativa.
Úvod a kontext
Zálohy, rotace logů, healthcheck skripty, čištění dočasných souborů,
synchronizace dat — spousta provozních úloh se má spouštět pravidelně, bez
lidského zásahu. Nejrozšířenějším nástrojem pro plánování takových úloh na
Linuxu je cron, démon běžící na pozadí, který v přesně daných časech
spouští zadané příkazy. Tato lekce ukazuje syntaxi crontabu, běžné chyby
při plánování úloh a moderní alternativu — systemd timery.
Teorie
Jak cron funguje
Cron je démon (crond/cron), který běží na pozadí od startu systému a
každou minutu kontroluje seznam naplánovaných úloh — tzv. crontab
(cron table). Pokud aktuální čas odpovídá naplánování nějaké úlohy, cron ji
spustí jako nový proces.
Každý uživatel může mít vlastní crontab. Úpravu spouští příkaz:
crontab -e
Otevře se textový editor s obsahem uživatelova crontabu. Zobrazení
aktuálního obsahu bez editace:
crontab -l
Smazání celého crontabu:
crontab -r
Syntaxe řádku crontabu
Každý řádek crontabu má pět časových polí následovaných příkazem:
* * * * * příkaz
│ │ │ │ │
│ │ │ │ └── den v týdnu (0–7, 0 i 7 = neděle)
│ │ │ └──── měsíc (1–12)
│ │ └────── den v měsíci (1–31)
│ └──────── hodina (0–23)
└────────── minuta (0–59)
Hvězdička * znamená "kdykoliv" (každou hodnotu daného pole). Příklady:
0 3 * * * spustí se každý den ve 3:00
30 2 * * 1 spustí se každé pondělí ve 2:30
*/15 * * * * spustí se každých 15 minut
0 9-17 * * 1-5 každou celou hodinu od 9 do 17, pondělí až pátek
0 0 1 * * první den každého měsíce o půlnoci
Speciální zápisy usnadňující práci:
*/N— každých N jednotek (např.*/15v poli minut = každých 15 minut),A-B— rozsah (např.9-17= od 9 do 17),A,B,C— výčet konkrétních hodnot (např.1,15= 1. a 15. den v měsíci).
Cron nabízí i lidsky čitelné zkratky pro nejčastější případy:
@daily odpovídá "0 0 * * *" (jednou denně o půlnoci)
@weekly odpovídá "0 0 * * 0" (jednou týdně, v neděli)
@reboot spustí se jednou při startu systému
Prostředí, ve kterém cron úlohy běží
Jedna z nejčastějších chyb začátečníků: skript funguje při ručním
spuštění v terminálu, ale z cronu "nefunguje". Důvod je téměř vždy stejný
— cron spouští úlohy s minimálním prostředím, bez proměnných
(PATH, HOME apod.), které má uživatel nastavené ve svém interaktivním
shellu (přes ~/.bashrc apod.). Řešení:
- v příkazu vždy používat absolutní cesty (
/usr/bin/python3, ne
jenpython3), - explicitně nastavit
PATHna začátku crontabu, pokud je potřeba, - nechat skript, ne přímo příkaz, a nastavit prostředí uvnitř skriptu.
PATH=/usr/local/bin:/usr/bin:/bin
0 3 * * * /usr/local/bin/zaloha.sh
Logování a přesměrování výstupu
Cron ve výchozím nastavení výstup úlohy posílá e-mailem místnímu
uživateli (pokud je nastavený poštovní systém) — v praxi na většině
serverů e-mail nikam nedorazí a výstup se tiše ztratí. Standardní praxe je
výstup explicitně přesměrovat do log souboru:
0 3 * * * /usr/local/bin/zaloha.sh >> /var/log/zaloha.log 2>&1
>> připojuje výstup na konec souboru (nepřepisuje ho), 2>&1
přesměruje i chybový výstup (stderr) do stejného souboru jako standardní
výstup (stdout). Bez 2>&1 by se chybové hlášky ztratily úplně.
Systemd timery — moderní alternativa
Na systémech se systemd (většina moderních distribucí) existuje
alternativa k cronu: systemd timery. Skládají se ze dvou jednotek —
.service (co se má spustit) a .timer (kdy):
# /etc/systemd/system/zaloha.service
[Unit]
Description=Noční záloha databáze
[Service]
Type=oneshot
ExecStart=/usr/local/bin/zaloha.sh
# /etc/systemd/system/zaloha.timer
[Unit]
Description=Spouští zaloha.service každou noc ve 3:00
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Aktivace:
sudo systemctl enable --now zaloha.timer
Výhody oproti cronu: výstup a chyby jsou automaticky v journalctl
(journalctl -u zaloha.service), Persistent=true dožene spuštění, které
bylo zmeškáno kvůli vypnutému serveru, a stav lze kdykoliv zkontrolovat
příkazem systemctl list-timers. Nevýhoda: víc "obřadu" (dva soubory
místo jednoho řádku) pro triviální úlohy, proto se cron pro jednoduché
případy stále běžně používá.
Praktický příklad
Naplánování skriptu, který každou noc ve 2:30 zazálohuje databázi a
výsledek zaloguje, s ošetřením prostředí a výstupu:
crontab -e
Přidaný řádek:
PATH=/usr/local/bin:/usr/bin:/bin
30 2 * * * /usr/local/bin/zaloha-db.sh >> /var/log/zaloha-db.log 2>&1
Kontrola, že úloha je v crontabu:
crontab -l
Ověření, že cron démon skutečně běží (na systemd distribucích):
systemctl status cron
# nebo, podle distribuce:
systemctl status crond
Sledování logu po prvním spuštění:
tail -f /var/log/zaloha-db.log
Pokud je log prázdný i po čase, kdy úloha měla proběhnout, nejčastější
příčiny jsou: špatná syntaxe času, chybějící absolutní cesta ke skriptu,
nebo skript, který selhává kvůli chybějícímu prostředí (proměnné, které
byly nastavené jen v interaktivním shellu).
Shrnutí
Cron spouští úlohy podle pětipolové syntaxe (minuta, hodina, den v měsíci,
měsíc, den v týdnu), přičemž úlohy běží v minimálním prostředí — proto se
v crontabu vždy používají absolutní cesty a explicitní přesměrování
výstupu (>> log 2>&1), jinak se chyby ztratí. Systemd timery nabízejí
modernější alternativu s lepším logováním přes journalctl a dohnáním
zmeškaných spuštění, ale pro jednoduché jednorázové úlohy zůstává cron
kvůli jednoduchosti běžnou volbou.
Kontrolní otázky
- Co znamená crontabový řádek
*/15 9-17 * * 1-5a kdy přesně by se
podle něj úloha spouštěla? - Proč skript, který funguje při ručním spuštění v terminálu, může
selhávat, když ho spustí cron? - K čemu slouží přesměrování
>> soubor.log 2>&1a co se stane, pokud
ho v crontabu vynecháte? - Napište systemd
.timerjednotku, která spustí danou službu každou
neděli v 4:00 ráno, a vysvětlete, k čemu sloužíPersistent=true.
Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.