Risoluzione dei Problemi di Fallimento dei Servizi Systemd: Una Guida Passo-Passo
Diagnostica i fallimenti dei servizi systemd con controlli dello stato, log del journal, revisione dei file unit, correzione delle dipendenze e debug dell'ambiente.
Risoluzione dei Problemi di Fallimento dei Servizi Systemd: Una Guida Passo-Passo
I fallimenti dei servizi systemd sono più facili da diagnosticare quando si procede con calma e si seguono le prove. Un'unità fallita di solito lascia tre indizi utili: lo stato registrato da systemd, il comando che ha tentato di eseguire e i log scritti da systemd o dall'applicazione. Se li leggi in ordine, eviti la trappola comune di modificare un file unit prima di sapere se il problema è l'unità, l'applicazione, una dipendenza o l'host.
Gli esempi seguenti utilizzano un fittizio mywebapp.service, ma lo stesso flusso di lavoro si applica a helper di database, consumatori di code, job di backup, esportatori e demoni interni.
La Prima Linea di Difesa: systemctl status
Quando un servizio non si avvia, il primo comando da eseguire è systemctl status <nome_servizio>. Questo comando fornisce un'istantanea dello stato corrente del servizio, inclusi se è attivo, caricato e, cosa cruciale, un estratto dei suoi log recenti. Questo spesso fornisce informazioni sufficienti per identificare rapidamente il problema.
Supponiamo che il tuo servizio applicativo web, mywebapp.service, non si avvii:
systemctl status mywebapp.service
Interpretazione dell'Output di Esempio:
● mywebapp.service - My Web Application
Loaded: loaded (/etc/systemd/system/mywebapp.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Mon 2023-10-26 10:30:05 UTC; 10s ago
Process: 12345 ExecStart=/usr/local/bin/mywebapp-start.sh (code=exited, status=1/FAILURE)
Main PID: 12345 (code=exited, status=1/FAILURE)
CPU: 10ms
Oct 26 10:30:05 hostname systemd[1]: Started My Web Application.
Oct 26 10:30:05 hostname mywebapp-start.sh[12345]: Error: Port 8080 already in use
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Main process exited, code=exited, status=1/FAILURE
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Failed with result 'exit-code'.
Da questo output, possiamo immediatamente vedere:
- Il servizio
mywebapp.serviceèfailed. - È fallito con
Result: exit-code, il che significa che il comandoExecStartè uscito con uno stato diverso da zero. - La riga
Processmostra che il comandomywebapp-start.shè fallito constatus=1/FAILURE. - Fondamentalmente, le righe di log indicano:
Error: Port 8080 already in use. Questo è un chiaro indicatore del problema.
Questo comando è il tuo primo strumento diagnostico, spesso indica direttamente la causa o restringe il campo su dove cercare successivamente.
Approfondire con journalctl
Mentre systemctl status fornisce un riepilogo rapido, journalctl è il comando di riferimento per una registrazione dettagliata. Interroga il journal di systemd, che raccoglie i log da tutte le parti del sistema, inclusi i servizi.
Revisione di Base dei Log
Per visualizzare tutti i log per un servizio specifico, incluse le voci storiche:
journalctl -u mywebapp.service
Questo mostrerà tutte le voci di log associate a mywebapp.service. Se il servizio fallisce ripetutamente, vedrai le voci di ogni tentativo fallito.
Filtraggio e Query Basate sul Tempo
Per restringere i risultati, specialmente dopo un fallimento recente, puoi usare flag come --since e --priority:
- Mostra i log da un momento specifico:
journalctl -u mywebapp.service --since "10 minutes ago" journalctl -u mywebapp.service --since "2023-10-26 10:00:00" - Mostra solo messaggi di livello errore o superiori:
journalctl -u mywebapp.service -p err - Combina con
-xeper spiegazioni estese e output verboso:journalctl -u mywebapp.service -xe --since "5 minutes ago"-xpuò aggiungere testo esplicativo per alcuni messaggi di systemd. Tratta queste spiegazioni come suggerimenti, non come sostituti dei log specifici dell'unità.
Comprendere i Messaggi di Log
Cerca parole chiave come Error, Failed, Warning o messaggi specifici dell'applicazione che indicano cosa è andato storto. Presta attenzione ai timestamp per comprendere la sequenza degli eventi che hanno portato al fallimento.
Suggerimento: Se lo script ExecStart del tuo servizio stampa sull'output standard o sull'errore standard, quei messaggi vengono solitamente catturati da journalctl. Assicurati che i tuoi script registrino messaggi di errore descrittivi.
Ispezionare il File Unit: Il Progetto del Tuo Servizio
Ogni servizio systemd è definito da un file unit (es., mywebapp.service). Le configurazioni errate in questo file sono una fonte comune di fallimenti all'avvio. Devi capire cosa il servizio sta cercando di fare.
Recuperare il File Unit
Per visualizzare il file unit attivo per il tuo servizio:
systemctl cat mywebapp.service
Questo comando mostra il file unit esatto che systemd sta utilizzando, incluse eventuali sostituzioni.
Direttive Chiave da Controllare
Concentrati sulla sezione [Service] per problemi relativi all'esecuzione e su [Unit] per le dipendenze.
ExecStart: Questo è il comando che systemd esegue per avviare il tuo servizio. Verifica che il percorso sia corretto e che il comando stesso sia eseguibile e venga eseguito con successo quando invocato manualmente (es., comeUserspecificato).ExecStart=/usr/local/bin/mywebapp-start.shType: Definisce il tipo di avvio del processo. I tipi comuni includono:simple(predefinito):ExecStartè il processo principale.forking:ExecStartcrea un processo figlio e il processo padre esce. Systemd attende che il processo padre esca.oneshot:ExecStartviene eseguito ed esce; systemd considera il servizio attivo finché il comando è in esecuzione.notify: Il servizio invia una notifica a systemd quando è pronto.- Un
Typeerrato può portare systemd a pensare che un servizio sia fallito quando in realtà è stato avviato, o viceversa.
User/Group: L'utente e il gruppo sotto cui verrà eseguito il servizio. I problemi di autorizzazione spesso derivano dal tentativo del servizio di accedere a file o risorse per cui non ha i diritti sotto questo utente.User=mywebappuser Group=mywebappgroupWorkingDirectory: La directory da cui verrà eseguito il servizio. I percorsi relativi inExecStarto altri comandi dipendono da questo.Restart: Definisce quando il servizio deve essere riavviato. Se impostato suon-failureoalways, un servizio che fallisce potrebbe riavviarsi costantemente, rendendo più difficile catturare il fallimento iniziale.TimeoutStartSec/TimeoutStopSec: Quanto tempo systemd attende che il servizio si avvii o si arresti. Se un servizio impiega più tempo per inizializzare diTimeoutStartSec, systemd lo ucciderà e segnalerà un fallimento.
Problemi Comuni del File Unit
- Percorsi errati: Errore di battitura in
ExecStarto in altri percorsi di file. - Variabili
Environmentmancanti: I servizi spesso richiedono variabili d'ambiente specifiche (es.,PATH) che potrebbero non essere presenti nell'ambiente pulito di systemd (vedi sotto). - Autorizzazioni: L'
Userspecificato non ha i permessi di esecuzione per lo script o i permessi di lettura/scrittura per i file di dati necessari. - Errori di sintassi: Semplici errori di battitura nel file unit stesso.
Per testare ExecStart manualmente:
Passa all'utente del servizio e prova a eseguire il comando direttamente:
sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh
Questo spesso riproduce l'errore visto in journalctl direttamente nel tuo terminale, rendendo il debug più facile.
Gestione delle Dipendenze: Quando i Servizi Non Possono Avviarsi da Soli
I servizi spesso si basano su altri servizi o componenti di sistema per essere attivi prima di poter avviarsi. Systemd utilizza le direttive Wants, Requires, After e Before per gestire queste dipendenze.
Identificare le Dipendenze
Usa systemctl list-dependencies <nome_servizio> per vedere cosa un servizio richiede o vuole esplicitamente per funzionare.
systemctl list-dependencies mywebapp.service
Direttive comuni nella sezione [Unit]:
After=: Specifica che questo servizio dovrebbe avviarsi dopo le unità elencate. Se l'unità elencata fallisce, questo servizio tenterà comunque di avviarsi (a meno che non venga utilizzato ancheRequires=).Requires=: Specifica che questo servizio richiede le unità elencate. Se una qualsiasi delle unità richieste non si avvia, questo servizio non si avvierà.Wants=: Una forma più debole diRequires=. Se un'unità desiderata fallisce, questo servizio tenterà comunque di avviarsi.
Esempio:
[Unit]
Description=My Web Application
After=network.target mysql.service
Requires=mysql.service
Qui, mywebapp.service è ordinato dopo network.target e mysql.service, e richiede che mysql.service sia avviato con successo. Se mysql.service fallisce, mywebapp.service non si avvierà.
Risolvere i Conflitti di Dipendenza
Se un servizio fallisce a causa di un problema di dipendenza, journalctl di solito indicherà quale dipendenza non è stata soddisfatta. Ad esempio, potrebbe indicare Dependency failed for My Web Application seguito da dettagli sul fallimento di mysql.service.
Passaggi per risolvere:
- Controlla il servizio dipendente: Esegui
systemctl status <servizio_dipendente>(es.,systemctl status mysql.service) ejournalctl -u <servizio_dipendente>per diagnosticare prima il suo fallimento. - Verifica le direttive
After=eRequires=: Assicurati che riflettano correttamente l'ordine di avvio desiderato e la rigidità. A volte, un servizio deve attendere che una porta specifica sia aperta, non solo che il job di avvio di un'altra unità sia terminato. Per controlli mirati,ExecStartPre=può aiutare. Per i demoni di rete, l'attivazione del socket o la logica di ripetizione a livello di applicazione sono spesso più affidabili.
Variabili d'Ambiente e Percorsi: Le Insidie Nascoste
I servizi systemd vengono eseguiti in un ambiente molto pulito e minimale. Questo spesso porta a problemi in cui i comandi che funzionano perfettamente nella shell di un utente falliscono quando vengono eseguiti da systemd perché mancano variabili d'ambiente cruciali (come PATH).
L'Ambiente Pulito di Systemd
Quando systemd avvia un servizio, non eredita l'ambiente completo dell'utente che ha avviato systemctl start. La variabile PATH, ad esempio, è spesso ridotta, il che significa che comandi come python o node potrebbero non essere trovati se non si trovano in posizioni standard come /usr/bin o /bin.
Sintomo: ExecStart=/usr/local/bin/myscript.sh fallisce con python: command not found, node: command not found, un errore di libreria mancante o un messaggio dell'applicazione che dice che un'impostazione richiesta è vuota.
Correzione: Rendi esplicito l'ambiente del servizio.
[Service]
WorkingDirectory=/opt/mywebapp
Environment="APP_ENV=production"
Environment="PATH=/opt/mywebapp/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
ExecStart=/opt/mywebapp/venv/bin/gunicorn app:app
Per molte variabili, usa un file di ambiente:
[Service]
EnvironmentFile=/etc/mywebapp/mywebapp.env
ExecStart=/opt/mywebapp/bin/server
Mantieni quel file semplice. EnvironmentFile= non è uno script Bash. Usa righe KEY=value, non export KEY=value, sostituzione di comandi o condizionali di shell. Imposta anche permessi restrittivi se il file contiene segreti:
sudo chown root:mywebapp /etc/mywebapp/mywebapp.env
sudo chmod 0640 /etc/mywebapp/mywebapp.env
Autorizzazioni: Riprodurre il Fallimento come Utente del Servizio
I problemi di autorizzazione sono comuni perché i test manuali spesso avvengono come root o come utente di login, mentre l'unità viene eseguita come account di servizio dedicato.
Controlla l'utente configurato:
systemctl show mywebapp.service -p User -p Group
Quindi esegui lo stesso comando come quell'utente:
sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh
Se l'app ha bisogno di una directory di lavoro, includila:
sudo -u mywebappuser bash -lc 'cd /opt/mywebapp && /usr/local/bin/mywebapp-start.sh'
Guarda oltre l'eseguibile. L'utente del servizio potrebbe aver bisogno di accesso in lettura a /etc/mywebapp/config.yml, accesso in scrittura a /var/lib/mywebapp, accesso in esecuzione su ogni directory padre o il permesso di creare un socket Unix sotto /run/mywebapp. Un controllo rapido può risparmiare molte supposizioni:
sudo -u mywebappuser test -r /etc/mywebapp/config.yml
sudo -u mywebappuser test -w /var/lib/mywebapp
namei -l /var/lib/mywebapp/uploads
Se il servizio fallisce solo quando si lega a una porta bassa come 80 o 443, non eseguirlo immediatamente come root. Un proxy inverso, l'attivazione del socket o una capacità mirata potrebbero essere più sicuri a seconda del servizio.
Limiti di Avvio e Cicli di Riavvio
Un servizio che si blocca ripetutamente potrebbe fermarsi con un messaggio come start request repeated too quickly. Ciò significa che il limite di velocità di systemd è intervenuto. Il fallimento originale è avvenuto prima, quindi non concentrarti solo sul messaggio del limite di velocità.
Usa:
journalctl -u mywebapp.service --since "30 minutes ago"
systemctl show mywebapp.service -p NRestarts -p Restart -p StartLimitBurst -p StartLimitIntervalUSec
Dopo aver risolto la causa principale, cancella lo stato di fallimento:
sudo systemctl reset-failed mywebapp.service
sudo systemctl start mywebapp.service
Fai attenzione con Restart=always. È utile per demoni resilienti, ma durante il debug può inondare il journal e nascondere il primo errore chiaro. Puoi fermare temporaneamente l'unità, rivedere i log e avviarla manualmente una volta che hai cambiato una cosa.
Convalidare l'Unità Prima di Ricaricare
Prima di riavviare un servizio dopo aver modificato un file unit, convalida il file e ricarica systemd:
sudo systemd-analyze verify /etc/systemd/system/mywebapp.service
sudo systemctl daemon-reload
sudo systemctl restart mywebapp.service
Se il servizio ha sostituzioni drop-in, ispeziona la versione unita:
systemctl cat mywebapp.service
systemctl show mywebapp.service -p FragmentPath -p DropInPaths -p ExecStart
Questo cattura i casi imbarazzanti: hai modificato un file sotto /usr/lib/systemd/system, ma un drop-in sotto /etc/systemd/system/mywebapp.service.d/override.conf cambia ancora ExecStart; o hai corretto un file unit copiato che non è quello caricato da systemd.
Un Ordine Pratico delle Operazioni
Quando un servizio di produzione è giù, usa un ciclo breve e ripetibile:
- Esegui
systemctl status mywebapp.service --no-pager. - Leggi
journalctl -u mywebapp.service --since "15 minutes ago". - Ispeziona
systemctl cat mywebapp.service. - Controlla il comando, l'utente, la directory di lavoro, l'ambiente e le dipendenze.
- Riproduci il comando come utente del servizio.
- Apporta una modifica.
- Esegui
systemctl daemon-reloadse l'unità è cambiata. - Riavvia e controlla di nuovo il journal.
Quest'ordine mantiene l'indagine concreta. Se il journal dice Permission denied, correggi le autorizzazioni. Se dice No such file or directory, controlla i percorsi dal punto di vista di systemd. Se dice Dependency failed, esegui il debug della dipendenza per primo. Se dice che il processo è uscito con stato 0/SUCCESS ma il servizio è fallito, controlla Type= e se l'applicazione si demonizza o esce immediatamente.
L'obiettivo non è memorizzare ogni direttiva di systemd. È continuare a far corrispondere il messaggio di fallimento al livello che lo ha prodotto.