Guida ai Timer di Systemd: Sostituire i Job Cron per una Pianificazione Affidabile
Usa i timer di systemd al posto di cron quando hai bisogno di log di journal, gestione delle esecuzioni mancate, dipendenze e controlli delle risorse.
Guida ai Timer di Systemd: Sostituire i Job Cron per una Pianificazione Affidabile
cron è ancora adatto per molti lavori. Se devi eseguire uno script shell ogni notte e hai già una reindirizzazione dei log funzionante, non c'è alcun premio per sostituirlo. Il motivo per cui molti team spostano il lavoro pianificato su Linux verso i timer di systemd non è una moda. È perché i timer di systemd danno al lavoro una vera unità di servizio, log prevedibili, gestione delle dipendenze, comportamento per esecuzioni mancate e limiti delle risorse.
Questo è importante quando il compito non è solo "esegui un comando". Un lavoro di backup potrebbe aver bisogno di un disco montato. Un cache warmer potrebbe aver bisogno che la rete sia utilizzabile. Un esportatore di report potrebbe dover essere eseguito come un account di servizio bloccato e lasciare log leggibili per la prossima persona di turno. Un timer di systemd ti permette di descrivere queste esigenze nello stesso posto in cui gestisci il resto del ciclo di vita del servizio.
Comprendere i Timer di Systemd
I timer di systemd sono file unit di systemd che controllano quando altre unità di systemd, tipicamente unità di service, vengono attivate. A differenza di cron, che è un demone autonomo, i timer di systemd sono una parte integrante del sistema init systemd. Questa integrazione profonda porta diversi vantaggi significativi, specialmente per quanto riguarda l'affidabilità, la registrazione e la gestione delle risorse.
Un timer di systemd funziona sempre in combinazione con un'altra unità, più comunemente un'unità di service. Il file .timer definisce quando dovrebbe verificarsi un evento, e il file .service corrispondente definisce quale azione dovrebbe essere eseguita quando quell'evento viene attivato. Questa chiara separazione delle responsabilità rende i timer di systemd altamente modulari e flessibili.
Vantaggi Chiave dei Timer di Systemd Rispetto a Cron
Mentre cron è funzionale, i timer di systemd affrontano molte delle sue limitazioni, offrendo una soluzione di pianificazione più robusta e ricca di funzionalità:
- Affidabilità e Persistenza: Se un timer calendario usa
Persistent=truee il sistema viene spento durante un'esecuzione pianificata, systemd registra che l'esecuzione è stata persa e avvia il servizio associato dopo il prossimo avvio. Il cron normale di solito non recupera senza uno strumento separato come anacron. - Integrazione con
systemd: I timer beneficiano della potente registrazione disystemd(tramitejournalctl), della gestione delle dipendenze e del controllo delle risorse (cgroups). Questo significa un monitoraggio migliore, una segnalazione degli errori più chiara e la capacità di definire sequenze di avvio complesse o limiti di risorse per le attività pianificate. - Riproducibilità e Controllo Versione: I file unit di
systemdsono file di testo semplice che possono essere facilmente archiviati in sistemi di controllo versione. Questo permette distribuzioni riproducibili e un tracciamento più facile delle modifiche alle attività pianificate su più sistemi. - Pianificazione Basata su Eventi: Oltre alla semplice pianificazione basata sul tempo, i timer di
systemdpossono essere attivati in relazione all'avvio del sistema (OnBootSec) o dopo l'ultima attivazione di un'unità (OnUnitActiveSec), fornendo opzioni di pianificazione più dinamiche. - Espressioni Temporali Flessibili:
systemdoffre un ricco insieme di espressioni di eventi calendario, spesso più leggibili e versatili della sintassi dicron, incluse orarie, giornaliere, settimanali e date/ore specifiche. - Gestione delle Risorse e Dipendenze: I servizi di
systemdavviati dai timer ereditano l'ambiente disystemd, incluse le impostazioni cgroup, e possono dichiarare dipendenze da altre unità disystemd(ad esempio, attendere che la rete o un database siano disponibili prima di eseguire). - Gestione dell'Output Standard/Errori:
systemdcattura automaticamentestdoutestderrdei servizi avviati dai timer e li indirizza al journal di sistema, rendendo il debug e l'auditing molto più semplici rispetto all'output basato su email dicrono alla reindirizzazione manuale.
Configurare i Timer di Systemd
Configurare un timer di systemd comporta la creazione di due file unit: un'unità di servizio (.service) e un'unità timer (.timer). Questi file sono tipicamente posizionati in /etc/systemd/system/ per i timer a livello di sistema o ~/.config/systemd/user/ per i timer specifici dell'utente.
1. L'Unità di Servizio (file .service)
L'unità di servizio definisce il comando o lo script effettivo da eseguire. È un file di servizio standard di systemd, ma spesso progettato per essere eseguito in modo non interattivo e per svolgere un compito specifico.
Esempio: /etc/systemd/system/mytask.service
[Unit]
Description=Servizio Attività Pianificata
[Service]
Type=oneshot
ExecStart=/usr/local/bin/mytask.sh
User=myuser
Group=mygroup
# Opzionale: Limita le risorse sulle versioni più recenti di systemd
# CPUWeight=50
# MemoryMax=1G
[Install]
WantedBy=multi-user.target
Spiegazione:
[Unit]: Contiene informazioni generiche sull'unità.Description: Una descrizione leggibile dall'uomo.
[Service]: Definisce la configurazione specifica del servizio.Type=oneshot: Indica che il servizio esegue un singolo comando e poi termina. Questo è comune per le attività pianificate.ExecStart: Il comando o script da eseguire. Fornisci il percorso completo.User,Group: Definisce l'utente e il gruppo sotto cui verrà eseguito il comando. Esegui sempre le attività con i privilegi minimi necessari.CPUWeight,MemoryMax: Controlli cgroup opzionali. Sono utili quando un lavoro pianificato non deve affamare il resto dell'host.
[Install]: Definisce come l'unità deve essere abilitata.WantedBy=multi-user.target: Sebbene presente, questa sezione è spesso meno critica per i servizi attivati da timer poiché l'unità timer stessa di solito determina l'attivazione. Tuttavia, può essere utile se vuoi anche che il servizio sia attivabile manualmente o per integrarsi in altri target disystemd.
2. L'Unità Timer (file .timer)
L'unità timer definisce quando l'unità di servizio corrispondente deve essere attivata. Deve avere lo stesso nome della sua controparte di servizio (ad esempio, mytask.timer per mytask.service).
Esempio: /etc/systemd/system/mytask.timer
[Unit]
Description=Esegue mytask.service ogni giorno
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=600
AccuracySec=1min
[Install]
WantedBy=timers.target
Spiegazione:
[Unit]: Informazioni generiche.Description: Una descrizione per il timer.
[Timer]: Definisce la configurazione specifica del timer.OnCalendar: L'impostazione più comune, definisce un evento calendario. Usa espressioni come:daily: Ogni giorno a mezzanotte.weekly: Ogni lunedì a mezzanotte.monthly: Il primo giorno di ogni mese a mezzanotte.hourly: Ogni ora al minuto zero.*-*-* 03:00:00: Ogni giorno alle 3:00.Mon..Fri 08:00..17:00: Giorni feriali tra le 8:00 e le 17:00.Mon *-*-* 03:00:00: Ogni lunedì alle 3:00.
OnBootSec: Attiva il servizio dopo un tempo specificato dall'avvio del sistema. Ad esempio,OnBootSec=10min.OnUnitActiveSec: Attiva il servizio dopo un tempo specificato dall'ultima attivazione del servizio. Ad esempio,OnUnitActiveSec=1hper eseguire ogni ora dopo il completamento dell'esecuzione precedente.Persistent=true: Cruciale per l'affidabilità. Se il sistema è spento durante un'esecuzione pianificata, il servizio verrà attivato poco dopo il prossimo avvio.RandomizedDelaySec=600: Aggiunge un ritardo casuale fino a 600 secondi. Questo è utile quando molte macchine condividono lo stesso timer e non vuoi che ogni host colpisca un database, un'API o un server di backup esattamente nello stesso secondo.
Una Migrazione Reale da Cron a Timer
Supponiamo che tu abbia attualmente questa voce cron di root:
15 2 * * * /usr/local/sbin/backup-app.sh >> /var/log/backup-app.log 2>&1
Funziona su una macchina tranquilla, ma ha i soliti punti deboli. Se il disco di backup non è montato, lo script potrebbe fallire a metà. Se il server è spento alle 2:15, l'esecuzione viene saltata. Se lo script scrive un errore utile, qualcuno deve ricordare quale file di log personalizzato controllare. Se lo script inizia a usare troppa memoria, cron non ti aiuterà a contenerlo.
La versione systemd separa il comando dalla pianificazione:
# /etc/systemd/system/backup-app.service
[Unit]
Description=Esegue il backup dei dati dell'applicazione
RequiresMountsFor=/mnt/backups
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=backup
Group=backup
WorkingDirectory=/srv/app
ExecStart=/usr/local/sbin/backup-app.sh
MemoryMax=1G
CPUWeight=40
Nice=10
# /etc/systemd/system/backup-app.timer
[Unit]
Description=Esegue il backup dell'applicazione ogni notte
[Timer]
OnCalendar=*-*-* 02:15:00
Persistent=true
RandomizedDelaySec=15min
AccuracySec=1min
Unit=backup-app.service
[Install]
WantedBy=timers.target
Ci sono alcuni dettagli degni di nota. RequiresMountsFor=/mnt/backups dice a systemd che il percorso deve essere montato prima che il servizio inizi. After=network-online.target e Wants=network-online.target sono utili solo se il tuo gestore di rete fornisce effettivamente un servizio wait-online; su molte distribuzioni quel servizio è disabilitato per impostazione predefinita. Se il backup scrive solo su un disco locale, lascia fuori la dipendenza di rete.
Type=oneshot è adatto per script che fanno il loro lavoro e poi escono. Non usarlo per un demone che rimane in esecuzione. WorkingDirectory= ti salva da script che accidentalmente dipendono dall'essere lanciati da una shell in una directory particolare. User=backup è di solito meglio che eseguire il lavoro come root e sperare che ogni comando all'interno dello script sia attento.
Dopo aver salvato i file:
sudo systemctl daemon-reload
sudo systemctl enable --now backup-app.timer
systemctl list-timers backup-app.timer
Per testare il lavoro immediatamente, avvia il servizio, non il timer:
sudo systemctl start backup-app.service
journalctl -u backup-app.service -n 100 --no-pager
Questa distinzione previene molta confusione. Avviare backup-app.timer arma la pianificazione. Avviare backup-app.service esegue il backup effettivo.
Scegliere l'Espressione Timer Giusta
OnCalendar= è la sostituzione più vicina alla sintassi di cron, ma si legge diversamente. Puoi controllare cosa systemd pensa che un'espressione significhi prima di distribuirla:
systemd-analyze calendar 'Mon..Fri 03:30'
systemd-analyze calendar '*-*-01 04:00:00'
systemd-analyze calendar 'Sun *-*-* 23:00:00'
Usa i timer calendario per lavori basati sull'orologio: backup notturni, report settimanali, pulizie mensili, controlli dei certificati e altre attività dove il calendario umano è importante. Usa i timer monotonici per comportamenti "esegui dopo che è successo qualcosa":
[Timer]
OnBootSec=10min
OnUnitActiveSec=1h
Questo modello avvia il servizio dieci minuti dopo l'avvio, poi di nuovo un'ora dopo l'ultima attivazione. È adatto per polling, pulizie locali e cicli di manutenzione leggeri. Non è la stessa cosa di "al minuto zero di ogni ora". Se il lavoro impiega dodici minuti, la prossima esecuzione viene contata dal momento dell'attivazione, non dalla tua aspettativa sull'orologio.
Pensa anche alla sovrapposizione. Per un'unità di servizio normale, systemd non avvierà una seconda copia della stessa unità attiva solo perché è arrivato il prossimo evento timer. Se il tuo lavoro può durare più a lungo del suo intervallo, decidi se è accettabile. A volte la risposta giusta è un lock nello script, come flock, perché può produrre un chiaro messaggio "esecuzione precedente ancora attiva". A volte la risposta giusta è allungare l'intervallo.
Abitudini Operative che Fanno Risparmiare Tempo
La vista dei timer è la tua prima dashboard:
systemctl list-timers --all
Mostra l'ultima esecuzione, la prossima esecuzione e l'unità che ogni timer attiva. Se il timer è elencato ma il servizio non viene mai eseguito, controlla l'espressione calendario e se il timer è abilitato. Se il servizio viene eseguito e fallisce, ignora il timer per un momento e ispeziona il servizio:
systemctl status backup-app.service
journalctl -u backup-app.service --since today
Quando modifichi uno dei file unit, esegui:
sudo systemctl daemon-reload
sudo systemctl restart backup-app.timer
Riavviare il timer dopo le modifiche alla pianificazione è una buona abitudine perché fa sì che il prossimo tempo di attivazione si aggiorni immediatamente. Se hai modificato solo lo script stesso, di solito non hai bisogno di daemon-reload.
Per i timer utente, usa systemctl --user e posiziona le unità sotto ~/.config/systemd/user/. Sono utili per workstation di sviluppatori e automazione per utente, ma hanno un avvertimento importante: per impostazione predefinita, i servizi utente sono legati alla sessione di login dell'utente. Se hai bisogno che un timer utente continui a funzionare dopo il logout, abilita il lingering con loginctl enable-linger username. Questa è una scelta amministrativa deliberata, non qualcosa da nascondere nell'articolo come una soluzione magica.
Quando Cron è Ancora lo Strumento Migliore
Non spostare tutto ciecamente. Cron è più facile da leggere per piccole attività locali all'utente, specialmente su server più vecchi o container minimi dove systemd non è PID 1. Se il tuo unico requisito è "esegui questo comando innocuo ogni cinque minuti", cron potrebbe essere la risposta più chiara.
I timer di systemd danno i loro frutti quando il lavoro ha esigenze simili a un servizio: identità controllata, log nel journal, limiti di risorse, dipendenze, comportamento di recupero o distribuzione standard tramite file unit. In pratica, mi rivolgo ai timer quando l'attività pianificata sveglierebbe qualcuno se fallisse. Il file unit extra vale la pena quando dà al prossimo operatore un percorso diretto da "cosa è stato eseguito?" a "cosa è fallito?" a "cosa è cambiato?".
Un'ultima abitudine vale la pena di adottare durante le migrazioni: tieni la vecchia voce cron commentata nelle vicinanze solo fino a quando il timer non è stato eseguito con successo alcune volte, poi rimuovila. Le pianificazioni duplicate sono una fonte silenziosa di danni. Due lavori di backup possono competere per lo stesso lock, due lavori di pulizia possono cancellare file prima del previsto e due lavori di report possono inviare email duplicate. Dopo aver abilitato il timer, controlla systemctl list-timers --all, conferma il journal del servizio e assicurati che il vecchio percorso cron non sia più attivo.