Timer di systemd: guida pratica per automatizzare attività su Linux
Eseguire automaticamente un backup, avviare uno script ogni notte, pulire una directory oppure eseguire periodicamente un’attività di manutenzione sono operazioni molto comuni in un sistema Linux.
Tradizionalmente, uno degli strumenti più utilizzati per questo scopo è stato cron.
systemd mette però a disposizione un meccanismo integrato nel proprio sistema di Unit: i Timer.
Una Unit di tipo .timer permette di stabilire quando deve essere attivata un’altra Unit, normalmente un servizio .service.
In questo modo è possibile costruire automazioni che utilizzano gli stessi strumenti che abbiamo già incontrato negli articoli precedenti: Unit, servizi, dipendenze, systemctl e journalctl.
In questa guida vedremo cosa sono i Timer, come sono strutturati e come creare un semplice esempio funzionante.
Cosa imparerai
Al termine dell’articolo saprai:
- che cosa sono i Timer di systemd;
- perché un .timer lavora normalmente insieme a un .service;
- come eseguire un’attività dopo un determinato intervallo;
- come pianificare un’attività a un’ora specifica;
- come attivare e verificare un Timer;
- che cosa significa Persistent=true;
- quando può essere conveniente utilizzare un Timer al posto di cron.
Che cos’è un Timer di systemd?
Un Timer è una particolare Unit con estensione:
.timer
Il suo compito non è eseguire direttamente un comando, ma attivare un’altra Unit quando si verifica una determinata condizione temporale.
Nella configurazione più comune avremo quindi due file:
mio-task.service
mio-task.timer
Il primo contiene l’attività da eseguire.
Il secondo stabilisce quando eseguirla.
Per convenzione, quando un Timer si chiama backup.timer, systemd cerca automaticamente una Unit backup.service da attivare.
È comunque possibile indicare esplicitamente un’altra Unit tramite Unit=.
Possiamo quindi rappresentare il meccanismo in questo modo:
TIMER
│
│ quando?
▼
SERVICE
│
│ cosa fare?
▼
COMANDO
È una distinzione importante: il Timer decide quando eseguire qualcosa, mentre il Service descrive cosa deve essere eseguito.
Il nostro primo esempio
Realizziamo un esempio molto semplice: uno script che scrive la data e l’ora dell’esecuzione in un file di log.
Creiamo lo script:
sudo nano /usr/local/bin/systemd-timer-demo.sh
Inseriamo:
#!/bin/bash
echo "Esecuzione: $(date)" >> /var/log/systemd-timer-demo.log
Rendiamo eseguibile lo script:
sudo chmod +x /usr/local/bin/systemd-timer-demo.sh
Ora creiamo il relativo Service:
sudo nano /etc/systemd/system/systemd-timer-demo.service
Il contenuto sarà:
[Unit]
Description=Esempio di servizio attivato da un Timer systemd
[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-timer-demo.sh
In questo caso utilizziamo:
Type=oneshot
perché il nostro programma viene eseguito, svolge il proprio compito e termina.
Non abbiamo bisogno di un processo che rimanga continuamente in esecuzione.
Creiamo il Timer
Adesso possiamo creare la Unit che stabilirà quando eseguire il servizio:
sudo nano /etc/systemd/system/systemd-timer-demo.timer
Inseriamo:
[Unit]
Description=Esegue periodicamente il servizio di esempio
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
Persistent=true
[Install]
WantedBy=timers.target
Abbiamo introdotto tre direttive importanti.
OnBootSec=
Stabilisce un intervallo relativo all’avvio del sistema.
Nel nostro esempio:
OnBootSec=5min
significa che il Timer entrerà in azione cinque minuti dopo il boot.
OnUnitActiveSec=
Permette di definire un intervallo relativo all’ultima attivazione della Unit associata.
Nel nostro esempio:
OnUnitActiveSec=1h
chiede di eseguire nuovamente il servizio a intervalli di un’ora.
Persistent=
Questa direttiva è particolarmente interessante.
Con:
Persistent=true
systemd memorizza su disco l’ultima attivazione del Timer. Se il computer era spento nel momento in cui sarebbe dovuta avvenire un’esecuzione di un Timer basato su OnCalendar=, al successivo avvio systemd può eseguire l’attività persa. Questa opzione ha effetto sui Timer basati su calendario.
Per questo motivo Persistent=true è molto utile, ad esempio, per attività di manutenzione o backup su macchine che possono rimanere spente.
Ricaricare systemd e avviare il Timer
Come abbiamo già visto quando abbiamo creato il nostro primo servizio, dopo aver aggiunto nuove Unit è necessario ricaricare la configurazione:
sudo systemctl daemon-reload
A questo punto possiamo attivare il Timer:
sudo systemctl enable --now systemd-timer-demo.timer
L’opzione –now permette di abilitarlo per l’avvio automatico e di avviarlo immediatamente.
Possiamo verificare che sia attivo con:
systemctl status systemd-timer-demo.timer
Vedere tutti i Timer attivi
Uno dei comandi più utili è:
systemctl list-timers --all
L’output contiene informazioni molto interessanti, tra cui:
- prossima esecuzione;
- tempo rimanente;
- ultima esecuzione;
- nome del Timer;
- servizio che verrà attivato.
È quindi uno dei primi comandi da utilizzare quando vogliamo capire se un’attività automatizzata è stata effettivamente pianificata.
Pianificare un’attività a un’ora precisa con OnCalendar=
I Timer diventano particolarmente interessanti quando utilizziamo:
OnCalendar=
Questa direttiva permette di utilizzare espressioni basate sul calendario.
Ad esempio:
OnCalendar=daily
esegue l’attività quotidianamente.
Possiamo anche specificare un orario:
OnCalendar=*-*-* 03:00:00
In questo caso l’attività viene pianificata ogni giorno alle 03:00.
Oppure possiamo utilizzare espressioni come:
OnCalendar=weekly
o:
OnCalendar=monthly
La sintassi di OnCalendar è molto più ricca e permette di descrivere date, giorni della settimana e orari in maniera estremamente flessibile. systemd mette inoltre a disposizione systemd-analyze calendar per verificare e normalizzare un’espressione prima di inserirla nel Timer.
Ad esempio:
systemd-analyze calendar "Mon *-*-* 03:00:00"
è un ottimo modo per controllare quando verrà eseguita una determinata espressione.
Un esempio più realistico: un backup giornaliero
A questo punto possiamo sostituire il nostro semplice script con qualcosa di più utile.
Immaginiamo di avere uno script:
/usr/local/bin/backup.sh
che esegue un backup con rsync.
Il relativo Service potrebbe essere:
[Unit]
Description=Backup automatico
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
e il Timer:
[Unit]
Description=Backup giornaliero
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
Il risultato è molto semplice da comprendere:
systemd
│
▼
backup.timer
│
ogni giorno
alle 02:00
│
▼
backup.service
│
▼
backup.sh
│
▼
rsync
In questo modo abbiamo separato completamente la pianificazione dall’attività da eseguire.
Ed è proprio questa separazione che rende i Timer di systemd molto flessibili.
Timer di systemd o cron?
Non significa che cron sia diventato inutile.
Cron rimane uno strumento semplice, maturo e ancora molto utilizzato.
I Timer di systemd hanno però un vantaggio importante: sono integrati direttamente nell’ecosistema systemd.
Questo significa che possiamo utilizzare insieme:
- systemctl per amministrarli;
- journalctl per analizzarne i log;
- dipendenze tra Unit;
- Target;
- utenti e permessi;
- le numerose possibilità offerte dai Service.
Inoltre, systemctl list-timers permette di avere una visione centralizzata delle attività pianificate.
Per un sistema già basato su systemd, quindi, un Timer rappresenta spesso una soluzione naturale per nuove automazioni.
I comandi da ricordare
| Operazione | Comando |
| ------------------------ | ---------------------------------------- |
| Elencare i Timer | `systemctl list-timers` |
| Elencare tutti i Timer | `systemctl list-timers --all` |
| Avviare un Timer | `sudo systemctl start nome.timer` |
| Abilitare un Timer | `sudo systemctl enable nome.timer` |
| Abilitare e avviare | `sudo systemctl enable --now nome.timer` |
| Verificarne lo stato | `systemctl status nome.timer` |
| Fermare un Timer | `sudo systemctl stop nome.timer` |
| Disabilitare un Timer | `sudo systemctl disable nome.timer` |
| Analizzare le esecuzioni | `journalctl -u nome.service` |
Errori comuni
Quando si iniziano a utilizzare i Timer è facile commettere alcuni errori.
Dimenticare il Service
Un .timer normalmente deve attivare una Unit, generalmente un .service.
Usare start pensando di abilitarlo al boot
Come per i servizi, anche per i Timer:
systemctl start
hanno scopi differenti.
Per entrambe le operazioni possiamo utilizzare:
sudo systemctl enable --now nome.timer
Dimenticare daemon-reload
Dopo aver creato o modificato una Unit:
sudo systemctl daemon-reload
rimane un passaggio fondamentale.
Non controllare il Timer
Dopo averlo configurato, utilizziamo:
systemctl list-timers --all
e:
systemctl status nome.timer
per verificare che sia stato effettivamente caricato e pianificato.
Conclusioni
I Timer rappresentano uno degli aspetti più interessanti di systemd perché permettono di trasformare un semplice servizio in un’attività automatizzata.
Il concetto fondamentale da ricordare è semplice:
Il .timer decide quando eseguire qualcosa; il .service decide cosa eseguire.
Da questa combinazione possiamo costruire automazioni molto più elaborate: backup, manutenzione, sincronizzazioni, script periodici e attività di monitoraggio.
Abbiamo visto soltanto le basi. Nelle configurazioni più avanzate entrano in gioco anche OnBootSec=, OnUnitActiveSec=, OnCalendar=, Persistent=, RandomizedDelaySec= e molte altre direttive.
Nel prossimo articolo continueremo a occuparci dell’avvio e delle prestazioni del sistema con systemd-analyze, uno strumento che permette di capire quanto tempo impiega Linux ad avviarsi e quali Unit contribuiscono maggiormente al tempo complessivo del boot.
LICENZA E CONDIZIONI D’USO
Questo How-to è rilasciato sotto licenza Creative Commons Attribution-NonCommercial 3.0 Italy.
CONTATTI DELL’AUTORE
Marco Ciammella marco@orvietolinux.it
Orvieto Linux User Group info@orvietolinux.it