Salta al contenuto principale

sysctl: regolare il kernel mentre è acceso

Inviato da tuxsa il

Il server usa un po' di swap anche se la memoria è abbondante. Un programma si lamenta che "il numero di file osservabili è esaurito". Una macchina deve fare da router e non inoltra i pacchetti. In tutti questi casi la causa è un parametro del kernel, un numero che il sistema legge per decidere come comportarsi. sysctl li mostra e permette di cambiarli, senza riavviare.

Cos'è

sysctl fa parte del pacchetto procps, lo stesso di top, free, ps, uptime e watch. Il kernel (il "cuore" di Linux: vedi l'articolo su uname) espone i suoi parametri come file speciali nella cartella /proc/sys: ogni file contiene un valore, e scrivere nel file cambia il comportamento del sistema, subito. sysctl è l'interfaccia comoda a questi file: li chiama con un nome a punti (vm.swappiness) invece che con un percorso (/proc/sys/vm/swappiness). I due nomi sono equivalenti: i punti corrispondono alle barre.

Cosa fa

Legge i parametri del kernel, uno per uno o tutti insieme, e scrive nuovi valori. Può anche caricare molti valori da file di configurazione, ed è quello che il sistema fa a ogni avvio per rendere permanenti le tue scelte.

Come si usa

$ sysctl [opzioni] [parametro[=valore]]

Gli esempi sono stati provati in un sandbox, con un kernel recente e come utente amministratore; i valori di web01, il server degli articoli precedenti, sono illustrativi.

Esempio 1: leggere un parametro

$ sysctl vm.swappiness
vm.swappiness = 60
$ sysctl -n vm.swappiness
60
$ sysctl -N vm.swappiness
vm.swappiness
$ sysctl kernel.hostname kernel.osrelease
kernel.hostname = web01
kernel.osrelease = 6.1.0-28-amd64
$ cat /proc/sys/vm/swappiness
60
$ sysctl vm/swappiness
vm.swappiness = 60

Il formato è nome = valore. Con -n (values) si ottiene solo il valore, comodo negli script; con -N (names) solo il nome. Si possono chiedere più parametri insieme. Il valore di kernel.osrelease è lo stesso che mostra uname -r: è il segno che uname legge gli stessi dati. Gli ultimi due comandi mostrano che il file in /proc/sys contiene lo stesso valore (si legge con cat) e che sysctl accetta anche i nomi scritti con le barre. Un parametro con più valori (come net.ipv4.ip_local_port_range) li mostra separati da uno spazio o da una tabulazione.

Esempio 2: l'elenco completo e la ricerca

$ sysctl -a 2>/dev/null | wc -l
808
$ sysctl -a 2>/dev/null | cut -d. -f1 | sort | uniq -c
     12 abi
      3 debug
      8 dev
    123 fs
    210 kernel
    554 net
     49 user
     90 vm
$ sysctl -ar '^vm\.dirty'
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
$ sysctl -a 2>/dev/null | grep -i swap

Con -a (all) si vedono tutti i parametri: centinaia, divisi in famiglie. Le più importanti: kernel (comportamento generale), vm (memoria virtuale, swap), fs (file system e file aperti), net (rete) e user (limiti per utente). Il numero esatto dipende dal kernel: il conteggio e la distribuzione delle righe nell'esempio vengono da un kernel di prova, e il tuo sarà un po' diverso. Con -r (pattern) si filtra per espressione; in alternativa si usa grep. Con 2>/dev/null si nascondono gli errori di permesso: un utente normale non può leggere alcuni parametri e sysctl -a lo segnala.

Esempio 3: cambiare un valore al volo

$ sudo sysctl -w vm.swappiness=10
vm.swappiness = 10
$ sysctl vm.swappiness
vm.swappiness = 10
$ sudo sysctl -w net.ipv4.ip_forward=1 vm.swappiness=30
net.ipv4.ip_forward = 1
vm.swappiness = 30
$ sudo sysctl -q -w vm.swappiness=60
$ echo 10 | sudo tee /proc/sys/vm/swappiness
10

Con -w (write) si scrive un nuovo valore: la modifica ha effetto immediato, ma vale solo fino al riavvio. sysctl risponde ripetendo il valore. Si possono impostare più parametri insieme, e con -q (quiet) si toglie la risposta. Il significato di vm.swappiness è quello visto negli articoli su free e lsblk: quanto il kernel è propenso a spostare la memoria sullo swap (60 è il valore predefinito, più basso significa meno swap). Lo stesso risultato si ottiene scrivendo nel file di /proc/sys, ma serve tee con sudo (vedi l'articolo su tee): con sudo echo 10 > /proc/sys/... la redirezione la fa la tua shell senza privilegi, e fallisce con Permission denied.

Esempio 4: gli errori più frequenti

$ sudo sysctl -w vm.swappiness = 10
sysctl: command line(0): invalid syntax, continuing...
$ sudo sysctl -w vm.swappiness=abc
sysctl: setting key "vm.swappiness": Invalid argument
$ sysctl vm.nonesiste
sysctl: cannot stat /proc/sys/vm/nonesiste: No such file or directory
$ sysctl -w vm.swappiness=10
sysctl: permission denied on key "vm.swappiness", ignoring
vm.swappiness = 10
$ sysctl -n vm.swappiness
60

Tre errori da conoscere. Gli spazi: con -w non vanno messi attorno al segno = (invalid syntax). Un valore non valido: il kernel rifiuta un testo al posto di un numero, o un numero fuori dai limiti, con Invalid argument. Un parametro che non esiste: il kernel in uso non lo ha (può dipendere dalla versione, da un modulo non caricato o da un errore di scrittura). L'ultimo esempio è il più insidioso: senza privilegi, sysctl avvisa che la scrittura è negata, ma subito dopo stampa comunque il valore richiesto, come se fosse stato cambiato. Il valore reale (sysctl -n) è rimasto 60. Controlla sempre rileggendo.

Esempio 5: rendere le modifiche permanenti

$ sudo nano /etc/sysctl.d/99-miei.conf
# Meno swap: la memoria è abbondante
vm.swappiness = 10

# Più file osservabili, per gli editor e i programmi di sincronizzazione
fs.inotify.max_user_watches = 524288
$ sudo sysctl --system
* Applying /etc/sysctl.d/10-bufferbloat.conf ...
* Applying /etc/sysctl.d/10-kernel-hardening.conf ...
...
* Applying /etc/sysctl.d/99-miei.conf ...
vm.swappiness = 10
fs.inotify.max_user_watches = 524288
* Applying /etc/sysctl.conf ...
$ sudo sysctl -p /etc/sysctl.d/99-miei.conf
$ sudo sysctl --dry-run -p /etc/sysctl.d/99-miei.conf

Per far sì che un valore sopravviva al riavvio lo si scrive in un file di configurazione: uno per ogni gruppo di scelte, nella cartella /etc/sysctl.d/ e con il nome che finisce in .conf (si modifica con nano: vedi l'articolo relativo). Il formato è semplice: parametro = valore, una riga per parametro; le righe che iniziano con # sono commenti. All'avvio il sistema legge, in ordine alfabetico, tutti i file di /etc/sysctl.d/ (e il vecchio /etc/sysctl.conf): per questo i nomi cominciano con un numero, e l'ultimo file che nomina un parametro vince. Il prefisso 99- garantisce che le tue scelte vengano per ultime. Per applicare i file subito, senza riavviare, sysctl --system li carica tutti e mostra quale sta leggendo; sysctl -p file carica un file solo; con --dry-run si vede cosa verrebbe impostato senza cambiare niente. Un parametro sbagliato nel file produce un errore (cannot stat ...), ma gli altri vengono comunque applicati.

Esempio 6: alcuni parametri utili

$ sysctl vm.swappiness vm.overcommit_memory vm.max_map_count
vm.swappiness = 60
vm.overcommit_memory = 0
vm.max_map_count = 1048576
$ sysctl fs.file-max fs.inotify.max_user_watches kernel.pid_max
fs.file-max = 819102
fs.inotify.max_user_watches = 524288
kernel.pid_max = 4194304
$ sysctl net.ipv4.ip_forward net.core.somaxconn net.ipv4.tcp_syncookies
net.ipv4.ip_forward = 0
net.core.somaxconn = 4096
net.ipv4.tcp_syncookies = 1
$ sysctl kernel.dmesg_restrict kernel.randomize_va_space
kernel.dmesg_restrict = 1
kernel.randomize_va_space = 2

Qualche parametro che si incontra spesso. Memoria: vm.swappiness (propensione allo swap); vm.max_map_count (le zone di memoria per processo: alcuni programmi, come i motori di ricerca, ne chiedono di più); vm.overcommit_memory (se il sistema promette più memoria di quella che ha). File e processi: fs.file-max è il numero massimo di file aperti dall'intero sistema (quello del singolo processo si regola con ulimit); fs.inotify.max_user_watches è il numero di file che un programma può "osservare" per sapere quando cambiano (se è troppo basso, gli editor e i programmi di sincronizzazione segnalano che i file osservabili sono esauriti); kernel.pid_max è il numero più alto assegnabile a un processo. Rete: net.ipv4.ip_forward (1 per far inoltrare i pacchetti al computer, necessario per un router, una VPN, e anche per Docker); net.core.somaxconn (la coda delle connessioni in attesa di un servizio: vedi l'articolo su ss); net.ipv4.tcp_syncookies (una protezione contro certi attacchi di saturazione). Sicurezza: kernel.dmesg_restrict (1 = solo gli amministratori leggono i messaggi del kernel) e kernel.randomize_va_space (2 = la disposizione della memoria cambia a ogni avvio dei programmi, un ostacolo per gli attaccanti).

Esempio 7: il server deve fare da router

$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0
$ sudo sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
$ echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-forward.conf
net.ipv4.ip_forward = 1
$ sudo sysctl --system | grep ip_forward
net.ipv4.ip_forward = 1

Un caso tipico: una macchina che deve inoltrare il traffico tra due reti (un punto di accesso, una VPN, un firewall: vedi l'articolo su ip per le interfacce e gli indirizzi). Si controlla il valore attuale, lo si cambia al volo e poi lo si scrive in un file di sysctl.d con tee, in modo che torni al riavvio. L'ultimo comando verifica che sysctl --system lo applichi. Anche qui i nomi dei file sono a tua scelta.

Esempio 8: aumentare i file osservabili

$ cat /proc/sys/fs/inotify/max_user_watches
8192
$ sudo sysctl -w fs.inotify.max_user_watches=524288
fs.inotify.max_user_watches = 524288
$ echo "fs.inotify.max_user_watches = 524288" | sudo tee -a /etc/sysctl.d/99-miei.conf
$ sudo sysctl --system > /dev/null; sysctl -n fs.inotify.max_user_watches
524288

Un errore tipico dei programmi che osservano molte cartelle (editor di codice, strumenti di sincronizzazione, server di sviluppo) è che il limite degli inotify watches è esaurito. Si controlla il valore attuale, lo si alza e lo si rende permanente aggiungendo la riga al file con tee -a (-a aggiunge in coda senza sovrascrivere). Ogni osservazione occupa un po' di memoria del kernel (qualche centinaio di byte): un valore molto alto va bene su un server con memoria abbondante.

Esempio 9: uso negli script

#!/bin/bash
# Verifica che i parametri del kernel abbiano i valori attesi
ATTESI="vm.swappiness=10
net.ipv4.tcp_syncookies=1
fs.inotify.max_user_watches=524288"

for RIGA in $ATTESI; do
    CHIAVE=${RIGA%%=*}
    VALORE=${RIGA#*=}
    ATTUALE=$(sysctl -n "$CHIAVE" 2>/dev/null)
    if [ "$ATTUALE" != "$VALORE" ]; then
        echo "DIVERSO: $CHIAVE vale '$ATTUALE', atteso '$VALORE'" >&2
    fi
done

Lo script confronta ogni parametro con il valore che dovrebbe avere e segnala quelli diversi. ${RIGA%%=*} toglie tutto da = in poi (la chiave), ${RIGA#*=} toglie tutto fino a = (il valore): sono due operazioni della shell. L'ho provato: con vm.swappiness a 60 invece di 10, il messaggio è comparso solo per quel parametro. Utile dopo un riavvio o un aggiornamento, per accorgersi se una configurazione non è stata applicata (vedi anche l'articolo su crontab per eseguirlo ogni giorno).

Opzioni utili

  • sysctl parametro: legge un valore; -n: solo il valore; -N: solo il nome; -a: tutti i parametri; -r espressione: filtra per espressione.
  • -w parametro=valore: scrive un valore (immediato, non permanente); -q: senza risposta.
  • -p [file]: carica i valori da un file (senza nome, da /etc/sysctl.conf); --system: carica tutti i file di configurazione di sistema; --dry-run: mostra senza applicare.
  • -e: ignora i parametri sconosciuti (utile per un file valido su più versioni del kernel).
  • /proc/sys/: i parametri come file; /etc/sysctl.d/*.conf, /etc/sysctl.conf: la configurazione permanente.
  • ulimit, prlimit: limiti per processo; uname, lscpu: informazioni sul kernel e sul processore.

Attenzione a

  • sysctl -w non è permanente: vale fino al riavvio. Per rendere stabile una scelta serve un file in /etc/sysctl.d/. Dimenticarlo è l'errore classico: "ho sistemato il problema, ma dopo il riavvio è tornato".
  • Cambia una cosa alla volta, e annota il valore di partenza: un parametro sbagliato può rendere la macchina instabile o irraggiungibile (per esempio una regola di rete). Copia il valore originale prima di modificarlo, così puoi tornare indietro, e prova su una macchina non critica. I limiti ammessi dipendono dal kernel: per vm.swappiness, i kernel recenti accettano valori fino a 200 e quelli più vecchi fino a 100.
  • Non copiare a occhi chiusi le "guide all'ottimizzazione": elenchi di parametri trovati in rete, scritti per altre macchine, altri kernel e altri carichi di lavoro, possono peggiorare le cose. Conta la causa che hai davvero misurato (con free, top, ss, journalctl).
  • Senza privilegi, -w sembra riuscire e non fa niente: il messaggio permission denied ... ignoring è seguito dalla riga con il valore richiesto. Rileggi sempre con sysctl -n. Per lo stesso motivo sudo echo ... > /proc/sys/... non funziona (esempio 3).
  • Parametri che non esistono ancora all'avvio: alcuni parametri esistono solo dopo il caricamento di un modulo del kernel (per esempio quelli di net.bridge.* richiedono il modulo br_netfilter). All'avvio, se il file di configurazione viene letto prima del modulo, la riga dà un errore e non viene applicata. In quel caso carica prima il modulo, o ripeti sysctl --system dopo.
  • L'ultimo file vince: se lo stesso parametro compare in più file (anche in quelli installati dai pacchetti, come 10-kernel-hardening.conf), vale l'ultimo in ordine alfabetico. Un nome che inizia con 99- garantisce che la tua scelta prevalga; controlla con sysctl --system.
  • I valori con più numeri vanno tra virgolette: per i parametri che ne hanno più di uno (come net.ipv4.tcp_rmem), scrivi sudo sysctl -w "net.ipv4.tcp_rmem=4096 131072 6291456"; nei file, i numeri si separano con spazi.
  • Nei container i parametri non sono tutti modificabili: molti appartengono al kernel della macchina ospite, condiviso da tutti, e spesso sono in sola lettura. Alcuni parametri di rete si possono impostare per ciascun container (con Docker, --sysctl).
  • Un valore non è una garanzia: alzare un limite non fa comparire la memoria o la capacità che mancano (vedi l'articolo su free); ne toglie solo il tetto.
  • Distribuzioni diverse, abitudini diverse: sulle più vecchie si modificava solo /etc/sysctl.conf; oggi si preferisce la cartella /etc/sysctl.d/, e la distribuzione tiene i propri file in /usr/lib/sysctl.d/, che non va modificata (un aggiornamento la sovrascrive).

Riepilogo

ComandoCosa fa
sysctl vm.swappinessLegge un parametro
sysctl -n vm.swappinessSolo il valore (per gli script)
sysctl -aElenca tutti i parametri (aggiungi 2>/dev/null)
sysctl -ar '^vm\.dirty'Elenca i parametri che corrispondono a un'espressione
sudo sysctl -w vm.swappiness=10Cambia un valore subito, fino al riavvio
cat /proc/sys/vm/swappinessLegge lo stesso valore come file
/etc/sysctl.d/99-miei.confFile di configurazione permanente (nome = valore)
sudo sysctl --systemApplica tutti i file di configurazione
sudo sysctl -p fileApplica un solo file
sysctl --dry-run -p fileMostra cosa farebbe, senza applicare
sysctl -e -p fileIgnora i parametri sconosciuti