Se cerchi “ridurre consumo RAM Linux”, quello che trovi sono per lo più liste di sysctl da incollare — metti swappiness a questo valore, svuota le cache, disabilita quest’altro. Non ho mai visto quell’approccio sopravvivere al contatto con un host di produzione vero. Quello che funziona è molto meno glamour: un ciclo misura-modifica-verifica, applicato nell’ordine giusto — prima ridurre il working set applicativo, poi isolare i servizi, poi comprimere, e comprare RAM solo quando i numeri dimostrano che non c’è altra strada.
Questa è la parte finale della serie. Mette insieme le misure di pressione della parte 1 , i profili zram e zswap della parte 2 e i limiti systemd/cgroup della parte 3 in un piano da eseguire dall’inizio alla fine.
1. Definisci il carico e il budget
Niente di questo piano significa qualcosa senza una finestra di test ripetibile: stesso dataset, stessa concorrenza, stessa durata. Registra almeno:
date -Is
free -h
cat /proc/pressure/memory
vmstat 1 60
ps -eo pid,user,comm,rss,%mem --sort=-rss | head -n 25
Accanto ai numeri di sistema, cattura quelli applicativi — latenza p95/p99, throughput, tasso di errori, durata dei job. È il guard rail dell’intero esercizio: “meno RAM” non è un’ottimizzazione se arriva con metà del lavoro utile.
2. Elimina solo quello che è davvero inutile
Parti da quello che sta effettivamente girando:
systemctl --type=service --state=running
systemd-cgtop --depth=2
Per ogni candidato, rispondi a tre domande prima di toccarlo: chi lo usa, cosa dipende da lui, come lo riporto indietro. Solo allora:
sudo systemctl disable --now esempio.service
La tentazione qui è andare a caccia di ogni ultimo demone. Non disabilitare agenti di sicurezza, networking, logging o gestione cloud per risparmiare qualche MiB — il risparmio è irrisorio e la macchina che ti ritrovi è una che non riesci più ad amministrare.
3. Riduci concorrenza, heap e cache alla fonte
È qui che sta la memoria vera, ed è il passo che tutti saltano perché significa aprire le configurazioni applicative invece di digitare sysctl. Un limite cgroup protegge l’host; un dimensionamento applicativo corretto è quello che ti evita di colpire quel limite. Tre moltiplicatori vale la pena cercarli:
- Worker e thread. 32 processi worker da 200 MiB l’uno fanno 6,4 GiB prima ancora di contare le cache condivise. Il numero di worker di solito viene impostato una volta, con generosità, e mai più rivisto.
- Heap massimo. JVM, runtime e database espongono quasi sempre un tetto esplicito — controlla se il tuo riflette quello che serve all’applicazione o quello che qualcuno ha stimato anni fa.
- Cache senza limite. Qualsiasi cache in-process senza un limite in byte o elementi prima o poi ne troverà uno al posto tuo. Dalle un bound e una politica di espulsione.
Cambia una variabile alla volta, e giudica da throughput per GiB e latenza, non solo dall’RSS. Se dimezzare i worker taglia la RAM del 40% e il throughput solo del 5%, hai trovato un miglioramento reale. E se un servizio ha un leak: i restart periodici sono una mitigazione, non una correzione — non lasciare che diventino architettura permanente.
4. Metti un tetto a tmpfs
Facile da dimenticare: tmpfs cresce dentro memoria e swap come qualsiasi altra cosa. Guarda cosa hai:
findmnt -t tmpfs
df -h -t tmpfs
Per un mount che gestisci in /etc/fstab, imposta una dimensione coerente con il workload:
tmpfs /srv/app-tmp tmpfs rw,nosuid,nodev,size=1G 0 0
La dimensione è un massimo, non una preallocazione, quindi il tetto non costa nulla finché l’uso è normale. Prima di abbassarne uno esistente, misura il picco reale e prova come reagisce l’applicazione a ENOSPC. E controlla con cosa hai davvero a che fare — un /tmp su disco non è un tmpfs.
5. Applica i budget ai servizi
Ora applica il pattern della parte 3 ai servizi che contano: MemoryHigh poco sopra il working set normale, MemoryMax come ultima difesa, MemoryLow solo per i componenti davvero essenziali.
Per i job batch, uno scope transitorio ti permette di provare un budget senza scrivere nessun file di unità:
systemd-run --user --scope -p MemoryHigh=2G -p MemoryMax=2500M ./job.sh
Un’aspettativa da mettere in chiaro: il job deve saper gestire rallentamento, fallimento e retry. Un limite contiene un’applicazione affamata di memoria; non la rende efficiente.
6. Scegli zram o zswap
Se dopo tutto questo restano ancora pagine fredde e comprimibili, applica uno dei due profili della parte 2 — uno, non entrambi. Conserva la configurazione solo se il quadro completo migliora:
- PSI
someefullcalano; - swap-in/out su disco e attesa I/O calano;
- la CPU conserva margine;
- p95 e p99 non peggiorano;
- non compaiono nuovi OOM.
Se uno di questi punti fallisce, la compressione ti sta costando più di quello che rende. Toglila.
7. Non trasformare i sysctl in superstizione
Ho lasciato i sysctl per ultimi apposta, perché è lì che devono stare. Mantieni i default finché una misura non punta a un problema specifico:
vm.swappinessesprime il costo relativo del paging — non è un interruttore della RAM;vm.vfs_cache_pressurepuò recuperare dentry e inode più aggressivamente, e penalizzare i workload filesystem-intensive nel farlo;vm.overcommit_memoryregola l’ammissione delle allocazioni virtuali — non fa nulla al tuo working set;vm.drop_cachesè uno strumento da test, non manutenzione periodica;- le Transparent Huge Pages aiutano alcuni workload e ne danneggiano altri — non sono un risparmia-RAM generico.
Per ogni modifica che decidi di tenere, scrivi valore precedente, motivazione, data e comando di rollback. Il te stesso delle 3 di notte ringrazierà.
La tabella prima/dopo
Questo è il deliverable dell’intera serie. Esegui ogni configurazione almeno tre volte e compilala:
| KPI | Prima | Dopo | Obiettivo |
|---|---|---|---|
MemAvailable minimo | margine positivo | ||
| RSS/PSS picco del servizio | sotto budget | ||
PSI memory some avg60 | in calo | ||
PSI memory full avg60 | vicino a zero nel carico normale | ||
| swap-in al minuto | niente thrashing | ||
| latenza p95/p99 | entro SLO | ||
| throughput | invariato o migliore | ||
| OOM / restart | zero inattesi |
Non giudicare da un singolo picco, in nessuna delle due direzioni. Confronta mediane e casi peggiori su finestre equivalenti.
Quando comprare RAM è la risposta giusta
Dopo un’intera serie sul non comprare RAM, è giusto dire chiaramente quando invece va comprata. L’acquisto è razionale quando valgono tutte queste condizioni:
- il carico è utile e non eliminabile;
- non restano leak o cache senza limite;
- concorrenza e heap sono dimensionati correttamente;
- i cgroup impediscono a un singolo servizio di destabilizzare l’host;
- compressione e swap non rispettano gli obiettivi di latenza e CPU;
- PSI
full, paging o OOM restano, in modo ripetibile.
A quel punto non stai buttando soldi su un sintomo: stai comprando un working set fisico che i dati dicono non entrare. Anche a prezzi DRAM moltiplicati è la scelta giusta, e il benchmark è quello che ti protegge da entrambi i modi di sbagliare: l’upgrade prematuro e le settimane di tuning su una macchina semplicemente sottodimensionata.
La checklist
Tutto quello delle quattro parti, in ordine:
- Baseline registrata con carico e SLO
- Top processi verificati con RSS/PSS
- Servizi inutili rimossi, con rollback
- Worker, heap, cache e tmpfs limitati
-
MemoryHigheMemoryMaxtestati - zram oppure zswap misurato
- PSI, swap, CPU, latenza e throughput confrontati
- Decisione documentata: mantenere, ridimensionare o comprare