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 = 60Il 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 swapCon -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
10Con -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
60Tre 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.confPer 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 = 2Qualche 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 = 1Un 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
524288Un 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
doneLo 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 -wnon è 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,
-wsembra riuscire e non fa niente: il messaggiopermission denied ... ignoringè seguito dalla riga con il valore richiesto. Rileggi sempre consysctl -n. Per lo stesso motivosudo 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 modulobr_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 ripetisysctl --systemdopo. - 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 con99-garantisce che la tua scelta prevalga; controlla consysctl --system. - I valori con più numeri vanno tra virgolette: per i parametri che ne hanno più di uno (come
net.ipv4.tcp_rmem), scrivisudo 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
| Comando | Cosa fa |
|---|---|
sysctl vm.swappiness | Legge un parametro |
sysctl -n vm.swappiness | Solo il valore (per gli script) |
sysctl -a | Elenca tutti i parametri (aggiungi 2>/dev/null) |
sysctl -ar '^vm\.dirty' | Elenca i parametri che corrispondono a un'espressione |
sudo sysctl -w vm.swappiness=10 | Cambia un valore subito, fino al riavvio |
cat /proc/sys/vm/swappiness | Legge lo stesso valore come file |
/etc/sysctl.d/99-miei.conf | File di configurazione permanente (nome = valore) |
sudo sysctl --system | Applica tutti i file di configurazione |
sudo sysctl -p file | Applica un solo file |
sysctl --dry-run -p file | Mostra cosa farebbe, senza applicare |
sysctl -e -p file | Ignora i parametri sconosciuti |