Aggiungere un timeout ad un servizio di systemd

Systemd pare forse troppo veloce ad avviare i server su cui lavoro, dei DELL PowerEdge T110-II con dischi SATA in mirror.
Me ne sono accorto perché, all’avvio del server, il database postgresql non veniva avviato ed il relativo servizio di systemd era in stato «failed». Nei log di postgresql non c’erano errori, ma il database era spento.
Avviare postgresql via systemd da linea di comando ha sempre funzionato correttamente, ma ovviamente questo avviene solo dopo aver avviato il sistema, quando la macchina è meno carica. Difatti all’avvio, systemd fa partire in parallelo tutti i servizi che non hanno dipendenze tra loro. Tutti questi, partendo, leggono dal disco, il file system risponde quindi più lentamente del normale, e postgresql va in timeout.

Il timeout predefinito per il servizio postgresql è di 90 secondi, come si può vedere dai parametri di postgresql@9.4-main.service, che è il servizio del quale stiamo parlando.

# systemctl show postgresql@9.4-main.service | grep -i timeouts
TimeoutStartUSec=1min 30s
TimeoutStopUSec=1min 30s

In realtà non si tratta di timeout specifici di postgresql, ma di quelli generici di systemd, difatti nella configurazione generale di systemd s’è scritto:

# grep -i timeou /etc/systemd/system.conf
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s

Nelle «unit» per i servizi, si possono utilizzare tre parametri per il timeout: TimeoutStartSec, TimeoutStopSec, oppure TimeoutSec. Il primo è quello dell’avvio del servizio, il secondo dell’arresto, il terzo è comodo se si vuole impostare lo stesso timeout per entrambi. Nel caso systemd non riceva il segnale di avvenuto avvio entro TimeoutStartSec, il servizio viene arrestato e il suo stato cambiato in «failed».

Oltre al timeout di systemd, c’è anche quello interno di postgresql, che si può mettere nella variabile PGCTLTIMEOUT (per Debian, su altre distribuzioni questa variabile potrebbe avere nomi diversi). Il valore predefinito di questo timeout è di 60 secondi (cfr: pagina di manuale di pg_ctl). Quindi, nel mio caso, ipotizzo che il database non si avviasse entro i 60 secondi.

A questo punto decido di impostare le due variabili nella «unit» del servizio di postresql. Nel mio caso si tratta di una «unit» istanziata, vale a dire che con il solo programma postgresql, ci possono essere più istanza del servizio, una per ogni cluster di postgresql. Nel mio caso l’istanza si chiama «9.4-main» e difatti il servizio è «postgresql@9.4-main.service».
Systemd vuole che le «unit» per i servizi istanziati abbiano, nel loro nome, il carattere chiocciolina. La «unit» è quindi /lib/systemd/system/postgresql@.service.

Potrei quindi modificare direttamente quel file, ma si tratta di un file che arriva con i pacchetti Debian di postgtresql e potrebbe essere aggiornato con nuove versioni del pacchetto, quindi sfrutto una caratteristica di systemd che permette di creare delle configurazioni aggiuntive che possono specificare parametri da aggiungere o modificare delle «unit». La documentazione (cfr: pagina di manuale di systemd.unit) dice che si possono creare delle directory foo.service.d/ e metterci dei file con estensione «conf» che contengono le modifiche alle «unit» con lo stesso nome. Queste directory possono essere in vari posti, ma nel mio caso userò /etc/systemd/system perché /etc è deputata alla configurazione gestita direttamente dall’amministratore di sistema.

Ecco quindi il contenuto di /etc/systemd/system/postgresql@.service.d/timeout.conf:

[Service]
# Aggiunti i due time out
# Giuseppe Sacco <giuseppe.sacco@example.org> 2018-08-20
# Numero massimo di secondi che pg_ctl attenderà per l'avvio di postgresql. Notare che
# PGCTLTIMEOUT deve essere minore di TimeoutSec.
# Valore predefinito: 60 secondi
Environment=PGCTLTIMEOUT=270
# Indica a systemd di attendere un certo periodo per l'avvio e arresto di questa «unit»
# Deve essere maggiore di quello impostato con PGCTLTIMEOUT
# Valore predefinito: 90 secondi
TimeoutSec=300s

adesso vediamo se systemd utilizza correttamente i nuovi valori:

# systemctl daemon-reload
# systemctl show postgresql@9.4-main.service | grep -i timeouts
TimeoutStartUSec=5min
TimeoutStopUSec=5min

Tutto OK