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.

Odhadovaná délka studia: 50 minut · Stav: nehotovo

Technologie: Cron

Ú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ř. */15 v 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
    jen python3),
  • explicitně nastavit PATH na 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

  1. Co znamená crontabový řádek */15 9-17 * * 1-5 a kdy přesně by se
    podle něj úloha spouštěla?
  2. Proč skript, který funguje při ručním spuštění v terminálu, může
    selhávat, když ho spustí cron?
  3. K čemu slouží přesměrování >> soubor.log 2>&1 a co se stane, pokud
    ho v crontabu vynecháte?
  4. Napište systemd .timer jednotku, 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í.