Su quasi tutte le distribuzioni moderne i programmi che lavorano in sottofondo, come il server web, il demone SSH o il servizio di stampa, sono gestiti da systemd. Il comando con cui si parla con systemd è systemctl. Bastano una decina di sottocomandi per coprire la gran parte del lavoro quotidiano, e capire la logica delle unità evita di modificare file che al prossimo aggiornamento verranno sovrascritti.
Unità: il concetto di base
systemd ragiona per unità. Ogni unità è descritta da un file di testo e ha un tipo, indicato dall'estensione:
.service: un programma da avviare e tenere sotto controllo;.timer: un'attivazione pianificata di un altro servizio;.mount: un punto di montaggio;.socket: una porta o un socket che avvia il servizio al primo collegamento;.target: un gruppo di unità che rappresenta uno stato del sistema.
Quando scrivi systemctl status ssh l'estensione .service è sottintesa.
Avviare, fermare, riavviare
I comandi di base agiscono subito ma non sopravvivono a un riavvio del computer:
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
La differenza tra restart e reload è importante su un server in produzione. restart ferma il processo e lo fa ripartire, interrompendo le connessioni in corso. reload chiede al programma di rileggere la configurazione senza fermarsi, ma funziona solo se il servizio lo prevede. Per nginx, dopo aver modificato la configurazione, conviene prima verificarla con nginx -t e poi usare reload.
Abilitare all'avvio
Che un servizio parta da solo all'accensione è una cosa diversa dal fatto che sia in esecuzione adesso. Per questo esistono comandi separati:
sudo systemctl enable nginx
sudo systemctl disable nginx
sudo systemctl enable --now nginx
enable --now fa le due cose insieme: abilita all'avvio e avvia subito. Esiste anche mask, più drastico: collega l'unità a /dev/null e impedisce che venga avviata in qualsiasi modo, anche come dipendenza di un'altra. Si annulla con unmask.
Leggere lo stato
systemctl status nome è il primo comando da lanciare quando qualcosa non va. Mostra:
- Loaded: dove si trova il file di unità e se è abilitato (
enabled,disabled,masked); - Active: lo stato attuale, per esempio
active (running),inactive (dead)ofailed, con l'ora dell'ultimo cambiamento; - il PID principale e la memoria usata;
- le ultime righe del registro del servizio.
Per una visione d'insieme sono utili systemctl --failed, che elenca solo le unità fallite, e systemctl list-units --type=service, che mostra tutti i servizi caricati. Le righe di registro complete si leggono con journalctl -u nginx.
Dove stanno i file di unità
| Percorso | Contenuto |
|---|---|
/usr/lib/systemd/system/ (o /lib/systemd/system/) |
unità installate dai pacchetti |
/etc/systemd/system/ |
unità e modifiche dell'amministratore |
~/.config/systemd/user/ |
unità del singolo utente |
La regola d'oro: non modificare i file in /usr/lib. Al prossimo aggiornamento del pacchetto verrebbero rimpiazzati. Per cambiare un'impostazione si usa:
sudo systemctl edit nginx
Il comando crea un file di override in /etc/systemd/system/nginx.service.d/ che contiene solo le righe da cambiare. Per vedere il risultato finale, file originale più modifiche, c'è systemctl cat nginx.
Scrivere un servizio semplice
Supponiamo di avere uno script Python che deve restare sempre attivo, per esempio un piccolo bot che controlla una cartella. Un file /etc/systemd/system/controllo.service può essere così:
[Unit]
Description=Controllo cartella in arrivo
After=network-online.target
[Service]
User=app
WorkingDirectory=/opt/controllo
ExecStart=/usr/bin/python3 /opt/controllo/main.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Le sezioni hanno ruoli precisi. [Unit] descrive il servizio e le sue dipendenze. [Service] dice come eseguirlo: con quale utente, da quale cartella, quale comando, e cosa fare se si chiude con un errore. [Install] indica a quale target agganciarlo quando lo si abilita.
Dopo aver creato o modificato un file di unità bisogna avvisare systemd:
sudo systemctl daemon-reload
sudo systemctl enable --now controllo
Due accorgimenti che nel mio lavoro risolvono metà dei problemi: usare sempre percorsi assoluti in ExecStart, perché systemd non usa il PATH della tua shell, e far girare il servizio con un utente dedicato e non con root.
Errori frequenti
- Dimenticare
daemon-reload: systemd continua a usare la versione precedente del file e lo segnala con un avviso instatus. - Servizio che parte e si chiude subito: spesso il programma va in background da solo. In quel caso serve
Type=forking, oppure, meglio, avviarlo in modalità «in primo piano» se il programma lo consente. - Riavvii a raffica: con
Restart=alwayse un errore di configurazione il servizio entra in un ciclo; systemd lo ferma dopo alcuni tentativi ravvicinati. Il motivo vero si legge injournalctl -u nome. - Variabili d'ambiente mancanti: quelle impostate in
.bashrcnon valgono per i servizi. Si dichiarano conEnvironment=oEnvironmentFile=.
In sintesi
start,stoperestartagiscono ora;enableedisabledecidono cosa succede all'avvio.systemctl statusejournalctl -usono i primi strumenti per capire un problema.- Le modifiche si fanno con
systemctl edit, mai nei file sotto/usr/lib. - Dopo ogni cambiamento a un file di unità serve
systemctl daemon-reload. - Un servizio ben scritto usa percorsi assoluti, un utente dedicato e una politica di riavvio.