Optimizarea parametrilor kernel Linux pentru performanță mai bună

Optimizarea parametrilor kernel Linux pentru performanță mai bună

Majoritatea serverelor Linux rulează cu parametrii kernel impliciți. Este în regulă pentru sarcini generale, dar odată ce începi să încarci mai serios un server, acele valori implicite lasă performanță reală pe masă. sysctl este utilitarul care permite citirea și scrierea parametrilor kernel în timp ce sistemul rulează, iar /proc/sys este locul unde acești parametri există efectiv.

Acest ghid acoperă cei mai utili parametri de ajustat pentru throughput de rețea, gestionarea memoriei, I/O pe fișiere și performanță generală a serverului - cum să aplici modificările în siguranță, să le faci permanente și să verifici că fac ce te aștepți.

Ce este sysctl?

sysctl este un utilitar pentru examinarea și modificarea parametrilor kernel în timp ce sistemul rulează, fără repornire. Parametrii sunt expuși ca fișiere sub /proc/sys/, organizați în subdirectoare pe categorii.

Listarea tuturor parametrilor curenți:

sysctl -a

Citirea unui parametru specific:

sysctl net.ipv4.tcp_syncookies

Setarea unui parametru temporar (nu supraviețuiește unui reboot):

sudo sysctl -w net.ipv4.tcp_syncookies=1

Pentru a face modificările permanente, adaugă-le în /etc/sysctl.conf sau într-un fișier din /etc/sysctl.d/. Abordarea preferată pe sistemele moderne este un fișier separat:

sudo nano /etc/sysctl.d/99-tuning.conf

Aplicarea modificărilor fără reboot:

sudo sysctl --system

Sau reîncărcarea unui fișier specific:

sudo sysctl -p /etc/sysctl.d/99-tuning.conf

Parametrii pot fi citiți și direct din /proc/sys. Slash-urile din cale înlocuiesc punctele din numele sysctl. Astfel, net.ipv4.tcp_syncookies se află la:

cat /proc/sys/net/ipv4/tcp_syncookies

Ambele abordări citesc aceleași valori kernel. sysctl este pur și simplu mai convenabil.

Tuning performanță rețea

Acesta este domeniul unde ajustarea atentă poate da rezultate, dar și unde oamenii copiază valori de care nu au nevoie. Kernel-urile moderne ajustează automat buffer-ele de recepție TCP în intervalul setat, deci creșterea plafonurilor ajută doar când conexiunea este efectiv limitată de bandă pe o cale cu latență mare. Dacă muți cantități mari de date pe distanțe lungi, aceste setări contează. Pentru un server web tipic pe o rețea cu latență redusă, valorile implicite sunt de obicei bune.

Dimensiuni buffer TCP

Kernel-ul folosește buffer-e de recepție și trimitere pentru conexiunile TCP. Valorile implicite sunt conservatoare (aproximativ 208 KB). Pentru transferuri cu bandă largă și latență mare, maxime mai mari permit autotuning-ului să extindă fereastra mai mult. Acestea sunt plafoane, nu ținte - kernel-ul nu va aloca 64 MB per socket decât dacă o conexiune chiar are nevoie:

# Buffer recepție socket TCP (min, implicit, max) în octeți
net.ipv4.tcp_rmem = 4096 87380 67108864

# Buffer trimitere socket TCP (min, implicit, max) în octeți
net.ipv4.tcp_wmem = 4096 65536 67108864

# Maxime buffer recepție/trimitere socket core
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.core.rmem_default = 262144
net.core.wmem_default = 262144

Controlul congestiei TCP

BBR (Bottleneck Bandwidth and Round-trip propagation time) este algoritmul de control al congestiei TCP de la Google. Poate depăși implicit cubic pe legături cu latență mare sau pierderi, deși nu este universal mai rapid. Benchmarkează-l față de algoritmul existent înainte să-l adopți.

Verifică ce este disponibil pe sistem:

sysctl net.ipv4.tcp_available_congestion_control

BBR poate veni ca modul și să nu apară în acea listă până nu îl încarci. Dacă nu vezi bbr:

sudo modprobe tcp_bbr

Pentru încărcare automată la boot:

echo tcp_bbr | sudo tee /etc/modules-load.d/tcp-bbr.conf

Odată disponibil BBR, activează-l (necesită kernel 4.9+):

net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

fq (fair queuing) este scheduler-ul de pachete recomandat când folosești BBR, deoarece BBR se bazează pe pacing.

SYN Backlog și gestionarea conexiunilor

Sub trafic intens, cozile de conexiuni implicite se umplu rapid, cauzând refuzuri de conexiuni înainte ca aplicația să vadă cererea:

# Numărul maxim de conexiuni half-open în coada SYN
net.ipv4.tcp_max_syn_backlog = 8192

# Numărul maxim de cereri de conexiune în coadă înainte ca aplicația să le accepte
net.core.somaxconn = 65535

# Numărul maxim de pachete în coadă pe partea INPUT
net.core.netdev_max_backlog = 16384

Activează SYN cookies pentru protecție împotriva atacurilor SYN flood:

net.ipv4.tcp_syncookies = 1

TIME_WAIT și reciclarea conexiunilor

Serverele care gestionează multe conexiuni de scurtă durată (API-uri HTTP, de exemplu) pot acumula mii de socket-uri în starea TIME_WAIT. Verifică contorul curent:

ss -s | grep TIME-WAIT

tcp_tw_reuse permite kernel-ului să reutilizeze un socket TIME_WAIT pentru o conexiune nouă. Important: se aplică doar conexiunilor ieșitoare, deci ajută un server care face multe conexiuni în exterior (un reverse proxy care vorbește cu backend-uri, de exemplu) și nu face nimic pentru conexiunile client entrante. Valoarea implicită este 2 (loopback only):

# Avansat: reutilizează socket-uri TIME_WAIT doar pentru conexiuni ieșitoare
# Implicit este 2 (loopback). Setează deliberat, nu din obișnuință.
net.ipv4.tcp_tw_reuse = 1

tcp_max_tw_buckets limitează numărul de socket-uri TIME_WAIT. Această limită există ca protecție DoS de bază, deci reducerea ei este instinctul greșit:

net.ipv4.tcp_max_tw_buckets = 262144

Tuning Keepalive

Keepalive este detectarea peer-urilor moarte, nu o optimizare de performanță, și se activează doar pentru aplicațiile care activează SO_KEEPALIVE pe socket-urile lor. Valorile implicite ale kernel-ului sunt 2 ore idle, 75 secunde între sonde și 9 sonde. Scurtarea lor detectează mai rapid conexiunile moarte, eliberând descriptorii de fișiere și memoria:

# Începe trimiterea keepalive după 60 secunde de idle (implicit: 7200)
net.ipv4.tcp_keepalive_time = 60

# Intervalul dintre sondele keepalive
net.ipv4.tcp_keepalive_intvl = 10

# Numărul de sonde înainte de a declara conexiunea moartă
net.ipv4.tcp_keepalive_probes = 6

Gestionarea memoriei

Swappiness

Controlează cât de agresiv mută kernel-ul paginile de memorie anonimă în swap. Valoarea implicită este 60, intervalul este 0 la 200. Verifică valoarea curentă:

cat /proc/sys/vm/swappiness

Pentru un server sensibil la latență unde preferi să păstrezi paginile în RAM:

vm.swappiness = 10

Valoarea 1 este cea mai mică setare utilă fără a dezactiva complet swap-ul. Testează cu sarcina ta reală înainte să stabilești un număr.

VFS Cache Pressure

Controlează cât de agresiv recuperează kernel-ul memoria folosită pentru cache-urile inode și dentry (cache-ul de metadate director/fișier). Valorile mai mici păstrează mai mult din acest cache în memorie, ceea ce accelerează operațiunile de sistem de fișiere:

vm.vfs_cache_pressure = 50

Valoarea implicită este 100. Pe un server cu multă RAM și o sarcină de lucru care accesează frecvent multe fișiere, reducerea la 50 menține mai multe metadate în cache. Nu merge prea jos pe sisteme cu memorie limitată și nu seta niciodată la 0.

Dirty Page Writeback

Controlează cât de agresiv scrie kernel-ul paginile murdare (date modificate în așteptare pentru a fi scrise pe disc) pe stocare. Relevant pe servere cu sarcini de scriere intense:

# Procentul din RAM murdar înainte ca kernel-ul să blocheze scrierile pentru flush (limita hard)
vm.dirty_ratio = 10

# Procentul din RAM care poate fi murdar înainte să înceapă writeback în fundal
vm.dirty_background_ratio = 5

# Cât timp rămân datele murdare înainte să fie scrise (în centisecunde, implicit 3000 = 30s)
vm.dirty_expire_centisecs = 1500

# Cât de des se trezește daemon-ul writeback (centisecunde, implicit 500 = 5s)
vm.dirty_writeback_centisecs = 250
Atenție: Timing-ul writeback nu este o garanție de durabilitate și nu protejează împotriva pierderii bruște de curent. Durabilitatea vine din fsync() / fdatasync() în aplicație, journaling, cache-uri de scriere cu baterie și UPS.

Pe mașini cu memorie mare există o capcană de scalare: pe un server cu 256 GB RAM, dirty_ratio = 10 înseamnă ~25 GB, iar flush-ul dintr-o dată poate bloca sistemul cu un spike masiv de I/O. Pe sisteme cu memorie mare, setează limite absolute:

# Limite absolute în octeți în loc de procente (mai bine pentru sisteme cu RAM mare)
vm.dirty_bytes = 536870912          # 512 MB limita hard
vm.dirty_background_bytes = 268435456   # 256 MB pragul pentru background

Setarea formei _bytes la o valoare nenulă dezactivează forma corespunzătoare _ratio. Folosește una sau cealaltă pereche, nu ambele.

Comportament Overcommit

# 0 = heuristic (implicit)
# 1 = permite întotdeauna overcommit (util pentru unele sarcini HPC/științifice)
# 2 = niciodată overcommit peste RAM fizic + swap
vm.overcommit_memory = 0

Documentația Redis recomandă setarea la 1 pentru a evita eșecurile la salvarea în fundal. Lasă la 0 dacă nu ai un motiv specific.

Limite pentru descriptori de fișiere și procese

Serverele cu trafic intens pot atinge limita descriptorilor de fișiere mult înainte să atingă limitele CPU sau memorie. Nginx, Node.js și bazele de date deschid mulți descriptori de fișiere per conexiune.

Numărul maxim de fișiere deschise

# Limita de descriptori de fișiere la nivel de sistem
fs.file-max = 2097152

Înainte de a modifica, verifică dacă ești cu adevărat aproape de plafon:

cat /proc/sys/fs/file-nr

Cele trei valori sunt: handle-uri alocate, handle-uri alocate nefolosite și maximul. Dacă prima valoare este departe de a treia, ridicarea fs.file-max nu îți folosește la nimic.

Notă: Limita kernel setată aici este plafonul. Trebuie să setezi și LimitNOFILE în unitățile de serviciu systemd, sau să folosești ulimit -n pentru procesele non-systemd. Limita kernel singură nu este suficientă.

Limite Inotify

Dacă rulezi aplicații care monitorizează fișiere (unelte de build, IDE-uri pe un server remote, sau agenți de monitorizare), poți atinge limitele inotify:

fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512

Simptomul când atingi aceste limite este de obicei o eroare de tipul „too many open files“ sau „inotify watch limit reached“ în logurile aplicației.

Parametri de securitate

Unii parametri sysctl privesc atât securitatea cât și performanța. Merită incluși în configurația de bază standard:

# Ignorarea cererilor ICMP broadcast (protecție atac Smurf)
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Ignorarea răspunsurilor ICMP de eroare false
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Filtrare reverse path (ajută la prevenirea IP spoofing)
# 1 = strict, 2 = loose. Folosește 2 pentru rutare asimetrică/multihomed.
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Dezactivarea IP source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# Dezactivarea acceptării redirect-urilor ICMP
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Nu trimite redirect-uri ICMP (nu este router)
net.ipv4.conf.all.send_redirects = 0

# Logarea pachetelor martian (pachete cu adrese sursă imposibile)
net.ipv4.conf.all.log_martians = 1
Atenție: Filtrarea reverse path strictă (1) poate strica rutarea asimetrică, policy routing, VPN-urile și unele configurații multihomed. Dacă traficul poate ajunge legitim pe o interfață diferită față de cea pe care pleacă, folosește modul loose (2).

Dacă serverul nu acționează ca router:

net.ipv4.ip_forward = 0

Schimbarea ip_forward resetează mai mulți parametri de configurare IPv4, deci setează deliberat. Dacă mașina este router sau rulează Docker, vei vrea valoarea 1.

O configurație de bază completă

Aceasta este un punct de pornire, nu o configurație de copiat orb. Dimensiunile buffer-elor de rețea, swappiness, cache pressure și setările dirty-page depind de sarcina de lucru. Salvează în /etc/sysctl.d/99-tuning.conf:

# /etc/sysctl.d/99-tuning.conf
# Punct de pornire sysctl - citește secțiunile de mai sus înainte să aplici

## Rețea: dimensiuni buffer TCP
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864
net.core.rmem_max = 67108864
net.core.wmem_max = 67108864
net.core.rmem_default = 262144
net.core.wmem_default = 262144

## Rețea: control congestie (necesită kernel 4.9+ pentru BBR)
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq

## Rețea: gestionare conexiuni
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_syncookies = 1

## Rețea: TIME_WAIT
# tcp_tw_reuse omis - este doar pentru ieșiri, implicit 2. Setează per sarcină de lucru.
net.ipv4.tcp_max_tw_buckets = 262144

## Rețea: Keepalive
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 10
net.ipv4.tcp_keepalive_probes = 6

## Memorie
vm.swappiness = 10
vm.vfs_cache_pressure = 50
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
vm.dirty_expire_centisecs = 1500
vm.dirty_writeback_centisecs = 250

## Descriptori de fișiere (ridică doar dacă file-nr arată că ești aproape de plafon)
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288

## Securitate
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# rp_filter: folosește 2 (loose) dacă ai rutare asimetrică/multihomed
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.log_martians = 1

Aplică imediat:

sudo sysctl -p /etc/sysctl.d/99-tuning.conf

Cum verifici că modificările funcționează

Nu presupune niciodată că o modificare a făcut ce te așteptai. Verifică fiecare parametru după aplicare:

sysctl net.ipv4.tcp_congestion_control

Confirmă că BBR este activ pe conexiunile stabilite:

ss -tin | grep bbr | head -5

Verifică situația descriptorilor de fișiere sub sarcină:

cat /proc/sys/fs/file-nr

Monitorizează activitatea paginilor murdare:

cat /proc/meminfo | grep -i dirty

Dacă ceva se comportă greșit după aplicarea modificărilor, dmesg este un check util de depanare:

sudo dmesg | tail -20

Potrivește utilitarul cu ce testezi: iperf3 pentru throughput brut de rețea, ss -s și ss -tin pentru starea TCP, vmstat / iostat / sar pentru comportamentul sistem în timp. Pentru orice contează cu adevărat, folosește un benchmark specific aplicației - wrk sau ab (Apache Bench) pentru un server web, uneltele proprii ale bazei de date pentru o bază de date.

Considerații pentru medii speciale

În containere, anumiți parametri (mai ales cei de rețea) pot fi setați la nivel de host și să nu poată fi scriși din interior. Parametrii kernel ai hostului se aplică tuturor containerelor care rulează pe el. În mașini virtuale, ai control complet atâta timp cât hypervisor-ul nu restricționează accesul.

Notă: Unele imagini pre-configurate (cloud sau altele) pot avea deja ajustați anumiți parametri. Rulează sysctl -a pe un sistem proaspăt și compară cu valorile implicite înainte să aplici un fișier de tuning, ca să nu duplici sau să suprascrii setări deja existente.

Ce să nu atingi

Câțiva parametri par tentanți, dar cauzează mai multe probleme decât rezolvă:

  • net.ipv4.tcp_tw_recycle a fost eliminat complet în kernel 4.12. Pe kernel-uri mai vechi cauza conexiuni abandonate pentru clienții din spatele NAT. Nu îl folosi.
  • vm.overcommit_memory = 1 poate masca scurgeri de memorie și face kill-urile OOM mai greu de prezis. Setează-l doar dacă știi exact de ce ai nevoie.
  • Huge pages (vm.nr_hugepages) pot ajuta semnificativ serverele de baze de date ca MySQL și PostgreSQL, dar configurarea greșită poate împiedica pornirea serviciilor. Este un subiect separat care merită propriul ghid.

Concluzie

Configurația implicită a kernel-ului Linux este conservatoare prin design - trebuie să funcționeze rezonabil pe orice, de la un Raspberry Pi la un server cu 256 de nuclee. Pentru orice sarcină specifică, poți face mai bine.

Începe cu setările de buffer de rețea și control al congestiei dacă rulezi orice cu față la internet. Adaugă ajustările de swappiness și cache pressure pentru orice server care rulează pe termen lung. Adaugă setările de hardening de securitate ca bază peste tot.

Fă modificările o secțiune pe rând. Măsoară înainte și după. Păstrează o copie a valorilor implicite originale pentru a putea reveni. Și pune fișierul final de tuning în control de versiuni sau sistemul tău de configuration management, astfel încât să urmeze automat fiecare server nou provizionat.

Referințe