Systemd: cosa è successo durante il mio boot? (alternative)

Introduzione

Nel post precedente ho mostrato la capacità di systemd di supervisionare lo stato di esecuzione dei servizi. La domanda a cui voglio rispondere oggi è: esiste qualcosa di simile? La risposta è ni o almeno non esiste nulla di integrato e funzionale a livello di init, ma qualcosa che magari potrebbe essere per voi sufficiente si.

Sysv e OpenRC

Supponiamo il caso più vantaggioso in cui tutti gli script sono capaci di determinare il reale stato del servizio in esecuzione con il comando “/etc/init.d/servizio status”. Non esiste ugualmente un unico comando per conoscere se uno dei servizi è defunto. Openrc porta una piccola miglioria nell’intero universo del sysv, in quanto ci mostra cosa è avvenuto in fase di attivazione dei servizi con il comando:

openrc status

Ma cosa succede se uno dei servizi muore inaspettatamente ed eseguiamo quel comando?

Facciamo un esperimento:

killall -s SIGABRT sshd
openrc status

systemd_openrc

Il risultato non sarà quello sperato, dato che il servizio che abbiamo appena ucciso risulterà essere ancora started. Perché openrc (e sysv) non sono furbi e tentano di eseguire il comando status dello script?

Per rispondere facciamo un altro un piccolo esperimento posizionandoci nella directory “/etc/rc2.d” ed eseguendo il seguente comando:

for i in *; do ./$i status; done

systemd_openrc2

Abbiamo ottenuto quello che vogliamo, eppure perché una soluzione di questo genere non viene resa fruibile per tutti? Queste sono alcune risposte valide:

  • non tutti gli script hanno l’opzione status, specie quelli di software esoterici di terze parti che forniscono script approssimativi per cercare di non dipendere da alcuna distribuzione.
  • il comando è pericoloso, perché possono essere eseguiti script che eseguono comandi anche se non sono stati invocati con il parametro start (provate a dare lo stesso for in bash dalla directory init.d e vedrete i fuochi d’artificio).
  • il controllo è inefficiente perché per avere lo stato generale si deve eseguire il controllo su ogni servizio

Tuttavia, ritengo che nonostante questi difetti, un controllo di questo genere è ugualmente utile al sistemista che deve indagare sullo stato di salute della macchina. Purtroppo non potrà mai essere una soluzione strutturata, ma potete sempre creare la vostra soluzione custom.

Supervisione dei processi

Per ottenere un reale controllo dello stato dei servizi è necessario utilizzare un supervisor.  Come avrete capito systemd ne integra uno, ma ne esistono di diversi. In questo post vogli fare due esempi:

  • runit: è un supervisor che può essere utilizzato come se fosse un servizio, ma lo proveremo utilizzando la sua possibilità di essere al posto dell’usuale init.
  • supervisord: è un programma in python che può essere usato per avviare servizi di ogni natura senza dover prendere posto di init.

Quando un supervisor viene eseguito insieme all’init classico abbiamo un limite: non possiamo spostare tutti i servizi dentro il supervisor perché ciò potrebbe rompere la dipendenza dei servizi di sysv o il funzionamento dell’intero sistema. Ma capiremo questo concetto a tempo dedito.

Runit

Come già anticipato, runit può essere utilizzato al posto di init e qui potete trovare una guida su come fare. I servizi da attivare al boot vengono posti nella directory “/service”.

Quando un servizio deve essere avviato, runit esegue runsv il quale a sua volta lancerà il servizio come progetto figlio. Confusi? Avete ragione! Per questo le immagini sono più esplicative di mille parole.

systemd_runit1

In questo esempio abbiamo una installazione estremamente minimale, dove gli unici servizi attivi sono exim e sshd ognuno monitorato da una istanza di runsv.

Anche in questo caso non esiste un comando globale, ma è sempre possibile utilizzare un for in bash:

cd /system
for i in *; do sv status $i; done

run: exim: (pid 1432) 1740s
run: getty-1: (pid 1430) 1740s
run: sshd: (pid 1506) 1705s

Ogni riga mostra lo stato di esecuzione del servizio, il pid del processo principale e il numero di secondi/minuti/ore dall’ultimo cambio di stato. Queste informazioni sono fornite direttamente dal supervisor, non è dunque necessario preparare alcuno script per reperire queste informazioni.

Infine, la configurazione predefinita di runit fa si che runsv tenti di avviare nuovamente il servizio se il processo muore. Ma attenzione, runsv controlla solo i processi. Se non fate attenzione potreste aggiungere un servizio mal fatto creando una fork bomb:

#!/bin/bash
exec script_che_lancia_daemon_forkato.sh

Per un esempio veloce provate a eseguire uno script di init come se fosse un servizio:

#!/bin/bash
exec /etc/init.d/exim4 start

Inconvenienti a parte, se runit sembra essere così funzionale, perché non viene utilizzato al posto di sysv e soci? La risposta è tanto semplice quanto amara: per via della totale assenza di retrocompatibilità Ogni servizio deve essere convertito (qui potete trovare numerosi esempi) e lo stesso vale per le azioni da intraprendere al boot.

Supervisord

Anche se supervisord non è un init, può essere utilizzato con soddisfazione per supervisionare quasi tutti i servizi che in genere init attiva al boot.

Utilizzando la nostra configurazione minimale, vogliamo spostare sshd ed exim nel supervisor, dobbiamo quindi disattivare prima questi servizi da sysv. Per questa operazione vi consiglio di utilizzare sysv-rc-conf.

systemd_supervisord1

La definizione dei servizi può essere minimale (quasi ermetica) o estremamente dettagliata. Nel nostro esempio ci basta aggiungere le seguenti definizioni:

[program:sshd]
command=/usr/sbin/sshd -D

[program:exmi4]
command=/usr/sbin/exim -bdf -q30m

Il pstree mostrerà i processi e potrete riconoscere una struttura simile a quella vista con runit dove i servizi sono processi figli del supervisore. Inoltre utilizzando il comando:

supervisorctl

possiamo monitorare istantaneamente lo stato di esecuzione dei servizi e operare comandi nella shell interattiva. Tutto fantastico e come volevamo in pratica!

systemd_supervisord2

Ma ora veniamo alle cattive notizie. La prima è l’assenza di retrocompatibilità: non possiamo utilizzare gli script dei servizi creati per la distribuzione, rendendo complicata la migrazione. Inoltre alcuni componenti e servizi abitualmente avviati da init non possono essere spostati in supervisord senza mettere a rischio il sistema di dipendenze della Linux Standard Base (LSB) e/o l’intero funzionamento del sistema.

Quindi non potrete avere un sistema completamente supervisionato come accade in systemd, nonostante supervisord sotto questo aspetto sia più avanzato e completo.

Ma ci sono buone notizie: nessuno vi impedisce di eseguirlo da systemd. Anzi supervisord a breve supporterà in pieno i tanto odiati (non è vero, sono amati da tutti) cgroups in modo da usare la stessa politica di raggruppamento dei processi utilizzata da systemd.

Anche se ora sembra arabo, a breve affronteremo proprio questo argomento. Tenete duro.

Conclusioni

Una delle nozioni che abbiamo appreso da questi esempi è che si è sempre pensato a come migliorare l’esecuzione dei servizi utilizzando la supervisione dei processi. In systemd tutto è integrato e semplice, ma sta a voi scegliere quale soluzione adottare. Di certo c’è che per avere queste funzionalità bisogna estromettere i servizi da suprevisionare da sysv o sostituirlo in toto con qualcosa di meglio.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.