A la lliçó anterior vas trobar un problema de rendiment la causa del qual era un valor de configuració mal ajustat: max_connexions=200 contra un max_connections=100. El vas trobar mesurant, i el vas corregir comprovant-ne l'efecte. Aquesta lliçó fa el mateix un nivell més avall: els valors que ajustaràs són els del nucli.
I comença amb un advertiment, perquè l'ajust del nucli és l'àrea de l'administració de sistemes amb més folklore per metre quadrat. Internet és ple de llistes de paràmetres «per optimitzar Linux» que es copien sense entendre, que contradiuen els valors per defecte sense motiu, i que en el millor dels casos no fan res. La regla que separa la feina del ritual és una de sola:
No s'ajusta el que no s'ha mesurat.
I el procediment que l'aplica, que és el mateix que ja has fet servir dues vegades —a l'incident del web lent de 05-07 i al de la latència de 07-02—:
- Mesurar l'estat actual i anotar-lo. Sense línia base no hi ha «després».
- Formular una hipòtesi concreta: quin paràmetre, per què creus que importa, i què esperes que canviï.
- Canviar una sola cosa. Dos canvis simultanis fan impossible atribuir el resultat.
- Mesurar de nou amb el mateix mètode.
- Documentar el canvi amb el seu motiu, o revertir-lo. Un paràmetre sense justificació escrita és un paràmetre que ningú no podrà treure d'aquí a un any.
Els valors per defecte d'Ubuntu 24.04 són raonables per a la majoria de les càrregues. Canviar-los sense una mesura que ho avali és, amb força probabilitat, empitjorar el sistema.
Contingut
- sysctl: la interfície de paràmetres del nucli
- Memòria virtual
- Xarxa i pila TCP
- Límits de fitxers i processos
- Planificador d'E/S
- Pàgines enormes transparents
- Límits per procés i qui guanya
- Governadors de freqüència i tuned
- Mòduls del nucli
- Compilar el nucli: quan té sentit i quan no
- Cas Tramontana: ajustar la pila de xarxa amb mesura
sysctl: la interfície de paràmetres del nucli
El nucli exposa els seus paràmetres ajustables com a fitxers sota /proc/sys/. És el «tot és un fitxer» del Mòdul 1 aplicat a la configuració del nucli: llegir el paràmetre és llegir un fitxer, canviar-lo és escriure-hi.
$ cat /proc/sys/vm/swappiness
60
$ sudo sh -c 'echo 10 > /proc/sys/vm/swappiness'
$ cat /proc/sys/vm/swappiness
10sysctl és l'eina que fa el mateix amb una sintaxi de punts, on cada punt és una barra de la ruta:
$ sysctl vm.swappiness # equival a /proc/sys/vm/swappiness
vm.swappiness = 10
$ sysctl -a 2>/dev/null | wc -l
1247
$ sysctl -a 2>/dev/null | grep -c '^net\.'
684Gairebé mil tres-cents paràmetres, dels quals dos terços són de xarxa. La immensa majoria no es toquen mai.
Els tres modes d'operació, i quin fer servir quan:
| Operació | Ordre | Persisteix en reiniciar |
|---|---|---|
| Consultar | sysctl <parametre> |
— |
| Canviar ara, per provar | sudo sysctl -w <parametre>=<valor> |
No |
| Canviar permanentment | Fitxer a /etc/sysctl.d/ + sysctl --system |
Sí |
Aquell -w efímer és l'eina del pas 3 del procediment: permet provar un canvi, mesurar, i si empitjora les coses n'hi ha prou de reiniciar —o tornar a escriure el valor anterior— per desfer-ho. Mai es posa un paràmetre directament a /etc/sysctl.d/ sense haver-lo provat abans en calent.
La persistència i la seva precedència:
$ ls /etc/sysctl.d/
10-console-messages.conf 10-network-security.conf 60-enfortiment.conf
10-ipv6-privacy.conf 10-ptrace.conf 99-sysctl.conf
$ sudo tee /etc/sysctl.d/70-rendiment.conf >/dev/null <<'EOF'
# Ajustos de rendiment. Cada linia amb el seu motiu i la seva mesura.
vm.swappiness = 10
EOF
$ sudo sysctl --system 2>&1 | grep -A1 70-rendiment
* Applying /etc/sysctl.d/70-rendiment.conf ...
vm.swappiness = 10Els fitxers s'apliquen en ordre alfabètic, i l'últim a escriure un paràmetre guanya. D'aquí la convenció de numerar-los: 10- per al que ve de la distribució, 60- a 90- per al propi, i 99- per al que s'ha d'imposar a tot. I una nota important: /etc/sysctl.conf continua funcionant, però està en desús; fes servir /etc/sysctl.d/ amb fitxers separats per propòsit.
Fixa't que ja tens 60-enfortiment.conf, de 06-06, amb els paràmetres de seguretat. Els de rendiment van en un fitxer a part, i aquella separació no és estètica: quan algú hagi de revisar la postura de seguretat, o quan calgui revertir un ajust de rendiment, es toca un sol fitxer i no es barreja el que es pot treure amb el que no.
Memòria virtual
El subsistema de memòria virtual és on els ajustos tenen més impacte i on hi ha més mitologia. Els paràmetres que importen:
vm.swappiness
El més mal entès de tots. No és «quant intercanvi fer servir», ni un percentatge de memòria. És la preferència relativa del nucli entre dues maneres d'alliberar memòria quan li'n cal: descartar pàgines de memòria cau de fitxer, o moure pàgines anònimes (memòria de procés) a l'intercanvi.
| Valor | Comportament | Quan |
|---|---|---|
0 |
Només fa servir l'intercanvi per evitar l'OOM killer | Gairebé mai; pot provocar OOM abans d'hora |
1 |
El mínim possible sense desactivar-lo | Bases de dades amb tota la RAM assignada |
10 |
Prefereix descartar memòria cau | Servidors amb RAM suficient |
60 |
Per defecte | Escriptori i ús general |
100 |
Tracta memòria cau i anònimes igual | Càrregues on la memòria cau és més valuosa |
I la dada que cal interioritzar abans de tocar-lo: si el sistema no fa servir intercanvi, swappiness no fa res. És un paràmetre que només es manifesta sota pressió de memòria. Abans de canviar-lo, mesura si hi ha pressió:
# La mesura previa: hi ha activitat d intercanvi? (columnes si/so)
$ vmstat 5 4
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 1284412 104882 1841204 0 0 0 12 412 882 3 2 95 0 0
0 0 0 1284188 104882 1841204 0 0 0 8 388 841 2 2 96 0 0
# I l acumulat des de l arrencada
$ grep -E 'pswpin|pswpout' /proc/vmstat
pswpin 0
pswpout 0Zero entrades i zero sortides d'intercanvi des de l'arrencada. A srv-tramontana, amb 3,8 GB i 2,4 GB disponibles, swappiness és irrellevant avui. Abaixar-lo no milloraria res.
Val la pena canviar-lo, doncs? Sí, però per un motiu diferent del rendiment mitjà: com a assegurança contra la degradació sota pressió. Si un dia l'aplicació creix i comença a haver-hi pressió, amb swappiness=10 el nucli preferirà descartar memòria cau abans que enviar a l'intercanvi les pàgines actives del procés, cosa que evita l'escenari de latències de centenars de mil·lisegons. És una decisió defensable, i així cal documentar-la: no «això accelera el servidor», sinó «això limita el dany si algun dia falta memòria».
vm.dirty_ratio i vm.dirty_background_ratio
Aquests sí que tenen efecte mesurable, i són la causa d'un patró de latència molt característic.
Quan un procés escriu en un fitxer, les dades van primer a la memòria cau de pàgina i es marquen com a brutes (dirty); el nucli les aboca al disc més tard. Aquests dos paràmetres controlen quan:
$ sysctl vm.dirty_background_ratio vm.dirty_ratio vm.dirty_expire_centisecs
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20
vm.dirty_expire_centisecs = 3000| Paràmetre | Què passa en assolir-lo |
|---|---|
dirty_background_ratio |
El nucli comença a abocar en segon pla, sense bloquejar ningú |
dirty_ratio |
El procés que escriu es bloqueja fins que s'aboca prou |
dirty_expire_centisecs |
Antiguitat màxima d'una pàgina bruta abans d'abocar-la (30 s) |
El patró que produeixen: escriptures ràpides mentre hi ha marge, i de cop una pausa llarga quan s'assoleix dirty_ratio i el procés queda bloquejat. Amb 3,8 GB de RAM, el 20 % són uns 760 MB de dades brutes que poden haver d'anar-se'n al disc de cop.
Això és exactament el perfil de l'incident de 05-07: la còpia de seguretat escrivint a ràfegues, amb w_await de 22,85 ms i l'aplicació bloquejada. ionice va mitigar el símptoma; aquests paràmetres ataquen el mecanisme.
# Mesura previa: quantes dades brutes hi ha en un moment donat
$ grep -E '^Dirty|^Writeback' /proc/meminfo
Dirty: 1024 kB
Writeback: 0 kB
# I durant la copia, amb el volum xifrat com a desti
$ while true; do grep '^Dirty:' /proc/meminfo; sleep 2; done
Dirty: 412844 kB
Dirty: 688204 kB # s acosta al 20% de 3,8 GB
Dirty: 12408 kB # abocat de cop: aqui esta la pausaEls valors més petits produeixen abocaments més freqüents i petits, amb latència més uniforme:
I l'advertiment honest: això no fa el sistema «més ràpid» en cabal total —de vegades el fa una mica més lent— sinó més predictible. Per a un servei interactiu, la latència uniforme val més que el cabal màxim. Per a una còpia de seguretat aïllada, el contrari. És un compromís, no una millora.
La resta de la memòria virtual
| Paràmetre | Per defecte | Què fa | Quan tocar-lo |
|---|---|---|---|
vm.vfs_cache_pressure |
100 | Agressivitat en recuperar memòria cau d'inodes i dentries | Abaixar a 50 si hi ha molts fitxers petits i es rellegeix molt |
vm.overcommit_memory |
0 | 0 heurístic, 1 permetre-ho tot, 2 estricte | 1 per a Redis; 2 només amb overcommit_ratio ben calculat |
vm.min_free_kbytes |
~45000 | Reserva mínima lliure | Pujar si hi ha fallades d'assignació en ràfegues de xarxa |
vm.max_map_count |
65530 | Regions de memòria per procés | Pujar per a Elasticsearch i bases de dades grans |
vm.overcommit_memory=2 mereix un avís: sembla prudent («no prometis memòria que no tens») i a la pràctica fa que aplicacions perfectament normals fallin en reservar, perquè moltes en reserven molta més de la que fan servir. No es toca sense una raó molt concreta.
Xarxa i pila TCP
Aquí és on srv-tramontana té marge real, i on l'incident de 07-02 va deixar una pista sense explorar.
Cues de connexions entrants
Quan arriba una connexió TCP, passa per dues cues abans que l'aplicació l'accepti:
$ sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlog
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 1024| Paràmetre | Quina cua controla |
|---|---|
tcp_max_syn_backlog |
Connexions a mig establir: va arribar el SYN, falta l'ACK final |
somaxconn |
Connexions establertes esperant que l'aplicació les accepti |
I el detall crucial que fa que aquest paràmetre s'ajusti malament la meitat de les vegades: somaxconn és un sostre, no un valor efectiu. La cua real és el mínim entre somaxconn i el paràmetre backlog que l'aplicació passa a la crida listen(). Pujar somaxconn a 65535 no serveix de res si l'aplicació en demana 128.
La mesura que diu si la cua s'està omplint:
# La columna Recv-Q en un socol LISTEN es la cua d acceptacio pendent;
# Send-Q es el maxim efectiu (el minim entre somaxconn i el backlog de l app)
$ ss -ltn
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 4096 10.0.2.15:5432 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
# El comptador de connexions descartades per cua plena: LA metrica que importa
$ nstat -az | grep -iE 'ListenOverflows|ListenDrops'
TcpExtListenOverflows 0 0.0
TcpExtListenDrops 0 0.0Aquell Send-Q 128 del port 8080 és la informació valuosa: l'aplicació demana un backlog de 128, així que el somaxconn de 4096 no l'afecta en absolut. I ListenOverflows a zero significa que cap connexió no s'ha descartat per cua plena. Conclusió: pujar somaxconn en aquesta màquina no faria res, i qui el posés al seu sysctl.d estaria afegint folklore.
Això és el procediment funcionant: la hipòtesi raonable («les cues de connexió són el coll») queda refutada per la mesura abans de tocar res. Refutar hipòtesis és tan valuós com confirmar-les, i força més barat.
Ports efímers i connexions en espera
Aquí sí que hi ha una cosa que l'incident de 07-02 va deixar a la vista. Quan l'aplicació obria 300 connexions cada deu segons, cadascuna consumia un port efímer que quedava en estat TIME_WAIT durant 60 segons:
$ sysctl net.ipv4.ip_local_port_range net.ipv4.tcp_fin_timeout net.ipv4.tcp_tw_reuse
net.ipv4.ip_local_port_range = 32768 60999
net.ipv4.tcp_fin_timeout = 60
net.ipv4.tcp_tw_reuse = 2
$ ss -s
Total: 284
TCP: 112 (estab 24, closed 74, orphaned 0, timewait 71)Setanta-un sòcols en TIME_WAIT. Amb 28.231 ports disponibles no és un problema, però durant l'incident, amb 30 connexions per segon, se'n van arribar a acumular uns 1.800 — i una aplicació que n'obrís deu vegades més els esgotaria.
TIME_WAIT no és un defecte: garanteix que paquets endarrerits d'una connexió tancada no es confonguin amb una de nova que reutilitzi la mateixa parella de ports. Els ajustos possibles:
| Ajust | Efecte | Risc |
|---|---|---|
Ampliar ip_local_port_range a 1024 65535 |
Més ports disponibles | Pot xocar amb serveis que escoltin en ports alts |
tcp_tw_reuse = 1 |
Reutilitzar sòcols en TIME_WAIT per a connexions sortints |
Cap de rellevant avui; requereix marques de temps TCP |
Abaixar tcp_fin_timeout |
Menys temps en TIME_WAIT |
Reintrodueix el risc que TIME_WAIT evita |
tcp_tw_recycle |
— | Eliminat del nucli a la 4.12. No existeix. |
Aquell últim és l'exemple perfecte de folklore: tcp_tw_recycle apareix en centenars de guies «per optimitzar TCP», trencava les connexions des de xarxes amb NAT de forma subtil i intermitent, i va ser eliminat del nucli fa anys. Si trobes una guia que el recomana, saps que el seu autor no l'ha provada en aquesta dècada.
A Ubuntu 24.04, tcp_tw_reuse ja ve en 2 (habilitat només per a adreces de bucle i enllaç local), que és un valor prudent. La correcció real del problema de 07-02 no era cap sysctl: era arreglar el grup de connexions, que és el que vas fer.
Memòries intermèdies de sòcol
$ sysctl net.core.rmem_max net.core.wmem_max net.ipv4.tcp_rmem net.ipv4.tcp_wmem
net.core.rmem_max = 212992
net.core.wmem_max = 212992
net.ipv4.tcp_rmem = 4096 131072 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304Els tres valors de tcp_rmem són mínim, per defecte i màxim, i el nucli els ajusta automàticament dins d'aquell rang. Ampliar el màxim importa quan el producte amplada de banda × latència és gran: un enllaç d'1 Gbit/s amb 100 ms d'anada i tornada necessita uns 12 MB de finestra per saturar-se, i amb 6 MB de màxim es queda a la meitat.
En una xarxa local amb 0,4 ms de latència, com la de Tramontana, això és completament irrellevant. És un ajust per a transferències de llarga distància, i posar-lo «per si de cas» només consumeix memòria.
BBR: l'ajust de xarxa que sí que val la pena
El control de congestió decideix a quina velocitat envia TCP quan detecta problemes a la xarxa. L'algorisme clàssic, CUBIC, interpreta la pèrdua de paquets com a senyal de congestió. BBR, desenvolupat per Google, modela en canvi l'amplada de banda i la latència reals del camí.
La diferència pràctica és gran en enllaços amb pèrdua o amb memòries intermèdies grans (el problema del bufferbloat), que és el cas de gairebé qualsevol connexió a través d'Internet:
$ sysctl net.ipv4.tcp_available_congestion_control net.ipv4.tcp_congestion_control
net.ipv4.tcp_available_congestion_control = reno cubic
net.ipv4.tcp_congestion_control = cubic
# Carregar el modul de BBR
$ sudo modprobe tcp_bbr
$ sysctl net.ipv4.tcp_available_congestion_control
net.ipv4.tcp_available_congestion_control = reno cubic bbr
# Mesurar ABANS, amb el client a l altre costat de la xarxa
$ iperf3 -c 198.51.100.20 -t 20 -R | tail -3
[ 5] 0.00-20.00 sec 184 MBytes 77.2 Mbits/sec receiver
# Canviar (efimer) i mesurar DESPRES
$ sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
$ sudo sysctl -w net.core.default_qdisc=fq
$ iperf3 -c 198.51.100.20 -t 20 -R | tail -3
[ 5] 0.00-20.00 sec 441 MBytes 185 Mbits/sec receiverDe 77 a 185 Mbit/s en el mateix enllaç, i amb latència menor sota càrrega. default_qdisc=fq no és opcional: BBR necessita l'encuament fair queue per funcionar tal com està dissenyat, i sense ell el resultat és pitjor.
Amb la mesura feta, el canvi es fa persistent amb el seu motiu escrit:
$ sudo tee -a /etc/sysctl.d/70-rendiment.conf >/dev/null <<'EOF'
# Control de congestio BBR. Mesurat amb iperf3 contra un client remot:
# 77 -> 185 Mbit/s de baixada, i menor latencia sota carrega. Requereix fq.
# Mesura: 2026-08-18. Revertir amb cubic si apareixen anomalies.
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF
$ echo 'tcp_bbr' | sudo tee /etc/modules-load.d/bbr.conf
$ sudo sysctl --system >/dev/null && sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = bbrFixa't en el comentari: què es va mesurar, amb què, el resultat, la data i com revertir-ho. Això és la diferència entre un ajust i una línia copiada.
Límits de fitxers i processos
$ sysctl fs.file-max fs.file-nr kernel.pid_max fs.inotify.max_user_watches
fs.file-max = 9223372036854775807
fs.file-nr = 2848 0 9223372036854775807
kernel.pid_max = 4194304
fs.inotify.max_user_watches = 65536fs.file-max en nuclis moderns és pràcticament il·limitat, així que pujar-lo —un altre clàssic de les guies— no fa res. El límit real que s'assoleix a la pràctica és el per procés, que es veu a l'apartat següent.
fs.inotify.max_user_watches sí que s'esgota de debò, i amb un error desconcertant. Cada inotify watch consumeix una entrada, i eines de desenvolupament, sincronitzadors de fitxers i logrotate en fan un ús intensiu:
# El simptoma: "No space left on device" sense que falti espai al disc
$ df -h / | tail -1
/dev/mapper/vg-root 23G 7,1G 15G 33% /
# La causa real
$ find /proc/*/fd -lname anon_inode:inotify 2>/dev/null | wc -l
1284
$ sudo sysctl -w fs.inotify.max_user_watches=524288Aquell ENOSPC que no és falta de disc és una de les confusions més freqüents de Linux, i mereix ser al manual d'operació.
Planificador d'E/S
El planificador decideix en quin ordre se serveixen les peticions al disc. Ubuntu 24.04 fa servir la infraestructura de multicua (blk-mq):
$ cat /sys/block/sda/queue/scheduler
[none] mq-deadline kyber bfq
$ lsblk -d -o NAME,ROTA,SCHED
NAME ROTA SCHED
sda 1 none| Planificador | Com funciona | Per a quin dispositiu |
|---|---|---|
none |
Sense reordenació; FIFO per cua | NVMe i SSD ràpids |
mq-deadline |
Terminis per petició, evita la inanició | SATA SSD i discos mecànics de servidor |
kyber |
Objectius de latència adaptatius | Càrregues mixtes amb moltes cues |
bfq |
Repartiment equitatiu per procés | Escriptori; interactivitat per sobre del cabal |
La regla de none per a NVMe té un perquè concret que cal entendre: un planificador reordena peticions per minimitzar el moviment del capçal d'un disc mecànic. Un NVMe no té capçal, atén desenes de milers d'operacions per segon i té les seves pròpies cues al maquinari. Reordenar només hi afegeix latència i consum de CPU. En un disc mecànic, en canvi, reordenar pot multiplicar el cabal.
# Canvi efimer, per provar
$ echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler
$ cat /sys/block/sda/queue/scheduler
none [mq-deadline] kyber bfqCom que /sys no és persistent, la manera correcta de fixar-ho és una regla d'udev que decideixi segons el tipus de dispositiu:
$ sudo tee /etc/udev/rules.d/60-planificador-io.rules >/dev/null <<'EOF'
# NVMe: sense planificador. El dispositiu te les seves propies cues.
ACTION=="add|change", KERNEL=="nvme[0-9]*n[0-9]*", ATTR{queue/scheduler}="none"
# Discos rotacionals (ROTA=1): mq-deadline reordena i aprofita la localitat.
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", \
ATTR{queue/scheduler}="mq-deadline"
# SSD SATA (ROTA=0): mq-deadline tambe, pels seus terminis.
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", \
ATTR{queue/scheduler}="mq-deadline"
EOF
$ sudo udevadm control --reload && sudo udevadm trigger --subsystem-match=block
$ cat /sys/block/sda/queue/scheduler
none [mq-deadline] kyber bfqI dos paràmetres més de la cua que de vegades importen:
read_ahead_kb és quant llegeix el nucli per endavant. Pujar-lo (a 512 o més) ajuda en lectures seqüencials grans —una còpia de seguretat, un abocament de base de dades—; abaixar-lo ajuda en accessos aleatoris petits, perquè evita llegir dades que no es faran servir. És exactament el compromís que cal mesurar abans de tocar.
Una observació sobre la pila de Tramontana: el volum de còpies és LUKS sobre LVM sobre sda, i cada capa presenta el seu propi dispositiu de bloc amb la seva pròpia cua. El planificador que importa és el del dispositiu físic (sda); els dispositius dm-* passen les peticions cap avall. És la mateixa pila que calia descompondre al primer exercici de 07-02.
Pàgines enormes transparents
El processador tradueix adreces virtuals a físiques en pàgines de 4 KB, fent servir una memòria cau anomenada TLB. Amb molta memòria, la TLB falla sovint i cada fallada costa accessos extra a memòria. Les pàgines enormes de 2 MB redueixen dràsticament el nombre d'entrades necessàries.
THP (Transparent Huge Pages) fa això automàticament. I és el paràmetre que totes les bases de dades demanen desactivar:
$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
$ cat /sys/kernel/mm/transparent_hugepage/defrag
always defer [defer+madvise] madvise never| Valor | Comportament |
|---|---|
always |
THP per a tot. Per defecte a Ubuntu |
madvise |
Només per a processos que ho demanen amb madvise(MADV_HUGEPAGE) |
never |
Desactivat |
Per què les bases de dades ho rebutgen, que és la part interessant: per donar una pàgina de 2 MB, el nucli necessita 2 MB físicament contigus. Quan la memòria està fragmentada, l'ha de compactar, i aquella compactació passa de forma síncrona en el context del procés que demana memòria. El resultat són pauses impredictibles de desenes o centenars de mil·lisegons justament al procés que menys les tolera. PostgreSQL, MongoDB, Redis, Oracle i Elasticsearch documenten tots la recomanació de madvise o never.
madvise és el millor compromís: els processos que se'n beneficien ho demanen explícitament, i els altres no en paguen el cost.
Com que /sys no persisteix, es fixa amb una unitat de systemd —aplicant 05-05—, que és més net que un rc.local:
$ sudo tee /etc/systemd/system/thp-madvise.service >/dev/null <<'EOF'
[Unit]
Description=Posar transparent hugepages en madvise (recomanat per PostgreSQL)
Documentation=https://www.postgresql.org/docs/16/kernel-resources.html
DefaultDependencies=no
After=sysinit.target local-fs.target
Before=postgresql.service tramontana.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled'
[Install]
WantedBy=basic.target
EOF
$ sudo systemctl daemon-reload && sudo systemctl enable --now thp-madvise.service
$ cat /sys/kernel/mm/transparent_hugepage/enabled
always [madvise] neverEl Before=postgresql.service importa: el canvi ha d'estar aplicat abans que la base de dades reservi la seva memòria compartida.
Límits per procés i qui guanya
Un límit del nucli és global; els que s'assoleixen a la pràctica són els per procés, i aquí hi ha tres mecanismes que es trepitgen entre ells. Saber quin guanya estalvia hores de desconcert.
| Mecanisme | On | A qui s'aplica |
|---|---|---|
ulimit |
Ordre del shell | El procés actual i els seus fills |
/etc/security/limits.conf |
PAM (pam_limits) |
Només sessions que passen per PAM (login, SSH, su) |
LimitNOFILE= a la unitat |
systemd | Els serveis de systemd |
DefaultLimitNOFILE= |
/etc/systemd/system.conf |
Tots els serveis sense límit propi |
I la resposta a «quin guanya», que és la raó d'aquest apartat: per a un servei de systemd, guanya la unitat, i limits.conf no s'aplica en absolut. systemd arrenca els serveis directament, sense passar per PAM. Posar svc-tramontana hard nofile 16384 a limits.conf i esperar que afecti tramontana.service és un error clàssic que no dona cap avís: simplement no funciona.
# Comprovar el limit REAL d un servei en marxa: la font de veritat
$ pid=$(systemctl show tramontana.service -p MainPID --value)
$ grep -E 'Max open files|Max processes' /proc/$pid/limits
Max open files 1024 524288 files
Max processes 15370 15370 processesAmb MemoryMax=512M i TasksMax=64 ja posats a 05-05, el límit de fitxers oberts s'ajusta igual, per drop-in:
$ sudo tee /etc/systemd/system/tramontana.service.d/limits.conf >/dev/null <<'EOF'
[Service]
# 80 connexions al grup de PostgreSQL + socols de client + registres + marge.
# Mesurat despres de l ajust de 07-02: pic de 214 descriptors.
LimitNOFILE=8192
EOF
$ sudo systemctl daemon-reload && sudo systemctl restart tramontana.service
$ pid=$(systemctl show tramontana.service -p MainPID --value)
$ grep 'Max open files' /proc/$pid/limits
Max open files 8192 8192 files
# I verificar l us real, per saber si 8192 es raonable o excessiu
$ ls /proc/$pid/fd | wc -l
214214 descriptors en ús amb un límit de 8192: marge de sobres sense ser absurd. Aquell és el criteri — un límit es dimensiona a partir de l'ús mesurat, no d'un número rodó copiat.
Governadors de freqüència i tuned
$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null \
|| echo "sense cpufreq (habitual en una VM)"
sense cpufreq (habitual en una VM)En una màquina virtual la gestió de freqüència la fa l'amfitrió, així que aquest apartat no s'aplica a srv-tramontana. En maquinari físic:
| Governador | Comportament | Quan |
|---|---|---|
powersave |
Freqüència mínima, puja segons demanda | Per defecte; amb intel_pstate és raonable |
performance |
Freqüència màxima sempre | Servidors sensibles a la latència |
schedutil |
Guiat pel planificador | El modern; bon equilibri |
El cas d'ús real de performance és la latència de cua: pujar de freqüència triga microsegons, i en un servei amb objectius de latència estrictes aquells microsegons apareixen al percentil 99. A canvi de força més consum elèctric.
tuned és la manera sensata d'aplicar conjunts coherents d'ajustos en lloc de paràmetres solts:
$ sudo apt install tuned
$ sudo tuned-adm list
Available profiles:
- balanced - General non-specialized tuned profile
- latency-performance - Optimize for deterministic performance
- network-latency - Optimize for deterministic performance, low latency
- network-throughput - Optimize for streaming network throughput
- throughput-performance - Broadly applicable tuning for throughput
- virtual-guest - Optimize for running inside a virtual guest
$ sudo tuned-adm active
Current active profile: virtual-guest
# Veure EXACTAMENT que canvia un perfil abans d aplicar-lo
$ cat /usr/lib/tuned/throughput-performance/tuned.conf | grep -A12 '\[sysctl\]'Aquella última ordre és el consell important: tuned és útil perquè aplica conjunts provats i consistents, però llegir què fa un perfil abans d'activar-lo és obligatori. Un perfil pot canviar vint paràmetres, i si un d'ells entra en conflicte amb el teu sysctl.d, el resultat depèn de l'ordre d'aplicació i és difícil de depurar.
Mòduls del nucli
Un mòdul és un tros de nucli que es carrega i es descarrega en calent: controladors, sistemes de fitxers, protocols.
$ lsmod | head -5
Module Size Used by
tcp_bbr 24576 20
dm_crypt 65536 1
raid1 49152 0
vboxguest 57344 2
$ modinfo tcp_bbr | head -5
filename: /lib/modules/6.8.0-41-generic/kernel/net/ipv4/tcp_bbr.ko.zst
license: Dual BSD/GPL
description: TCP BBR (Bottleneck Bandwidth and RTT)
depends:
intree: Y| Operació | Ordre |
|---|---|
| Llistar els carregats | lsmod |
| Informació | modinfo <modul> |
| Carregar (amb dependències) | sudo modprobe <modul> |
| Descarregar | sudo modprobe -r <modul> |
| Veure els paràmetres actuals | systool -v -m <modul> |
La configuració persistent:
# Carregar un modul a l arrencada (ho vas fer servir per a BBR)
$ echo 'tcp_bbr' | sudo tee /etc/modules-load.d/bbr.conf
# Passar parametres a un modul
$ sudo tee /etc/modprobe.d/tramontana.conf >/dev/null <<'EOF'
# Redueix l us de memoria del modul de xifratge en una VM petita
options dm_crypt max_read_size=131072
EOF
# Impedir que un modul es carregui: reduccio de superficie d atac (06-06)
$ sudo tee /etc/modprobe.d/blacklist-tramontana.conf >/dev/null <<'EOF'
# Sistemes de fitxers i protocols que aquest servidor no fa servir mai.
# Cada linia redueix superficie d atac: son moduls amb CVE historics.
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install udf /bin/true
install dccp /bin/true
install sctp /bin/true
install rds /bin/true
install tipc /bin/true
EOF
$ sudo update-initramfs -u -k allDos detalls importants. El primer: install <modul> /bin/true és més eficaç que blacklist <modul>, perquè blacklist només evita la càrrega automàtica i no impedeix que un altre mòdul el carregui com a dependència. El segon: després de tocar modprobe.d, cal regenerar l'initramfs, pel que vas aprendre a 07-01 — l'initramfs porta la seva pròpia còpia d'aquella configuració.
Aquesta llista de mòduls bloquejats és, de fet, una recomanació dels CIS Benchmarks que va quedar pendent a la llista de comprovació de 06-06. És un bon moment per tancar-la.
DKMS (Dynamic Kernel Module Support) resol el problema dels mòduls de tercers: un mòdul compilat per al nucli 6.8.0-41 no funciona al 6.8.0-45. DKMS el recompila automàticament a cada actualització del nucli:
$ dkms status
virtualbox-guest/7.0.16, 6.8.0-41-generic, x86_64: installed
virtualbox-guest/7.0.16, 6.8.0-39-generic, x86_64: installedAmb Secure Boot actiu, un mòdul recompilat per DKMS necessita signatura, i aquí entra el mmx64.efi (MokManager) que vas veure a 07-01. És la causa habitual que un controlador de tercers deixi de funcionar després d'actualitzar el nucli en una màquina amb Secure Boot.
Compilar el nucli: quan té sentit i quan no
Compilar el nucli és un ritu de pas en l'aprenentatge de Linux, i convé ser honest sobre la seva utilitat en un servidor de producció: gairebé mai no en té.
| Motiu al·legat | Compensa? |
|---|---|
| «Un nucli més petit i ràpid» | No. Els mòduls no carregats no consumeixen res. El guany és indetectable |
| «Necessito una opció no habilitada» | De vegades. Primer comprova si existeix un paquet d'Ubuntu que ja la porta |
| «Necessito un pedaç que no és a cap versió» | Sí. És el cas legítim |
| «Necessito una versió molt recent per a un controlador» | No. Fes servir el nucli HWE o linux-generic-hwe-24.04 |
| «Per aprendre» | Sí, i al laboratori, no en producció |
I el cost que se subestima: un nucli compilat a mà queda fora del sistema de paquets. No rep actualitzacions de seguretat automàtiques —adeu a l'unattended-upgrades de 05-03—, no està signat per a Secure Boot, i cada CVE del nucli exigeix recompilar a mà. En una màquina que tracta dades personals, això és un problema de compliment, no només de comoditat.
Les variants empaquetades cobreixen gairebé tot:
| Paquet | Per a què |
|---|---|
linux-image-generic |
Ús general. És el que tens |
linux-image-virtual |
Màquines virtuals: sense controladors de maquinari físic, més petit |
linux-image-generic-hwe-24.04 |
Nucli més recent sobre la LTS, per a maquinari nou |
linux-image-lowlatency |
Menor latència de planificació, menor cabal |
El procediment, per al laboratori:
$ sudo apt install build-essential libncurses-dev bison flex libssl-dev \
libelf-dev dwarves zstd
$ apt source linux-image-unsigned-$(uname -r)
$ cd linux-6.8.0
# Partir de la configuracio ACTUAL, mai de zero
$ cp /boot/config-$(uname -r) .config
$ make olddefconfig # ajusta la config antiga a les opcions noves
$ make menuconfig # nomes el canvi que necessites
$ make -j"$(nproc)" 2>&1 | tail -3
$ sudo make modules_install
$ sudo make install # copia vmlinuz i executa update-initramfs
$ sudo update-grub
# I la xarxa de seguretat de 07-01: el nucli anterior continua al menu
$ awk -F"'" '/menuentry .Ubuntu, with Linux/ {print $2}' /boot/grub/grub.cfgmake olddefconfig partint de /boot/config-$(uname -r) és el que evita l'error del principiant: make defconfig genera una configuració genèrica que probablement no inclogui el controlador del teu controlador de disc, i el resultat és l'indicador (initramfs) de 07-01.
Cas Tramontana: ajustar la pila de xarxa amb mesura
L'exercici complet, aplicant el procediment de cinc passos. Punt de partida: la línia base de 05-07, l'incident dels db_timeout amb connexions_actives=200, i l'ajust del grup de connexions de 07-02.
Pas 1: mesurar l'estat actual
$ cat ~/scripts/linia_base_nucli.sh
#!/usr/bin/env bash
# linia_base_nucli.sh - Registra els parametres i comptadors del nucli
# rellevants per al rendiment, per poder comparar abans i despres.
# Us: linia_base_nucli.sh [etiqueta]
set -euo pipefail
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib/comuns.sh
source "${SCRIPT_DIR}/lib/comuns.sh"
readonly ETIQUETA_LOG="linia-base-nucli"
readonly DESTI="${TRAMONTANA_BASE_DIR:-/home/operador/dades/linies-base}"
main() {
local etiqueta="${1:-manual}"
local data; data="$(date +%Y%m%d-%H%M%S)"
local fitxer="${DESTI}/nucli-${etiqueta}-${data}.txt"
install -d -m 750 "$DESTI"
{
printf '# Linia base de nucli — %s — %s\n\n' "$etiqueta" "$(date -Is)"
printf '## Parametres\n'
sysctl vm.swappiness vm.dirty_ratio vm.dirty_background_ratio \
net.core.somaxconn net.ipv4.tcp_max_syn_backlog \
net.ipv4.tcp_congestion_control net.core.default_qdisc \
net.ipv4.ip_local_port_range 2>/dev/null
printf '\n## Planificador d E/S\n'
for d in /sys/block/sd*/queue/scheduler; do
printf '%s: %s\n' "$d" "$(cat "$d")"
done
printf '\n## Comptadors de xarxa (els que importen)\n'
nstat -az 2>/dev/null | grep -iE 'ListenOverflows|ListenDrops|TCPSynRetrans|RetransSegs'
printf '\n## Socols\n'
ss -s
printf '\n## Cues d escolta\n'
ss -ltn
printf '\n## Intercanvi\n'
grep -E 'pswpin|pswpout' /proc/vmstat
printf '\n## Latencia de l aplicacio (5 mostres)\n'
for _ in {1..5}; do
curl -s -o /dev/null -w '%{time_total}\n' \
"http://127.0.0.1:${TRAMONTANA_PORT:-8080}/cases" || true
done
} >"$fitxer"
chmod 640 "$fitxer"
log "linia base escrita a $fitxer"
printf '%s\n' "$fitxer"
}
main "$@"$ chmod +x ~/scripts/linia_base_nucli.sh
$ shellcheck ~/scripts/linia_base_nucli.sh && ~/scripts/linia_base_nucli.sh abans
[2026-08-18 17:12:04] linia-base-nucli: linia base escrita a /home/operador/dades/linies-base/nucli-abans-20260818-171204.txtPas 2: hipòtesis, i la que es refuta
Tres hipòtesis raonables a partir de l'historial, i què diu la mesura de cadascuna:
| Hipòtesi | Mesura | Veredicte |
|---|---|---|
| Les cues de connexió es desborden | ListenOverflows = 0, Send-Q = 128 (backlog de l'app) |
Refutada. somaxconn no hi intervé |
| Hi ha pressió de memòria i intercanvi | pswpin = 0, pswpout = 0, 2,4 GB disponibles |
Refutada avui; s'ajusta com a assegurança |
| El control de congestió limita el trànsit remot | cubic, i 77 Mbit/s mesurats amb iperf3 |
Confirmada. BBR dona 185 Mbit/s |
Dues de tres refutades per la mesura, i aquest és el resultat normal. Si haguessis copiat una guia d'«optimització de xarxa per a Linux», hauries posat somaxconn = 65535 —que aquí no fa res— i probablement tcp_tw_recycle, que ja no existeix.
Passos 3 i 4: un canvi, mesurar
# L unic canvi confirmat per la mesura, primer en efimer
$ sudo modprobe tcp_bbr
$ sudo sysctl -w net.core.default_qdisc=fq
$ sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
$ iperf3 -c 198.51.100.20 -t 20 -R | tail -2
[ 5] 0.00-20.00 sec 441 MBytes 185 Mbits/sec receiver
# I verificar que no ha empitjorat res mes
$ ~/scripts/revisio_salut.sh; echo "estat: $?"
estat: 0
$ ~/scripts/linia_base_nucli.sh despres
$ diff -u ~/dades/linies-base/nucli-abans-*.txt \
~/dades/linies-base/nucli-despres-*.txt | head -20Pas 5: documentar el fitxer final
$ sudo tee /etc/sysctl.d/70-rendiment.conf >/dev/null <<'EOF'
# Ajustos de rendiment de srv-tramontana.
# REGLA: cada parametre porta el seu motiu, la seva mesura i la seva data.
# Els parametres de SEGURETAT son a 60-enfortiment.conf, a part i a
# proposit: aquests es poden revertir, aquells no.
# --- Xarxa -------------------------------------------------------------
# BBR modela amplada de banda i latencia en lloc de reaccionar a la perdua.
# Mesurat amb iperf3 contra 198.51.100.20 el 2026-08-18:
# cubic: 77 Mbit/s -> bbr: 185 Mbit/s, i menor latencia sota carrega.
# Requereix l encuament fq: sense ell, BBR rendeix PITJOR que cubic.
# Revertir: net.ipv4.tcp_congestion_control = cubic
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# --- Memoria virtual ---------------------------------------------------
# NO es una millora de rendiment mitja: avui no hi ha activitat d intercanvi
# (pswpin=0, pswpout=0). Es una ASSEGURANCA: si algun dia hi ha pressio de
# memoria, el nucli preferira descartar memoria cau abans que enviar a
# l intercanvi les pagines actives del proces, evitant latencies de
# centenars de ms.
vm.swappiness = 10
# Llindars de pagines brutes mes baixos que el 10/20 per defecte.
# Motiu: amb 3,8 GB, el 20% son ~760 MB que poden abocar-se de cop i
# bloquejar el proces que escriu. Es el mecanisme de l incident del
# 2026-08-18 (copia nocturna saturant E/S, w_await 22,85 ms).
# Efecte: menys cabal maxim, latencia mes uniforme. Compromis acceptat.
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
# --- Fitxers -----------------------------------------------------------
# S esgota de debo i l error es enganyos ("No space left on device"
# sense que falti disc). Mesurat: 1284 watches en us amb 65536 de limit.
fs.inotify.max_user_watches = 524288
# NO TOQUEM, i consta per que:
# net.core.somaxconn: l app demana backlog=128, aixi que somaxconn no
# hi intervé (ss -ltn: Send-Q=128). ListenOverflows=0. Sense efecte.
# net.core.rmem_max/wmem_max: xarxa local amb 0,4 ms de RTT. El producte
# amplada de banda x latencia no justifica memories intermedies majors.
# net.ipv4.tcp_tw_recycle: NO EXISTEIX. Eliminat del nucli a la 4.12.
EOF
$ sudo sysctl --system >/dev/null
$ sysctl net.ipv4.tcp_congestion_control vm.swappiness
net.ipv4.tcp_congestion_control = bbr
vm.swappiness = 10La secció final —«NO TOQUEM, i consta per què»— és la part del fitxer que més valor té a llarg termini. Documenta les hipòtesis refutades, i això impedeix que d'aquí a sis mesos algú —tu inclòs— afegeixi somaxconn = 65535 perquè ho ha vist en una guia. Un ajust descartat amb el seu motiu escrit és coneixement; un ajust absent és només una omissió.
Errors Comuns i Consells
- Copiar llistes de
sysctld'Internet. La majoria són de fa deu anys, moltes contradiuen valors per defecte que ja són correctes, i algunes recomanen paràmetres que ja no existeixen (tcp_tw_recycleés el senyal inequívoc d'una guia sense provar). - Canviar diverses coses alhora. Si millora, no saps quina; si empitjora, tampoc. Un canvi, una mesura.
- Posar un paràmetre a
/etc/sysctl.d/sense provar-lo amb-w. El canvi efímer és reversible amb un reinici; el persistent et pot deixar un sistema que arrenca malament. - Creure que
swappinessredueix l'ús de memòria. Només actua quan hi ha pressió i el sistema recorre a l'intercanvi. Sense intercanvi en ús, no fa res. - Pujar
somaxconnsense mirar elbacklogde l'aplicació. El límit efectiu és el mínim dels dos.ss -ltnmostra el real aSend-Q. - Confiar en
limits.confper a un servei de systemd. No s'aplica: systemd no passa per PAM. Es fa servirLimitNOFILE=a la unitat, i es verifica a/proc/<pid>/limits. - Deixar THP en
alwaysamb una base de dades. Provoca pauses síncrones de compactació impredictibles.madviseés el compromís correcte, i cal aplicar-lo abans que la base de dades arrenqui. - Posar
mq-deadlineen un NVMe. Reordenar peticions no té sentit sense capçal: només hi afegeix latència.noneper a NVMe. - Oblidar
update-initramfsdesprés de tocar/etc/modprobe.d/. L'initramfs porta la seva pròpia còpia. És la lliçó de 07-01. - Compilar el nucli en producció. Queda fora del sistema de paquets: sense actualitzacions de seguretat automàtiques, sense signatura per a Secure Boot, i amb cada CVE a mà. Prova primer
linux-image-virtualo el nucli HWE. - Ajustar sense línia base. Sense un «abans» registrat, el «després» no significa res, i la sensació de millora és notòriament poc fiable.
- Consell de mètode. El comentari de cada paràmetre ha de respondre a quatre preguntes: què vas mesurar, amb quina ordre, quin resultat, i com revertir-ho. I documenta també el que vas decidir no tocar: és el que evita que el folklore torni a entrar.
Exercicis
Exercici 1
El Luis t'envia aquesta llista «per optimitzar el servidor», treta d'un blog:
net.core.somaxconn = 65535
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_max_syn_backlog = 65535
fs.file-max = 2097152
vm.swappiness = 0
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728Avalua cada línia: què faria, si té sentit a srv-tramontana, i amb quina ordre ho comprovaries. Redacta la resposta que li enviaries.
Exercici 2
Després de l'ajust de 07-02, l'aplicació manté un grup de 80 connexions a PostgreSQL. Comprova si els límits de descriptors de fitxer són adequats als tres nivells implicats (el servei de l'aplicació, el de PostgreSQL, i el global), i ajusta el que calgui amb la seva mesura.
Exercici 3
/srv/tramontana/backups és LUKS sobre LVM sobre sda, i vols saber si el planificador d'E/S influeix en la durada de la còpia nocturna. Dissenya l'experiment: què mesuraries, com aïllaries la variable, i per què el resultat podria no ser concloent.
Solucions
Solució 1
Re: llista d'optimització del nucli
Gràcies, Luis. L'he revisada línia per línia contra l'estat real del servidor. Resum: una té sentit, una és contraproduent, una no existeix, i quatre no farien res. Et passo el detall amb les ordres, perquè ho puguis comprovar tu mateix.
1.
net.core.somaxconn = 65535— Sense efecte
somaxconnés un sostre. La cua real és el mínim entre aquest valor i elbacklogque l'aplicació demana alisten().$ ss -ltn | grep 8080 LISTEN 0 128 127.0.0.1:8080 0.0.0.0:* $ nstat -az | grep ListenOverflows TcpExtListenOverflows 0 0.0
Send-Q = 128: l'aplicació en demana 128, així que el valor actual (4096) ja és irrellevant, i 65535 ho seria igual. IListenOverflows = 0significa que mai no s'ha descartat una connexió per cua plena. Perquè aquest paràmetre servís caldria canviar elbacklogal codi de l'aplicació, i no hi ha cap evidència que calgui.2.
net.ipv4.tcp_tw_recycle = 1— No existeix$ sysctl net.ipv4.tcp_tw_recycle sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recycle: No such file or directoryEs va eliminar del nucli a la versió 4.12 (2017) perquè trencava les connexions des de xarxes amb NAT de forma intermitent i molt difícil de diagnosticar. Que aparegui a la guia ens diu una cosa útil: l'autor no l'ha provada en aquesta dècada, així que convé desconfiar de la resta. Si l'objectiu era el reciclatge de
TIME_WAIT, el paràmetre vigent éstcp_tw_reuse, i ja és en2, que és el valor prudent per defecte a Ubuntu 24.04.3.
net.ipv4.tcp_max_syn_backlog = 65535— Sense efecte mesurable$ nstat -az | grep -iE 'TCPReqQFullDrop|ListenDrops' TcpExtListenDrops 0 0.0És la cua de connexions a mig establir, i la seva utilitat real és resistir una inundació de SYN. Zero descarts amb el valor actual de 1024. A més, contra aquell atac ja tenim
tcp_syncookiesactiu des de 06-03, que és la defensa correcta. Pujar-lo reservaria memòria del nucli sense benefici.4.
fs.file-max = 2097152— Sense efecte (i seria una baixada)$ sysctl fs.file-max fs.file-max = 9223372036854775807 $ sysctl fs.file-nr fs.file-nr 2848 0 9223372036854775807En nuclis moderns ja és pràcticament il·limitat: fixar 2.097.152 seria reduir-lo. I en fem servir 2.848. El límit que sí que s'assoleix a la pràctica és el de per procés, que en un servei de systemd es posa amb
LimitNOFILE=a la unitat —limits.confno s'aplica als serveis, que és un detall que confon molta gent.5.
vm.swappiness = 0— ContraproduentAquest és el que em preocupa.
swappiness = 0no significa «no facis servir intercanvi», significa «fes servir intercanvi només per evitar l'OOM killer». L'efecte real és que el nucli esgota la memòria cau per complet abans de considerar l'intercanvi, i en un pic de memòria pot invocar l'OOM killer abans d'hora — matant el procés de l'aplicació o de PostgreSQL.$ grep -E 'pswpin|pswpout' /proc/vmstat pswpin 0 pswpout 0 $ free -h | awk 'NR==2 {print "disponible:", $7}' disponible: 2,4GiAvui tant se val perquè no hi ha activitat d'intercanvi, però el dia que n'hi hagi,
0és pitjor que el valor per defecte. He posat10, que prefereix descartar memòria cau sense renunciar a l'intercanvi com a xarxa de seguretat, i ho he documentat com a assegurança contra la degradació, no com a millora de rendiment.6 i 7.
rmem_max/wmem_max= 128 MB — Sense efecte, i consumeix memòriaLes memòries intermèdies grans importen quan el producte amplada de banda × latència és gran.
$ ping -c3 10.0.2.15 | tail -1 rtt min/avg/max/mdev = 0.312/0.398/0.482/0.071 msAmb 0,4 ms d'anada i tornada en xarxa local, la finestra necessària per saturar 1 Gbit/s són uns 50 KB. El màxim actual (208 KB de
rmem_max, i fins a 6 MB atcp_rmem) sobra amb escreix. Reservar 128 MB per sòcol en una màquina amb 3,8 GB de RAM és un risc real si s'obren moltes connexions.
El que sí que he fet, i per què
Seguint el mateix criteri —mesurar primer—, l'únic canvi de xarxa que ha resultat justificat és el control de congestió BBR, que no era a la teva llista:
# cubic (per defecte) $ iperf3 -c 198.51.100.20 -t 20 -R | tail -2 [ 5] 0.00-20.00 sec 184 MBytes 77.2 Mbits/sec receiver # bbr + fq $ iperf3 -c 198.51.100.20 -t 20 -R | tail -2 [ 5] 0.00-20.00 sec 441 MBytes 185 Mbits/sec receiverDe 77 a 185 Mbit/s cap a clients remots. Aquell sí que és un ajust amb efecte mesurat, i és a
/etc/sysctl.d/70-rendiment.confamb la mesura, la data i la manera de revertir-lo.El que proposo per a la propera vegada. Abans d'aplicar un paràmetre, tres preguntes: quin comptador em diu que aquest recurs és el coll? l'he provat amb
sysctl -wi mesurat abans i després? està escrit el motiu? Si alguna resposta és no, el paràmetre no entra. He afegit al fitxer una secció de «NO TOQUEM, i consta per què» amb aquestes set línies i la seva raó, precisament perquè no ho tornem a discutir d'aquí a sis mesos.
Solució 2
Els tres nivells, mesurats de dins cap enfora.
# --- Nivell 1: el servei de l aplicacio ---
$ pid_app=$(systemctl show tramontana.service -p MainPID --value)
$ grep 'Max open files' /proc/$pid_app/limits
Max open files 8192 8192 files
$ ls /proc/$pid_app/fd | wc -l
214Ús real 214 sobre un límit de 8192: folgat i correcte. El desglossament confirma que el número té sentit:
$ ls -l /proc/$pid_app/fd | awk '{print $NF}' | sed 's/[0-9]*$//' \
| sort | uniq -c | sort -rn | head -5
80 socket:[
94 /opt/tramontana/releases/3.2.1/plantilles/
14 /var/log/tramontana/
3 pipe:[
2 /dev/null80 sòcols = el grup de connexions a PostgreSQL, exactament el valor de max_connexions=80 que es va ajustar a 07-02. Els descriptors són on han de ser.
# --- Nivell 2: PostgreSQL. Aqui esta el problema. ---
$ pid_pg=$(systemctl show [email protected] -p MainPID --value)
$ grep 'Max open files' /proc/$pid_pg/limits
Max open files 1024 524288 files
$ sudo -u postgres psql -tAc "SHOW max_connections;"
100
$ sudo -u postgres psql -tAc "SHOW max_files_per_process;"
1000El límit tou és 1024, i max_files_per_process és 1000. I aquí està el càlcul que revela el risc: cada procés de rerefons de PostgreSQL obre descriptors per als fitxers de taula i índex que toca. Amb 100 connexions possibles i fins a 1000 fitxers per procés, el límit de 1024 del procés principal és estret.
# La comprovacio directa: us real agregat
$ sudo ls /proc/$pid_pg/fd | wc -l
88
$ for p in $(pgrep -P $pid_pg); do sudo ls /proc/$p/fd 2>/dev/null | wc -l; done \
| awk '{s+=$1} END {print "descriptors als processos de rerefons:", s}'
descriptors als processos de rerefons: 412Avui no s'està assolint, però el marge és escàs i la fallada, quan arriba, es manifesta com a errors de connexió intermitents sota càrrega — difícils de diagnosticar.
# Corregir per drop-in, mai editant la unitat del paquet
$ sudo mkdir -p /etc/systemd/system/[email protected]
$ sudo tee /etc/systemd/system/[email protected]/limits.conf >/dev/null <<'EOF'
[Service]
# max_connections=100 x max_files_per_process=1000 en el pitjor cas.
# 65536 dona marge ampli sense ser absurd. Mesurat el 2026-08-18:
# proces principal 88 fd, processos de rerefons 412 fd agregats.
LimitNOFILE=65536
EOF
$ sudo systemctl daemon-reload && sudo systemctl restart [email protected]
$ pid_pg=$(systemctl show [email protected] -p MainPID --value)
$ grep 'Max open files' /proc/$pid_pg/limits
Max open files 65536 65536 files# --- Nivell 3: el limit global ---
$ sysctl fs.file-nr fs.file-max
fs.file-nr 2848 0 9223372036854775807
fs.file-max = 92233720368547758072.848 descriptors a tot el sistema contra un límit pràcticament infinit: no hi ha res a ajustar, i això confirma el que se li va respondre al Luis a l'exercici anterior sobre fs.file-max.
# I el valor per defecte per a serveis que no declarin limit propi
$ grep -i defaultlimitnofile /etc/systemd/system.conf
#DefaultLimitNOFILE=1024:524288Aquí hi ha una decisió de criteri: es podria pujar el valor per defecte global, però és preferible no fer-ho. Un límit per defecte alt amaga les fuites de descriptors: un servei que no tanca el que obre falla aviat i de forma visible amb un límit raonable, i consumeix memòria del nucli silenciosament durant mesos amb un d'alt. Millor límits explícits i dimensionats per servei.
Verificació final:
$ ~/scripts/revisio_salut.sh; echo "estat: $?"
estat: 0
$ sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;"
82
$ sudo timeout 10 strace -f -c -p $pid_app 2>&1 | grep -cE 'EMFILE|ENFILE'
0Zero errors EMFILE/ENFILE, que són els que apareixerien si s'esgotessin els descriptors. I la lliçó de mètode: el problema era a PostgreSQL, no a l'aplicació, encara que el símptoma anterior es manifestés a l'aplicació. Igual que a 07-02, la causa era a l'altre costat de la relació client-servidor. Convé afegir al manual d'operació la regla: en ajustar un límit d'un costat, comprovar el de l'altre.
Solució 3
Què mesuraria, i per què aquelles mètriques. La durada total de la còpia és la variable d'interès, però és massa gruixuda per atribuir-la al planificador. Cal mesurar a tres nivells:
# 1. Durada total (el que li importa a la Marta)
$ systemd-analyze --no-pager verify tramontana-copia.service
$ sudo systemctl show tramontana-copia.service \
-p ExecMainStartTimestamp -p ExecMainExitTimestamp
# 2. Latencia per DISPOSITIU. -D separa les capes de la pila, que es
# la rao de fer servir biolatency en lloc de la mitjana d iostat.
$ sudo biolatency-bpfcc -D 300 1 > /tmp/lat-planificador.txt
# 3. On es bloqueja el proces: xifratge (CPU) o E/S (espera)
$ sudo timeout 60 offcputime-bpfcc -p $(pgrep -f 'restic backup') -f \
| sort -k2 -rn | head -5
$ sudo perf stat -p $(pgrep -f 'restic backup') -- sleep 30 2>&1 \
| grep -E 'CPUs utilized'Com aïllaria la variable. Aquest és el nucli de l'exercici, i hi ha quatre fonts de contaminació que cal neutralitzar:
# a) Dades identiques a totes dues execucions. Una copia incremental de
# restic depen del que hagi canviat: dues nits diferents NO son
# comparables. Solucio: partir de la mateixa instantania a les dues proves.
$ sudo restic -r "$REPO" snapshots --latest 1
$ sudo lvcreate -L 4G -s -n prova-io /dev/vg-dades/lv-backups
# b) Estat de la memoria cau de pagina identic. Una segona execucio es mes
# rapida nomes perque les dades ja son a la memoria cau.
$ sync && echo 3 | sudo tee /proc/sys/vm/drop_caches
# c) Sense carrega concurrent. La copia real corre a les 02:30 amb ionice;
# per a l experiment cal treure tant la carrega com l ionice,
# perque ionice interactua amb el planificador i confondria el resultat.
$ sudo systemctl stop tramontana.service # nomes a la VM de proves
$ sudo ss -tn state established | wc -l
# d) Diverses repeticions, no una. La variabilitat d E/S en una VM es alta.
$ for planificador in none mq-deadline bfq; do
for repeticio in 1 2 3; do
echo "$planificador" | sudo tee /sys/block/sda/queue/scheduler >/dev/null
sync && echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null
inici=$(date +%s)
sudo restic -r /srv/tramontana/backups/restic backup \
--quiet /opt/tramontana/releases/3.2.1
printf '%s\t%s\t%s\n' "$planificador" "$repeticio" "$(( $(date +%s) - inici ))"
done
done | tee /tmp/experiment-planificador.tsvI una decisió de disseny important: l'experiment es fa a srv-tramontana-proves, no en producció. És la VM que crearàs a la lliçó següent, i aquest és exactament el cas d'ús que la justifica: canviar el planificador d'E/S en calent al servidor real, amb drop_caches i sense ionice, és demanar una degradació del servei.
Per què el resultat podria no ser concloent. Cinc motius, i són els que fan que aquest sigui un bon exercici:
-
El planificador que importa és el del dispositiu físic, no el de la pila.
/srv/tramontana/backupsésdm-1(LUKS) sobrelv-backups(LVM) sobresda. Els dispositiusdm-*no tenen planificador propi: passen les peticions cap avall. Es comprova:$ cat /sys/block/dm-1/queue/scheduler noneUn
nonefix, sense alternatives entre claudàtors, indica que aquell dispositiu no planifica. Així que l'experiment només té sentit sobresda. -
Som en una màquina virtual. El planificador de l'hoste opera sobre un disc que en realitat és un fitxer a l'amfitrió, que té el seu propi planificador i la seva pròpia memòria cau. Les decisions de l'hoste poden quedar completament anul·lades per l'amfitrió. Això és el que probablement farà que el resultat no sigui concloent, i és la raó per la qual
virtual-guestés el perfil detunedactiu. -
El coll podria ser el xifratge, no l'E/S. Si
perf statdonaCPUs utilizeda prop d'1,00 ioffcputimeassenyala funcions d'AES, el procés està limitat per CPU i el planificador d'E/S és irrellevant per definició. Aquest control cal fer-lo abans de l'experiment: si el coll és el xifratge, l'experiment no escau. -
La còpia és majoritàriament escriptura seqüencial, i allà les diferències entre planificadors són petites. On se separen de debò és en la barreja de lectura aleatòria i escriptura amb diversos processos competint.
-
resticdeduplica i comprimeix, així que la relació entre dades llegides i blocs escrits no és fixa. Encara que les dades d'origen siguin idèntiques, la càrrega d'E/S pot variar entre execucions.
Conclusió del disseny. L'experiment s'ha de fer, però amb l'expectativa correcta: la hipòtesi més probable és que el planificador no influeixi de forma mesurable en aquesta màquina, i el pas 3 —mesurar si el coll és CPU o E/S— probablement ho demostrarà abans de començar. Això no és un fracàs: és refutar una hipòtesi per 30 minuts de feina, i evita afegir una regla d'udev que no fa res.
I si el pas 3 mostra que el coll és el xifratge, la línia d'investigació correcta és una altra: comprovar si la CPU exposa AES-NI a l'hoste (grep -m1 aes /proc/cpuinfo) i, si no, activar host-passthrough a la definició de la VM. Això pot multiplicar per cinc o deu la velocitat del xifratge, i és un canvi de virtualització, no de nucli. Just el terreny de la lliçó següent.
Conclusió
Saps ajustar el nucli, i —més important— saps quan no ajustar-lo. Tens el mapa dels paràmetres que de debò importen: la memòria virtual amb swappiness i els llindars de pàgines brutes, la pila de xarxa amb les seves dues cues de connexió i el control de congestió, els límits de fitxers amb el parany de limits.conf que no s'aplica als serveis de systemd, el planificador d'E/S amb la regla de none per a NVMe i el seu perquè, i les pàgines enormes transparents que tota base de dades demana en madvise. Has vist els mòduls, modprobe.d amb install ... /bin/true —tancant de passada una fila pendent de la llista de comprovació de 06-06—, DKMS, i una resposta honesta a compilar el nucli: gairebé mai en producció, perquè treu la màquina del sistema de paquets i amb ell de les actualitzacions de seguretat.
Però el que t'endus d'aquesta lliçó no és una llista de paràmetres. És el procediment: mesurar, formular una hipòtesi, canviar una cosa amb sysctl -w, mesurar un altre cop, i documentar amb la mesura i la data o revertir. Aplicant-lo, de tres hipòtesis raonables sobre la xarxa de srv-tramontana dues van quedar refutades —somaxconn no hi intervé perquè l'aplicació demana backlog=128, i swappiness no fa res sense activitat d'intercanvi— i només una, BBR, va resultar tenir efecte mesurable: de 77 a 185 Mbit/s. Aquella proporció és el normal, i la secció «NO TOQUEM, i consta per què» del teu 70-rendiment.conf és el que impedirà que el folklore torni a entrar d'aquí a sis mesos.
Fixa't on t'ha deixat l'últim exercici. Per provar el planificador d'E/S sense degradar el servei calia una màquina on buidar la memòria cau, treure l'ionice i aturar l'aplicació sense que ningú se n'assabentés. Per mesurar si el xifratge de LUKS va per programari calia canviar la configuració de CPU de la VM. I el desplegament 3.3.0 que no va arrencar i va provocar una reversió al Mòdul 4 no es va arribar a diagnosticar mai, perquè no hi havia on reproduir-lo. Tot apunta al mateix: falta un entorn de proves, i el curs fa quatre mòduls que el troba a faltar.
A la lliçó 07-04: Virtualització amb Linux el construeixes. Entendràs què és virtualitzar de debò —els tres models, el paper exacte de KVM com a mòdul que converteix Linux en hipervisor, i la seva relació amb QEMU i libvirt—, manejaràs màquines amb virsh i virt-install, aprovisionaràs sense intervenció amb cloud-init, triaràs entre raw i qcow2 sabent per què, faràs instantànies recordant que una instantània no és una còpia de seguretat, i connectaràs la xarxa per NAT o per pont segons convingui —inclosa l'explicació d'aquell virbr0 sense enllaç que costava 35 segons d'arrencada a 07-01—. Al final tindràs srv-tramontana-proves, clonat del servidor real: el lloc on provar els canvis de nucli d'aquesta lliçó, reproduir el desplegament que va fallar, i assajar la recuperació de l'arrencada de 07-01 sense arriscar res. I de passada veuràs la pila de virtualització des de dins, que és el que fa que la lliçó següent —contenidors— s'entengui per contrast i no per analogia.
Curs de Linux: De Principiant a Administrador de Sistemes
Mòdul 1: Introducció a Linux
- Què és Linux?
- Història de Linux
- Distribucions de Linux
- Instal·lant Linux
- Primer Contacte amb el Sistema
- Estructura del Sistema de Fitxers de Linux
Mòdul 2: Comandes Bàsiques de Linux
- Introducció a la Línia de Comandes
- Obtenir Ajuda i Documentació del Sistema
- Navegant pel Sistema de Fitxers
- Operacions amb Fitxers i Directoris
- Visualització i Edició de Fitxers
- Enllaços Durs i Simbòlics
- Permisos i Propietat dels Fitxers
Mòdul 3: Habilitats Avançades en la Línia de Comandes
- L'Entorn del Shell: Variables, Àlies i Historial
- Ús de Comodins i Expressions Regulars
- Cerca de Fitxers i Contingut: find, locate i grep
- Canonades i Redirecció
- Processament de Text: cut, sort, uniq, sed i awk
- Gestió de Processos
- Programació de Tasques amb Cron
- Comandes de Xarxa
Mòdul 4: Scripting en Shell
- Introducció al Scripting en Shell
- Variables i Tipus de Dades
- Entrada, Sortida i Arguments d'un Script
- Estructures de Control
- Funcions i Biblioteques
- Depuració i Gestió d'Errors
- Scripts de Producció: Bones Pràctiques
Mòdul 5: Administració del Sistema
- Gestió d'Usuaris i Grups
- sudo i Permisos Especials
- Gestió de Paquets
- Gestió de Discs
- systemd i la Gestió de Serveis
- Registres del Sistema: journald i syslog
- Monitoratge del Sistema i Optimització del Rendiment
- Còpies de Seguretat i Restauració
Mòdul 6: Xarxes i Seguretat
- Configuració de Xarxes
- SSH i Accés Remot
- Tallafocs i Seguretat Perimetral
- Sistemes de Detecció d'Intrusions
- Gestió de Secrets i Certificats TLS
- Assegurant Sistemes Linux
Mòdul 7: Temes Avançats
- El Procés d'Arrencada i la Recuperació del Sistema
- Diagnòstic Avançat: strace, perf i eBPF
- Optimització del Nucli de Linux
- Virtualització amb Linux
- Contenidors de Linux i Docker
- Automatització amb Ansible
- Alta Disponibilitat i Balanceig de Càrrega
