Configurare un monitoraggio server semplice con gli strumenti di sistema

Pubblicato il 21 min di lettura

Uno script di controllo, un timer systemd e una via di notifica davvero provata bastano per un singolo server root. Questa guida mostra che cosa monitorare, come verificare ogni passo e da quando conviene la cassetta degli attrezzi grande.

Un server non si fa vivo da solo quando qualcosa va storto. Continua a funzionare finché non funziona più, e il primo segnale arriva da un cliente oppure da te, quando per caso ci dai un'occhiata. Questa guida costruisce il monitoraggio più piccolo possibile che mette fine a tutto questo: uno script di controllo, un timer systemd, una via di notifica. Nessun database di serie temporali, nessuna dashboard, nessuna porta aperta in più.

I sistemi di riferimento sono Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS e Ubuntu 22.04 LTS. Dove i quattro si comportano in modo diverso, lo trovi indicato espressamente. Tutti i comandi sono scritti per l'uso come root; se lavori come utente normale, anteponi sudo a ogni comando. Questa guida approfondisce il passo 8 della checklist per un nuovo server root.

Che cosa conviene davvero monitorare

L'errore più frequente quando si mette in piedi il primo monitoraggio non è misurare troppo poco, ma misurare troppo. Chi raccoglie 40 indicatori non ne guarda nessuno. Ha senso solo ciò che può fermare il servizio e su cui puoi intervenire. Restano sette punti.

IndicatorePerché merita di stare in elencoDa dove arriva il valoreSoglia sensata
Spazio su discoLa causa di guasto più frequente, e nessuno la vede arrivaredf --output=pcent,targeta partire dall'85%
InodeDisco apparentemente libero, eppure "No space left on device"df --output=ipcent,targeta partire dall'85%
RAM liberaL'OOM killer colpisce raramente il processo che sacrificheresti tuMemAvailable in /proc/meminfosotto i 200 MB
Carico di sistemaSegnala congestione, che la causa sia la CPU o il discoterzo campo di /proc/loadavgmedia a 15 minuti oltre il doppio dei core
Servizi fallitiUn servizio che muore di notte resta altrimenti morto fino al mattinosystemctl is-system-runningtutto tranne running
Scadenza del certificatoBlocca di colpo ogni visitatore, non solo una parteopenssl x509 -checkendmeno di 21 giorni residui
Raggiungibilità dall'esternoÈ l'unico controllo che risponde alla domanda se il server c'è ancorasecondo host, curldue fallimenti consecutivi

Nell'elenco non compare l'utilizzo della CPU in percentuale: un server al 100% perché sta girando un codificatore video lavora esattamente come previsto. Il throughput di rete e il numero di processi mancano per lo stesso motivo. Entrambi aiutano nella ricerca delle cause, ma non funzionano come allarme, perché non esiste un valore oltre il quale devi per forza intervenire.

La via di ritorno, prima che nasca il primo file

Il monitoraggio è un'operazione di sola lettura e di norma non può rompere niente. Tre cose però possono farlo lo stesso.

Uno script che ripara invece di segnalare. L'idea è allettante: se nginx è morto, che sia lo script a riavviarlo. Ne esce un servizio che parte ogni dieci minuti, gira mezzo secondo e nasconde la causa. E uno script che cancella per conto suo quando il disco è pieno, prima o poi cancella qualcosa che serviva. La prima versione si limita a leggere e non chiama né systemctl restart né rm né kill.

Interfacce di rete aperte. Gli exporter di metriche in ascolto su tutti gli indirizzi sono una delle più frequenti pubblicazioni involontarie di dati sui server singoli. La procedura descritta qui non apre nessuna porta e non richiede nessuna regola firewall.

La via di allarme stessa. Un monitoraggio la cui notifica non è mai stata provata non è un monitoraggio, è una bella sensazione. La prova la trovi più avanti e non è facoltativa.

Come raggiungere il server senza SSH

I server root KVM e i server dedicati non hanno né IPMI né iDRAC. Quando SSH non risponde più, l'accesso passa dalla console VNC nell'area clienti. Non dipende dallo stack di rete del sistema ospite, quindi né una regola firewall né un servizio SSH sovraccarico possono bloccarla. Accedi lì una volta prima che serva e assicurati di conoscere la password di root.

L'interruttore di spegnimento

Se è il monitoraggio stesso a diventare un problema, per esempio perché manda allarmi ogni minuto, ti servono due comandi. Imparali prima di cominciare:

systemctl disable --now kh-monitor.timer
systemctl mask kh-monitor.service

Il primo ferma subito il timer e impedisce che ritorni al riavvio successivo. Il secondo è il freno di emergenza: un servizio mascherato non si lascia più avviare a mano nemmeno per sbaglio, e si torna indietro con systemctl unmask kh-monitor.service. La procedura comporta pochi rischi soprattutto perché nascono solo file nuovi; smontarla significa cancellare quei file. Tieni comunque aperta una seconda sessione SSH finché lavori sul sistema.

Prima verifica gli indicatori a mano

Prima che uno script valuti qualcosa, dovresti aver visto ogni valore con i tuoi occhi. Altrimenti poi non sai se un allarme è giustificato o se la tua soglia è una sciocchezza.

Spazio su disco e inode

df -h
df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay
df -i

Le esclusioni servono davvero. Su Ubuntu 22.04 e 24.04 snap monta i suoi pacchetti come immagini squashfs in sola lettura, e quelle restano stabilmente al 100%. Senza -x squashfs il tuo monitoraggio segnala un disco pieno già dalla prima esecuzione, tutti i giorni, per sempre. Lo stesso vale per i punti di mount overlay di Docker.

Due particolarità vanno conosciute. df non accetta -P e --output insieme e si interrompe con un messaggio sulle opzioni che si escludono a vicenda. E su ext4 il cinque per cento è riservato a root di serie, per cui df segnala già il 100% mentre root può ancora scrivere. Che cosa fare dopo l'allarme lo trovi in Disco pieno su Linux.

Il controllo degli inode non è un dettaglio marginale. Una directory con milioni di minuscoli file di sessione o di cache può consumare tutti gli inode, mentre df -h mostra ancora spazio libero in abbondanza. Le scritture falliscono allora con No space left on device, e la spiegazione più ovvia è quella sbagliata.

Memoria (RAM)

free -m
awk '/^MemAvailable:/ { printf "%d MB\n", $2 / 1024 }' /proc/meminfo

Su un sistema Linux sano la colonna free è quasi sempre piccola, perché il kernel usa la memoria inutilizzata come cache dei file. L'unico dato affidabile è available, ovvero MemAvailable: la quantità di memoria che una nuova applicazione può ottenere senza che nulla finisca in swap. Fai scattare l'allarme su questo valore, mai su free.

Se in passato la situazione si era già fatta stretta, te lo dice il log del kernel:

journalctl -k -b --grep "Out of memory"

Ogni riscontro è un processo che il kernel ha terminato perché la memoria era finita. Come reagire senza creare swap alla cieca lo trovi in Out of Memory e swap configurato bene.

Carico di sistema

nproc
cat /proc/loadavg
uptime

I primi tre campi di /proc/loadavg sono le medie su uno, cinque e quindici minuti. Due cose vengono fraintese di continuo. Primo, sotto Linux il carico non è una grandezza puramente legata alla CPU: contano anche i processi in attesa di accessi al disco. Un carico di 20 su quattro core può voler dire che la CPU sta bruciando, oppure che un disco è impantanato. Secondo, il valore a un minuto non serve per gli allarmi, perché ogni giro di backup lo fa salire per un attimo. Prendi la media a 15 minuti e imposta la soglia in rapporto al numero di core.

Se il tuo kernel porta con sé la statistica di pressione, quella è più significativa, perché distingue CPU, input e output e memoria. Non è presente ovunque:

test -d /proc/pressure && cat /proc/pressure/io || echo "nessuna statistica di pressione in questo kernel"

Servizi

systemctl is-system-running
systemctl --failed --no-pager
systemctl is-active nginx

systemctl is-system-running è il controllo complessivo più breve che abbia senso. Restituisce running quando nessuna unit è in stato di errore, e degraded non appena una lo è. Il codice di uscita è di conseguenza 0 oppure diverso da 0.

Due trappole. Lo stato degraded resta finché non lo azzeri dopo la riparazione con systemctl reset-failed; altrimenti un solo job fallito tiene in piedi il messaggio per settimane. E nel controllo dei singoli servizi i quattro sistemi di riferimento si differenziano: su Ubuntu 24.04 SSH viene avviato tramite socket activation, lì ssh.service a riposo è inactive anche se SSH è perfettamente raggiungibile. Chi su quel sistema monitora ssh.service ottiene un falso allarme permanente. Su Ubuntu 24.04 va monitorato ssh.socket, su Debian 12, Debian 13 e Ubuntu 22.04 invece ssh.service.

Scadenza dei certificati

openssl x509 -enddate -noout -in /etc/letsencrypt/live/example.com/fullchain.pem
openssl x509 -checkend 1814400 -noout -in /etc/letsencrypt/live/example.com/fullchain.pem

Il secondo comando è quello interessante. -checkend si aspetta un numero di secondi, 1814400 corrispondono a 21 giorni. Se il certificato scade entro quel termine, il comando stampa Certificate will expire e restituisce il codice di uscita 1, altrimenti Certificate will not expire e 0. /etc/letsencrypt/live e /etc/letsencrypt/archive sono leggibili solo da root.

Il controllo ha una lacuna che molte guide tacciono: verifica il file sul disco, non il certificato che il tuo server web consegna davvero. Se il rinnovo va a buon fine ma il reload del server web fallisce, il file è nuovo e la chiave consegnata è vecchia. Il controllo sul file non segnala niente, mentre i visitatori vedono già un avviso sul certificato. Solo lo sguardo dall'esterno intercetta questo caso:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate

-servername non è facoltativo non appena più certificati risiedono sullo stesso indirizzo IP. Senza questa aggiunta ottieni il certificato predefinito del server e verifichi il dominio sbagliato.

Lo script di controllo

Tutti i controlli messi insieme danno uno script che non fa altro che leggere, confrontare e segnalare in caso di errore. Gli servono curl e, per la via del webhook, jq. Nelle installazioni Debian minimali mancano entrambi:

apt update
apt install -y curl jq
cat > /usr/local/sbin/kh-monitor <<'EOF'
#!/bin/bash
set -u

DISK_WARN="${DISK_WARN:-85}"
INODE_WARN="${INODE_WARN:-85}"
MEM_MIN_MB="${MEM_MIN_MB:-200}"
LOAD_FACTOR="${LOAD_FACTOR:-2}"
CERT_DAYS="${CERT_DAYS:-21}"
UNITS="${UNITS:-ssh nginx}"
STATE_DIR="${STATE_DIRECTORY:-/var/lib/kh-monitor}"

problems=""
add() { problems+="- ${1}"$'\n'; }

notify() {
  printf '%s | %s\n' "$1" "$(printf '%s' "$2" | tr '\n' ' ')"
  if [ -n "${WEBHOOK_URL:-}" ]; then
    printf '%s\n%s' "$1" "$2" | jq -Rs '{text: .}' \
      | curl -fsS -m 10 -o /dev/null -H 'Content-Type: application/json' \
             --data-binary @- "$WEBHOOK_URL"
  fi
  if [ -n "${MAILTO:-}" ]; then
    printf '%s\n' "$2" | mail -s "$1" "$MAILTO"
  fi
}

while read -r pcent target; do
  pcent="${pcent%\%}"
  case "$pcent" in ''|*[!0-9]*) continue ;; esac
  [ "$pcent" -ge "$DISK_WARN" ] && add "Disco ${target} occupato al ${pcent} per cento"
done < <(df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)

while read -r ipcent target; do
  ipcent="${ipcent%\%}"
  case "$ipcent" in ''|*[!0-9]*) continue ;; esac
  [ "$ipcent" -ge "$INODE_WARN" ] && add "Inode su ${target} occupati al ${ipcent} per cento"
done < <(df --output=ipcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)

mem_avail=$(awk '/^MemAvailable:/ { printf "%d", $2 / 1024 }' /proc/meminfo)
[ "${mem_avail:-0}" -lt "$MEM_MIN_MB" ] && add "solo ${mem_avail} MB di RAM disponibili"

cores=$(nproc)
load15=$(awk '{ print $3 }' /proc/loadavg)
awk -v l="$load15" -v c="$cores" -v f="$LOAD_FACTOR" 'BEGIN { exit !(l > c * f) }' \
  && add "Carico medio a 15 minuti ${load15} con ${cores} core"

sysstate=$(systemctl is-system-running)
[ "$sysstate" = "running" ] || add "systemd segnala lo stato ${sysstate}"

for unit in $UNITS; do
  systemctl is-active --quiet "$unit" || add "Il servizio ${unit} è $(systemctl is-active "$unit")"
done

for cert in /etc/letsencrypt/live/*/fullchain.pem; do
  [ -r "$cert" ] || continue
  openssl x509 -checkend $(( CERT_DAYS * 86400 )) -noout -in "$cert" >/dev/null 2>&1 \
    || add "Il certificato ${cert} scade fra meno di ${CERT_DAYS} giorni"
done

mkdir -p "$STATE_DIR"
now=$(printf '%s' "$problems" | sha256sum | cut -d' ' -f1)
before=$(cat "${STATE_DIR}/last" 2>/dev/null || true)
printf '%s' "$now" > "${STATE_DIR}/last"

if [ -z "$problems" ]; then
  [ -n "${HEARTBEAT_URL:-}" ] && curl -fsS -m 10 -o /dev/null "$HEARTBEAT_URL"
  [ -n "$before" ] && [ "$now" != "$before" ] \
    && notify "Cessato allarme $(hostname -s)" "Tutti i controlli sono di nuovo a posto."
  exit 0
fi

[ "$now" = "$before" ] && exit 0
notify "Avviso $(hostname -s)" "$problems"
EOF

Quattro punti meritano una spiegazione.

  • I cicli leggono da < <( ... ), non da una pipe. Una pipe sposta il ciclo in una subshell e i messaggi raccolti lì sarebbero spariti dopo il done. L'errore è insidioso, perché lo script gira senza errori e semplicemente non segnala mai niente.
  • Le soglie si leggono dall'ambiente. Puoi sovrascrivere ogni limite per una singola chiamata. Su questo si basa la prova dell'allarme più avanti.
  • Lo stato viene archiviato come checksum. La segnalazione parte solo quando l'elenco dei problemi è cambiato. Altrimenti un disco pieno ti manda lo stesso messaggio ogni dieci minuti e dopo due giorni spegni il monitoraggio.
  • Il ciclo dei certificati gira a vuoto se non esiste nessuna directory di Let's Encrypt. Il pattern resta non espanso, [ -r "$cert" ] fallisce e l'iterazione viene saltata.

Verifica adesso, prima che entri in gioco systemd:

chmod 700 /usr/local/sbin/kh-monitor
bash -n /usr/local/sbin/kh-monitor && echo "sintassi corretta"
/usr/local/sbin/kh-monitor; echo "Codice di uscita $?"

Su un server sano il terzo comando non stampa nulla a parte Codice di uscita 0. Se vedi un messaggio, o c'è davvero qualcosa che non va, oppure una soglia non è tarata bene, per esempio perché in UNITS compare un servizio che qui non esiste.

Il timer systemd

Andrebbe bene anche un cron job. Un timer però ha quattro vantaggi concreti: non avvia una seconda istanza finché la prima è in esecuzione, recupera dopo un riavvio l'esecuzione che era saltata, il suo output finisce nel journal e si spegne con un solo comando. La unit di servizio non ha bisogno di una sezione [Install], perché non viene attivata all'avvio del sistema ma dal timer.

cat > /etc/systemd/system/kh-monitor.service <<'EOF'
[Unit]
Description=Breve controllo dello stato del server
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/kh-monitor
EnvironmentFile=-/etc/default/kh-monitor
StateDirectory=kh-monitor
SyslogIdentifier=kh-monitor
Nice=10
IOSchedulingClass=idle
EOF
cat > /etc/systemd/system/kh-monitor.timer <<'EOF'
[Unit]
Description=Esegue kh-monitor a intervalli regolari

[Timer]
OnCalendar=*:0/10
RandomizedDelaySec=60
Persistent=true

[Install]
WantedBy=timers.target
EOF

StateDirectory=kh-monitor crea /var/lib/kh-monitor con i permessi giusti e imposta la variabile STATE_DIRECTORY, a cui lo script attinge. Il segno meno anteposto in EnvironmentFile=-/etc/default/kh-monitor significa che un file mancante non è un errore. RandomizedDelaySec=60 distribuisce il momento di avvio, Persistent=true recupera all'accensione un'esecuzione saltata durante uno spegnimento.

Il timer attiva automaticamente il servizio con lo stesso nome, una riga Unit= non serve:

systemd-analyze verify /etc/systemd/system/kh-monitor.service
systemd-analyze calendar '*:0/10'
systemctl daemon-reload
systemctl start kh-monitor.service
systemctl enable --now kh-monitor.timer

Verifica del risultato:

systemctl list-timers kh-monitor.timer --no-pager
journalctl -u kh-monitor.service -n 20 --no-pager

L'elenco deve mostrare una riga con la prossima esecuzione. Se resta vuoto, il timer non è attivo. systemd-analyze calendar trova gli errori di battitura nell'espressione temporale: la converte in una forma normalizzata e indica la prossima data. I dettagli sui file unit e sui loro quadri di errore li trovi in Creare un servizio systemd.

La via di notifica

È qui che fallisce la maggior parte dei sistemi di monitoraggio fatti in casa. Chi deve segnalare gira sullo stesso server che sorveglia, quindi cade insieme a lui. Segnala così ogni cosa in modo affidabile, tranne l'unico caso che conta davvero.

La risposta si chiama heartbeat, cioè un dispositivo a uomo morto: il server dà segno di vita a un servizio remoto dopo ogni esecuzione riuscita e, se il segnale non arriva, è quel servizio remoto a dare l'allarme. Lo script qui sopra lo fa quando HEARTBEAT_URL è impostata. Un guasto di rete, un file system bloccato e un sistema andato in crash hanno lo stesso aspetto visti dal servizio remoto, e di tutti e tre vuoi essere informato.

Le credenziali vanno in un file separato, non nello script. Un indirizzo webhook è un segreto: chi lo possiede può inviare messaggi a tuo nome:

cat > /etc/default/kh-monitor <<'EOF'
WEBHOOK_URL=https://esempio.example/hooks/xxxxxxxx
HEARTBEAT_URL=https://esempio.example/heartbeat/xxxxxxxx
UNITS="ssh nginx"
EOF
chmod 600 /etc/default/kh-monitor

Le virgolette intorno a UNITS sono importanti: systemd se la caverebbe anche senza, ma nella prova più avanti il file viene letto dalla shell, e lì nginx senza virgolette verrebbe interpretato come comando. Inserisci solo unit che esistono su questo sistema, quindi su Ubuntu 24.04 ssh.socket invece di ssh.

Chi preferisce la posta elettronica ha bisogno di una via di invio. Un mail server completo è sovradimensionato, basta un semplice inoltratore:

apt install -y msmtp msmtp-mta bsd-mailx

La configurazione sta in /etc/msmtprc con le credenziali di una casella già esistente. Il file contiene una password e va messo a chmod 600, altrimenti msmtp rifiuta il servizio con un riferimento ai permessi del file. Due limiti da dire onestamente: la posta inviata da un indirizzo IP appena assegnato finisce spesso nella cartella spam oppure viene respinta, e un messaggio che vedi solo alla prossima occhiata alla casella è troppo lento in caso di guasto. Per gli allarmi la consegna push è la scelta più pratica.

Fai scattare l'allarme una volta

Questo passo non è facoltativo. Forza una segnalazione impostando in modo assurdo una soglia per una singola chiamata:

set -a; . /etc/default/kh-monitor; set +a
DISK_WARN=0 /usr/local/sbin/kh-monitor
rm -f /var/lib/kh-monitor/last

Il messaggio deve ora arrivare davvero a te, non limitarsi a comparire nel journal. La terza riga cancella lo stato salvato, in modo che l'allarme di prova non sopprima la prossima esecuzione reale. Un effetto collaterale è voluto: se la consegna fallisce, kh-monitor.service termina con un errore e compare in systemctl --failed. Una via di notifica silenziosa sarebbe l'errore peggiore che si possa immaginare in un monitoraggio.

Lo sguardo dall'esterno

L'heartbeat ti dice che il server è vivo. Non ti dice che il tuo sito risponde. Per questo serve un controllo da un'altra posizione, con lo stesso schema fatto di script e timer, solo su un secondo host:

curl -fsS -m 10 -o /dev/null -w '%{http_code} %{time_total}\n' https://example.com/

Quattro punti decidono il valore di questo controllo. Primo: verifica il servizio reale, non solo ICMP. Un server che risponde al ping mentre il server web è bloccato in un ciclo infinito, per un controllo via ping risulta sano. Secondo: -f fa fallire curl sui codici di errore HTTP, senza questa opzione anche una pagina di errore vale come successo. Terzo: fai scattare l'allarme solo dopo due fallimenti consecutivi, altrimenti ogni breve disturbo di rete segnala un guasto. Quarto: una chiamata al minuto dallo stesso indirizzo può finire in un limite di frequenza oppure essere considerata un attacco da un software di blocco; inserisci l'indirizzo dell'host che effettua il controllo come eccezione.

Senza un secondo server resta un servizio di controllo ospitato da terzi. Le offerte gratuite controllano di solito ogni cinque minuti, quindi vieni a sapere di un guasto con il relativo ritardo. Per un singolo server è sufficiente, ed è meglio dell'alternativa, cioè non venirlo a sapere affatto.

Errori frequenti e soluzioni

Failed to start kh-monitor.service: Unit kh-monitor.service not found.: dopo aver creato o modificato una unit manca systemctl daemon-reload. La seconda causa più frequente è una directory sbagliata; le unit proprie vanno in /etc/systemd/system/.

The unit files have no installation config: hai chiamato systemctl enable kh-monitor.service invece di kh-monitor.timer. Il servizio non ha volutamente nessuna sezione [Install], ad essere attivato è il timer.

code=exited, status=203/EXEC: systemd non è riuscito a eseguire il file. O il percorso in ExecStart è sbagliato, oppure manca chmod 700, oppure il file è stato modificato su Windows e porta fine riga con ritorno a capo. Contro questo aiuta sed -i 's/\r$//' /usr/local/sbin/kh-monitor.

Syntax error: redirection unexpected: lo script è stato avviato con sh invece che con bash. Su Debian e Ubuntu /bin/sh è la shell dash, che non conosce né < <( ... ) né += sulle stringhe. La prima riga deve essere #!/bin/bash.

bash: mail: command not found: non c'è nessun programma di posta installato. Il comando mail arriva, a seconda del sistema, da bsd-mailx oppure da mailutils, e i due si differenziano nelle opzioni. Resta su -s per l'oggetto.

curl: (22) The requested URL returned error: 404: l'indirizzo del webhook è sbagliato oppure è stato cancellato sul servizio remoto. Senza -f curl avrebbe inghiottito l'errore in silenzio.

curl: (60) SSL certificate problem: certificate has expired: nel controllo dall'esterno non è un errore dello strumento, è esattamente il riscontro che cercavi. Verso il tuo webhook lo stesso messaggio indica invece un orario sbagliato sul server che effettua il controllo.

Certificate will expire: output regolare di openssl x509 -checkend con codice di uscita 1. Verifica che il rinnovo sia ancora attivo e che il server web venga ricaricato subito dopo.

Failed to parse calendar specification: l'espressione dopo OnCalendar= non è valida. Provala da sola con systemd-analyze calendar prima che finisca nella unit.

Warning: Stopping kh-monitor.service, but it can still be activated by: kh-monitor.timer: hai fermato il servizio invece del timer. Il servizio gira comunque solo pochi secondi, quello da spegnere è il timer.

Allarme a ogni esecuzione anche se non è cambiato nulla: lo stato non viene salvato. Verifica che nella unit ci sia StateDirectory=kh-monitor e che /var/lib/kh-monitor/last esista e sia scrivibile.

Nessun allarme anche se qualcosa è palesemente rotto: fai scattare la segnalazione forzata descritta sopra. Se non arriva nemmeno quella, il problema è nella via di consegna, non nei controlli.

Differenze fra i quattro sistemi

SistemaUnit SSH da monitorarePunti di mount squashfsDove finiscono i messaggi
Debian 13 (trixie)ssh.service, singole immagini usano ssh.socketdi regola nessunosolo journal, rsyslog manca nelle installazioni minimali
Debian 12 (bookworm)ssh.servicedi regola nessunojournal, rsyslog a seconda della variante di installazione
Ubuntu 24.04 LTSssh.socketquasi sempre presenti, esclusione necessariajournal e rsyslog
Ubuntu 22.04 LTSssh.servicequasi sempre presenti, esclusione necessariajournal e rsyslog

Su tutti e quattro i sistemi vale lo stesso: systemd-analyze, StateDirectory= e RandomizedDelaySec= ci sono, script e file unit funzionano senza modifiche.

Quando conviene la cassetta degli attrezzi grande

Quello che hai adesso ha limiti chiari. Non conserva nessuno storico, quindi non puoi controllare se il consumo di memoria sale da tre settimane. Non conosce correlazioni fra più host. E non ha né escalation né regole di reperibilità né la possibilità di silenziare per due ore un allarme già noto.

Sono proprio questi punti a rispondere alla domanda sul passaggio. Un impianto di metriche e dashboard conviene non appena uno di questi casi si applica:

  • Gestisci più di una manciata di server e li vuoi vedere uno accanto all'altro.
  • Ti servono storico e tendenze, per esempio per la pianificazione della capacità oppure per rispondere con i numeri a un reclamo sulla lentezza.
  • Più persone si dividono la reperibilità, servono quindi livelli di escalation e silenziamento.
  • Devi dimostrare la disponibilità a terzi.

Se non se ne applica nessuno, per un singolo server quell'impianto è di solito un cattivo affare. Punto di raccolta, database, dashboard ed exporter occupano insieme senza fatica diverse centinaia di megabyte di RAM proprio sulla macchina della quale dovrebbero sorvegliare la memoria libera. Ci sono poi una porta aperta in più e un secondo software che vuole essere aggiornato. Il punto decisivo resta lo stesso: se il punto di raccolta gira sullo stesso server, il guasto di quel server non lo segnala più di quanto lo faccia uno script. Nel passaggio il punto di raccolta va su un altro host, e l'exporter si lega a 127.0.0.1 oppure viene limitato via firewall all'indirizzo di quell'host.

La via di mezzo funziona bene: timer e script restano, perché coprono il caso di allarme, e l'impianto di metriche si aggiunge quando ti servono gli andamenti nel tempo. Le due cose non si escludono.

Smontaggio

Se vuoi liberarti di nuovo di questa procedura, bastano cinque righe:

systemctl disable --now kh-monitor.timer
rm -f /etc/systemd/system/kh-monitor.timer /etc/systemd/system/kh-monitor.service
rm -f /usr/local/sbin/kh-monitor /etc/default/kh-monitor
rm -rf /var/lib/kh-monitor
systemctl daemon-reload

Il controllo finale

Sei verifiche che mostrano lo stato reale e non quello sperato:

  1. systemctl list-timers kh-monitor.timer --no-pager indica una prossima esecuzione.
  2. journalctl -u kh-monitor.service --since "1 hour ago" --no-pager mostra esecuzioni alla distanza attesa.
  3. Un allarme forzato tramite DISK_WARN=0 arriva davvero a te, non solo nel journal.
  4. Dopo un reboot il timer continua a girare senza che tu faccia niente.
  5. L'heartbeat si fa sentire: ferma il timer e aspetta di vedere se il servizio remoto dà l'allarme.
  6. systemctl --failed --no-pager non elenca niente, in particolare non kh-monitor.service.

Il punto cinque è il più scomodo e il più importante. Un monitoraggio il cui caso di allarme non si è mai verificato è soltanto una supposizione. Solo quando hai provocato il guasto di proposito e hai ricevuto il messaggio, la catena dal server fino al tuo telefono è dimostrabilmente completa.

Domande frequenti

Per un singolo server root mi serve davvero un server di metriche con dashboard?
Nella maggior parte dei casi no. Punto di raccolta, database, dashboard ed exporter occupano insieme senza fatica diverse centinaia di megabyte di RAM proprio sulla macchina della quale dovrebbero sorvegliare la memoria libera, e portano con sé una porta aperta in più e un secondo software da tenere aggiornato. Uno script di controllo con timer systemd copre per intero il caso di allarme. L'impianto grande diventa sensato appena vuoi vedere più server uno accanto all'altro, ti serve lo storico per la pianificazione della capacità, più persone si dividono la reperibilità oppure devi dimostrare la disponibilità a terzi.
Perché un timer systemd e non un cron job?
Funzionano entrambi. Il timer ha quattro vantaggi concreti: non avvia una seconda istanza finché la prima è in esecuzione, con Persistent=true recupera all'accensione un'esecuzione saltata durante uno spegnimento, il suo output finisce automaticamente nel journal grazie a SyslogIdentifier e si spegne con un solo comando, systemctl disable --now kh-monitor.timer. Ad essere attivato è sempre il timer, non il servizio: la unit di servizio non ha volutamente nessuna sezione [Install].
Il mio monitoraggio segnala sempre un disco pieno anche se lo spazio libero c'è. Da che cosa dipende?
Quasi sempre dai punti di mount di snap. Su Ubuntu 22.04 e 24.04 i pacchetti snap vengono montati come immagini squashfs in sola lettura, e quelle restano per natura stabilmente al 100 per cento. Escludili, quindi df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay. Lo stesso vale per i punti di mount overlay di Docker. Seconda causa possibile: su ext4 il cinque per cento è riservato a root di serie, per cui df può segnalare già il 100 per cento mentre root può ancora scrivere.
Come vengo a sapere che il server è caduto del tutto?
Non da uno script su quel server, perché cade insieme a lui. Per questo ci sono due vie che si completano. L'heartbeat, cioè un dispositivo a uomo morto: il server dà segno di vita a un servizio remoto dopo ogni esecuzione riuscita e, se il segnale non arriva, è il servizio remoto a dare l'allarme. E il controllo dall'esterno: un secondo host oppure un servizio di controllo ospitato che chiama il servizio reale, non solo ICMP. I servizi di controllo gratuiti lavorano di solito ogni cinque minuti, quindi vieni a sapere di un guasto con il relativo ritardo.
Quale unit SSH devo monitorare?
Dipende dalla distribuzione. Su Ubuntu 24.04 SSH viene avviato tramite socket activation, lì ssh.service a riposo è inactive anche se SSH è perfettamente raggiungibile. Chi su quel sistema monitora ssh.service ottiene un falso allarme permanente; va monitorato ssh.socket. Su Debian 12, Debian 13 e Ubuntu 22.04 vale il contrario, lì conta ssh.service. Singole immagini di Debian 13 hanno però anche loro ssh.socket attivo, verificalo con systemctl is-enabled ssh.socket.
Come evito di ricevere lo stesso avviso ogni dieci minuti?
Con un file di stato. Lo script ricava un checksum dall'elenco dei problemi trovati, lo deposita in /var/lib/kh-monitor/last e segnala solo se quel checksum è cambiato rispetto all'esecuzione precedente. Se un problema scompare, arriva una volta il cessato allarme. Se ricevi comunque un messaggio a ogni esecuzione, nella unit manca la riga StateDirectory=kh-monitor, oppure il file non è scrivibile.
Il certificato sul disco è valido, i visitatori vedono comunque un avviso. Com'è possibile?
Perché il controllo del file e il certificato consegnato davvero sono due cose diverse. Se il rinnovo automatico va a buon fine ma il reload del server web fallisce, il file è nuovo e la chiave consegnata è vecchia. openssl x509 -checkend non segnala allora niente. Verifica quindi anche dall'esterno con openssl s_client -connect example.com:443 -servername example.com. L'aggiunta -servername non è facoltativa non appena più certificati risiedono sullo stesso indirizzo IP.
Come spengo in fretta il monitoraggio se dà fastidio?
Con systemctl disable --now kh-monitor.timer, che ferma subito il timer e impedisce che ritorni al riavvio successivo. Come freno di emergenza, systemctl mask kh-monitor.service impedisce inoltre qualsiasi avvio a mano, e si torna indietro con systemctl unmask. La procedura si rimuove del tutto cancellando i due file unit, lo script sotto /usr/local/sbin/, il file /etc/default/kh-monitor e la directory /var/lib/kh-monitor, seguito da systemctl daemon-reload. La configurazione esistente non viene toccata, perché sono stati creati soltanto file nuovi.

Monitoraggio systemd Linux Debian Ubuntu Server root Sorveglianza server Bash