Padroneggiare i file di servizio di Systemd: una guida completa
Crea file di servizio systemd affidabili con sezioni unità corrette, comportamento di riavvio, log, sicurezza e timer.
Padroneggiare i File di Servizio Systemd: Una Guida Completa
I file di servizio systemd dicono a Linux come avviare, fermare, riavviare e supervisionare la tua applicazione. Se il tuo servizio si avvia manualmente ma fallisce all'avvio, si riavvia in modo troppo aggressivo o scrive i log nella posizione sbagliata, di solito è il file unità che devi controllare.
Questa guida si concentra sulla creazione e configurazione dei file unità di servizio systemd da zero. Vedrai le sezioni principali, un esempio funzionante di servizio Python, comandi comuni per la risoluzione dei problemi e alcuni controlli di sicurezza e risorse utili in produzione.
Comprendere i File Unità Systemd
Systemd utilizza file unità per descrivere varie risorse di sistema come servizi, socket, dispositivi, punti di mount e altro. Un file unità di servizio, che tipicamente termina con l'estensione .service, definisce come systemd dovrebbe gestire un demone o un'applicazione specifica.
Questi file sono organizzati in sezioni, con ogni sezione contenente coppie chiave-valore che rappresentano direttive di configurazione. Le sezioni principali su cui ci concentreremo sono [Unit], [Service] e [Install].
Anatomia di un File di Servizio Systemd
Un tipico file di servizio systemd ha la seguente struttura:
[Unit]
Description=Una breve descrizione del servizio.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my_application --config /etc/my_app.conf
Restart=on-failure
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
Analizziamo ogni sezione e le sue direttive comuni:
La Sezione [Unit]
Questa sezione fornisce metadati sull'unità e definisce la sua relazione con altre unità. Viene utilizzata per dipendenze e ordinamento.
Description=: Un nome leggibile dall'uomo per il servizio. Questo è ciò che vedrai nell'output disystemctl status.Documentation=: URL o percorsi alla documentazione per il servizio.Requires=: Definisce dipendenze forti. Se un'unità elencata qui non riesce ad avviarsi, anche questa unità non riuscirà ad avviarsi.Wants=: Definisce dipendenze deboli. Se un'unità elencata qui non riesce ad avviarsi, questa unità tenterà comunque di avviarsi.Before=: Assicura che questa unità si avvii prima delle unità elencate.After=: Controlla solo l'ordinamento. Ad esempio,After=network.targetavvia questa unità dopo il target di rete di base, ma non garantisce la connettività esterna. I servizi dipendenti dalla rete potrebbero aver bisogno diAfter=network-online.targetpiù il servizio wait-online della distribuzione.Conflicts=: Se un'unità elencata qui viene avviata, questa unità verrà fermata e viceversa.
La Sezione [Service]
Questa sezione configura il comportamento del servizio stesso. È qui che definisci come avviare, fermare e gestire il processo.
Type=: Specifica il tipo di avvio del processo. I valori comuni includono:simple(predefinito): Il processo principale è quello specificato inExecStart=. Systemd presume che il servizio sia avviato immediatamente dopo che il processoExecStart=è stato biforcato.forking: Il processoExecStart=biforca un figlio e il genitore esce. Systemd considera il servizio avviato quando il genitore esce. Spesso è necessario specificarePIDFile=con questo tipo.oneshot: Simile asimple, ma si prevede che il processo esca dopo aver completato il suo lavoro. Utile per script di configurazione.notify: Il demone invia un messaggio di notifica a systemd quando è stato avviato con successo. Questo è il tipo preferito per i demoni moderni che lo supportano.dbus: Il servizio acquisisce un nome D-Bus.
ExecStart=: Il comando da eseguire per avviare il servizio. Per la maggior parte dei tipi di servizio, usa un comandoExecStart=. Più righeExecStart=sono valide perType=oneshot, dove vengono eseguite in sequenza.ExecStop=: Il comando da eseguire per fermare il servizio.ExecReload=: Il comando da eseguire per ricaricare la configurazione del servizio senza riavviarlo.Restart=: Definisce quando il servizio deve essere riavviato automaticamente. Valori comuni:no(predefinito): Non riavviare mai.on-success: Riavvia solo se il servizio esce correttamente (codice di uscita 0).on-failure: Riavvia se il servizio esce con un codice di uscita diverso da zero, viene terminato da un segnale o va in timeout.on-abnormal: Riavvia se terminato da un segnale o va in timeout.on-abort: Riavvia solo se terminato in modo non pulito da un segnale.always: Riavvia sempre, indipendentemente dallo stato di uscita.
RestartSec=: Il tempo di attesa prima di riavviare il servizio (predefinito 100ms).User=: L'utente con cui eseguire il servizio.Group=: Il gruppo con cui eseguire il servizio.WorkingDirectory=: La directory in cui spostarsi prima di eseguire i comandi.Environment=: Imposta le variabili d'ambiente per il servizio.EnvironmentFile=: Legge le variabili d'ambiente da un file.PIDFile=: Percorso al file PID (spesso usato conType=forking).StandardOutput=/StandardError=: Controlla dove vanno stdout e stderr, comejournal,nulloinherit. Sulle distribuzioni comuni basate su systemd, l'output del servizio finisce normalmente nel journal a meno che le impostazioni predefinite del manager non siano state modificate.
La Sezione [Install]
Questa sezione definisce come l'unità deve essere abilitata o disabilitata, tipicamente creando collegamenti simbolici.
WantedBy=: Specifica il target che dovrebbe "volere" questo servizio quando è abilitato. Valori comuni:multi-user.target: Per servizi che dovrebbero avviarsi quando il sistema raggiunge uno stato a riga di comando multiutente.graphical.target: Per servizi che dovrebbero avviarsi quando il sistema raggiunge uno stato di login grafico.
Creare il Tuo Primo File di Servizio Systemd
Creiamo un semplice file di servizio per un ipotetico script Python chiamato my_app.py situato in /opt/my_app/my_app.py.
1. Crea il file di servizio:
I file di servizio per applicazioni personalizzate sono tipicamente collocati in /etc/systemd/system/. Chiamiamo il nostro file my_app.service.
# Crea la directory se non esiste
sudo mkdir -p /etc/systemd/system/
# Crea il file di servizio usando un editor di testo
sudo nano /etc/systemd/system/my_app.service
2. Aggiungi il seguente contenuto a my_app.service:
[Unit]
Description=La Mia Applicazione Python Personalizzata
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/my_app/
ExecStart=/usr/bin/python3 /opt/my_app/my_app.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Spiegazione dell'esempio:
Description: Identifica chiaramente la nostra applicazione.After=network.target: Assicura che la rete sia disponibile prima di avviarsi.Type=simple: Presume chemy_app.pysia il processo principale e non biforca.User=appuser,Group=appgroup: Specifica l'utente e il gruppo con cui l'applicazione deve essere eseguita. Assicurati che questi utenti e gruppi esistano sul tuo sistema e abbiano i permessi appropriati. Potresti doverli creare:sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/my_app/WorkingDirectory: Imposta il contesto per lo script.ExecStart: Il comando per eseguire lo script Python. Assicurati che/usr/bin/python3sia il percorso corretto per il tuo interprete Python e che lo script sia eseguibile.Restart=on-failure: Se lo script si blocca, systemd proverà a riavviarlo.WantedBy=multi-user.target: Questo servizio verrà avviato automaticamente all'avvio del sistema in un ambiente multiutente.
3. Ricarica la configurazione del manager systemd:
Dopo aver creato o modificato un file di servizio, devi dire a systemd di ricaricare la sua configurazione.
sudo systemctl daemon-reload
4. Abilita e Avvia il Servizio:
- Abilita: Questo fa sì che il servizio si avvii automaticamente all'avvio.
sudo systemctl enable my_app.service - Avvia: Questo avvia il servizio immediatamente.
sudo systemctl start my_app.service
5. Controlla lo Stato del Servizio:
Per verificare se il tuo servizio è in esecuzione e vedere eventuali errori:
sudo systemctl status my_app.service
Se ci sono problemi, il comando status mostrerà spesso messaggi di errore o log da journald.
6. Visualizzare i Log:
Systemd si integra con journald per la registrazione. Puoi visualizzare i log per il tuo servizio usando:
sudo journalctl -u my_app.service
Puoi anche seguire i log in tempo reale:
sudo journalctl -f -u my_app.service
Altri Comandi Utili
- Ferma il servizio:
sudo systemctl stop my_app.service - Riavvia il servizio:
sudo systemctl restart my_app.service - Ricarica la configurazione (se supportato dall'app):
sudo systemctl reload my_app.service - Disabilita l'avvio automatico all'avvio:
sudo systemctl disable my_app.service
Configurazione Avanzata e Migliori Pratiche
Considerazioni sulla Sicurezza
- Esegui i servizi come utenti non root: Specifica sempre
User=eGroup=a meno che non sia assolutamente necessario. Questo segue il principio del minimo privilegio. - Isola i servizi: Considera funzionalità di sandboxing come
PrivateTmp=true,ProtectSystem=strict,ProtectHome=trueeNoNewPrivileges=true. Testali con la tua applicazione perché possono bloccare scritture di file legittime.PrivateTmp=true: Dà al servizio le proprie directory/tmpe/var/tmpprivate.ProtectSystem=strict: Rende la maggior parte del filesystem in sola lettura per il servizio. UsaReadWritePaths=per le directory in cui il servizio deve scrivere.NoNewPrivileges=true: Impedisce al servizio di ottenere nuovi privilegi.
Gestire Avvii Complessi
Type=forkingconPIDFile=: Per applicazioni più vecchie che biforcano, assicurati chePIDFile=punti al file corretto.Type=notify: Se la tua applicazione lo supporta, questo è il modo più robusto per systemd di sapere quando è veramente pronta.ExecStartPre=eExecStartPost=: Comandi da eseguire prima e dopoExecStart=. Utili per attività di configurazione o pulizia.
Controllo delle Risorse
Systemd ti permette di limitare l'uso delle risorse:
CPUWeight=: Peso relativo della CPU per il servizio.MemoryMax=: Memoria massima che il servizio può utilizzare.IOWeight=: Peso relativo I/O dove supportato dal kernel e dalla configurazione cgroup.
Esempio:
[Service]
# ... altre direttive ...
MemoryMax=512M
CPUWeight=50
Timer vs. Cron
I timer di systemd offrono un'alternativa moderna ai tradizionali lavori cron. Sono più flessibili e si integrano meglio con la gestione dei log e delle dipendenze di systemd.
- Cron: Attività pianificate definite nei file
crontab. - Timer Systemd (unità
.timer): Queste unità pianificano unità.service. Definisci un file.timerche specifica quando deve essere eseguito un corrispondente file.service.
Esempio:
Per eseguire uno script ogni giorno alle 3:00:
my_script.service: Il servizio da eseguire.[Unit] Description=Il mio script giornaliero [Service] Type=oneshot ExecStart=/opt/my_scripts/run_daily.sh User=scriptusermy_script.timer: Il timer che pianifica il servizio.[Unit] Description=Esegui il mio script giornaliero una volta al giorno [Timer] # Esegui alle 03:00 ogni giorno OnCalendar=*-*-* 03:00:00 # Esegui subito dopo l'avvio se l'orario pianificato è stato perso mentre la macchina era spenta. Persistent=true [Install] WantedBy=timers.target
Per usarlo:
- Metti entrambi i file in
/etc/systemd/system/. - Esegui
sudo systemctl daemon-reload. - Abilita e avvia il timer:
sudo systemctl enable my_script.timeresudo systemctl start my_script.timer.
I timer offrono vantaggi come Persistent=true (esegue i lavori persi all'avvio), eventi calendario (come hourly, daily, weekly) e una migliore integrazione con journalctl.
Risoluzione dei Problemi Comuni
- Il servizio non si avvia: Controlla
systemctl status <nome_servizio>ejournalctl -u <nome_servizio>. Cerca errori di battitura, percorsi errati, dipendenze mancanti o errori di permesso. Type=errato: Se un servizio fallisce immediatamente o si blocca, ilType=potrebbe essere sbagliato. Provasimpleoforkinge assicurati chePIDFilesia corretto se usiforking.- Permesso negato: Assicurati che
User=eGroup=specificati abbiano accesso in lettura/scrittura ai file e alle directory necessari. - Variabili d'ambiente: Se la tua applicazione si basa su variabili d'ambiente specifiche, assicurati che siano impostate correttamente usando
Environment=oEnvironmentFile=. - Dipendenze: Verifica che
After=,Wants=eRequires=corrispondano a ciò che intendi.After=ordina l'avvio; non tira un'altra unità da sola.
Prima di abilitare una nuova unità su un host di produzione, esegui:
sudo systemd-analyze verify /etc/systemd/system/my_app.service
Questo rileva molti errori di sintassi e direttive prima che tu faccia affidamento sul servizio all'avvio.
Punto Chiave
Scrivi il file di servizio più piccolo che descriva accuratamente la tua app, poi aggiungi deliberatamente politica di riavvio, logging, restrizioni di sicurezza e limiti di risorse. Dopo ogni modifica, esegui systemctl daemon-reload, verifica l'unità e controlla systemctl status più journalctl -u prima di fidartene in produzione.