Od czego zacząć: diagnoza zamiast losowych tweaków
Profil systemu: najpierw „co”, dopiero potem „jak”
Linux w firmie może pełnić zupełnie różne role: od prostego serwera plików, przez maszynę wirtualizującą, po krytyczny serwer bazodanowy z wymagającym SLA. Domyślne ustawienia dystrybucji są kompromisem, który ma działać „w miarę dobrze wszędzie”. Optymalizacja ma sens dopiero wtedy, gdy jest robiona pod konkretny profil systemu.
W praktyce warto na starcie jasno nazwać rolę każdej maszyny. Dla uproszczenia dobrze zdefiniować 3–5 klas: np. serwer aplikacyjny, serwer baz danych, serwer plików/backup, router/firewall, stacja robocza admina. Dla każdej klasy będą inne priorytety: dla DB kluczowy będzie I/O i opóźnienia, dla reverse proxy – liczba połączeń i wydajność sieci, dla firewalla – niezawodność i bezpieczeństwo kosztem „ostatnich procentów” wydajności.
Przykład z praktyki: zbyt agresywne „tuningowanie” sysctl pod kątem sieci (duże backlogi, krótkie timeouty TCP) na serwerze backupu przyniosło głównie głośniejsze wentylatory i brak realnego zysku, bo wąskim gardłem był wolny NAS po NFS, a nie stos TCP w jądrze.
Narzędzia do diagnozy: szybki podgląd bez rozbudowanych systemów APM
Nawet bez drogich pakietów monitoringu można bardzo szybko określić, gdzie jest największy problem. Kilka podstawowych narzędzi w Linuxie wystarczy, by zdecydować, czy dotyka nas problem CPU, pamięci, I/O czy sieci:
- top / htop – ogólny podgląd zużycia CPU, pamięci, procesów. Przydatne szczególnie:
- kolumna %CPU – czy jeden proces nie zjada całego rdzenia,
- load average – czy liczba procesów oczekujących na CPU nie jest kilka razy wyższa niż liczba rdzeni,
- kolumny wa (iowait) – wskazuje na czekanie na I/O.
- vmstat – pokazuje równowagę między CPU, pamięcią i I/O. Kluczowe kolumny:
- r – procesy oczekujące na CPU,
- si/so – swap in/out, częste wartości > 0 oznaczają presję pamięci,
- wa – procent czasu, gdy CPU czeka na I/O.
- iostat – diagnostyka dysków; wysokie %util na jednym urządzeniu zwykle oznacza wąskie gardło I/O.
- ss (następca netstat) – analiza połączeń sieciowych, stanów TCP, liczby połączeń do konkretnych portów.
- journalctl – podgląd logów systemd; przy hardeningu i zmianie limitów warto sprawdzić, czy usługi nie restartują się w pętli.
Wystarczy 10–15 minut obserwacji pod typowym obciążeniem, aby z grubsza określić typ problemu i ustalić priorytety. Dopiero po takiej diagnozie sensownie jest zmieniać ustawienia jądra czy konfigurację usług.
CPU-bound vs I/O-bound: jak to rozpoznać w praktyce
Klasyczne rozróżnienie systemów obciążonych bardziej CPU niż I/O brzmi podręcznikowo, ale przekłada się na konkretne decyzje konfiguracyjne. Na serwerze CPU-bound drobne optymalizacje procesora, planowania procesów i bibliotek kryptograficznych mogą dać duży efekt. Na serwerze I/O-bound wszelkie triki z sysctl dla sieci czy dysków mają większy sens niż rozważania o „super-szybkich” algorytmach szyfrowania.
System CPU-bound najczęściej zdradza się przez:
- stały wysoki load average zdominowany przez procesy w stanie R (running),
- niski iowait, dyski w iostat mają sporo wolnych zasobów,
- aplikacje typu serwer aplikacyjny, intensywne przetwarzanie danych w RAM (np. mikroserwisy, API),
- na VM: host zgłasza pełne wykorzystanie przydzielonych vCPU.
System I/O-bound rozpoznasz po:
- wysokim iowait (kolumna wa w top/vmstat),
- wysokim %util na jednym lub kilku dyskach,
- kolejkach I/O: w iostat avgqu-sz rośnie znacząco,
- serwerze DB/plikowym, który „zamiera”, gdy kilka osób uruchomi ciężkie raporty.
Kontrariańska uwaga: częsty błąd polega na „tuningowaniu” sysctl od sieci na serwerze, który tak naprawdę dusi się na pojedynczym, wolnym dysku SATA. W takiej sytuacji zmiany parametru somaxconn czy TCP backlog będą czystą kosmetyką.
Kiedy lepiej nie ruszać domyślnych ustawień
Nie każde środowisko firmowe potrzebuje agresywnego hardeningu i tuningu. Zdarzają się przypadki, gdy dotykanie ustawień systemowych przyniesie więcej szkody niż pożytku:
- małe, jednoużytkownikowe VM-ki do zadań pomocniczych, np. serwer Jenkins dla zespołu kilku deweloperów – często wąskim gardłem jest i tak chmura (I/O sieciowe, storage), a nie samo jądro;
- systemy z pełnym wsparciem vendorów, gdzie producent aplikacji wymaga „gołej” dystrybucji i ostrzega przed zmianami w sysctl czy parametrach mount – tu lepiej najpierw wynegocjować oficjalne rekomendacje, niż eksperymentować na produkcji;
- środowiska bez testów – jeśli firma nie ma ani stagingu, ani możliwości odtworzenia obciążenia, każda głębsza zmiana sysctl czy schedulerów I/O jest ryzykiem biznesowym.
Dobrą zasadą jest wprowadzenie prostej polityki: zmieniamy ustawienia systemowe tylko wtedy, gdy umiemy zmierzyć efekt i mamy plan szybkiego cofnięcia zmian. Nawet jeśli oznacza to zrezygnowanie z części „modnych” tweaków z blogów czy forów.
Bezpieczne fundamenty: aktualizacje, repozytoria i jądro
Polityka aktualizacji: różne tempo dla prod, test i staging
Aktualizacje to najtańszy hardening Linuxa w firmie, ale sposób ich wdrażania decyduje, czy będą chronić serwery, czy też wywoływać awarie. Kluczem jest rozdzielenie środowisk:
- dev/test – miejsce na szybkie wdrażanie nowych wersji pakietów, jąder, bibliotek. Tu można pozwolić sobie na bardziej agresywne aktualizacje, by wcześnie wychwycić problemy kompatybilności;
- staging/pre-prod – środowisko możliwie wierne produkcji. Aktualizacje pojawiają się tu po wstępnej weryfikacji na testach. Dobrze, gdy staging obciążony jest choć częściowo prawdziwym ruchem lub syntetycznym testem wydajności;
- prod – konserwatywne podejście: najpierw aktualizacje bezpieczeństwa, potem reszta. Wdrożenie po przetestowaniu na stagingu i z planem szybkiego rollbacku (snapshots, obrazy VM, backup konfiguracji).
Popularna rada „zawsze aktualizuj wszystko natychmiast” ma sens na laptopie developera, ale przy krytycznym serwerze bazodanowym może być ryzykowna. Lepiej krótkie opóźnienie i kontrola niż spontaniczna aktualizacja w środku dnia roboczego, zakończona niekompatybilnością bibliotek.
Automatyczne aktualizacje bezpieczeństwa: kiedy pomagają, a kiedy przesadzają
Mechanizmy typu unattended-upgrades (Debian/Ubuntu) czy dnf-automatic (RHEL/CentOS/Rocky/Alma) pozwalają automatycznie wciągać łatki bezpieczeństwa. Z punktu widzenia bezpieczeństwa firmy to atrakcyjne rozwiązanie, ale trzeba świadomie dobrać zakres i harmonogram.
Dobry kompromis w środowisku serwerowym:
- włączyć automatyczne aktualizacje tylko pakietów security (źródło: repozytorium security/updates),
- ustawić harmonogram na nocne okna i unikać restartów usług krytycznych w godzinach pracy,
- konfigurować powiadomienia mailowe o zainstalowanych aktualizacjach, by mieć ślad zmian.
Są jednak sytuacje, gdy automatyka jest zbyt ryzykowna. Dotyczy to zwłaszcza serwerów z aplikacjami, które agresywnie „przypinają się” do konkretnych wersji bibliotek (np. bardzo stare aplikacje ERP) lub wymagają certyfikacji z konkretnymi wersjami kernela. W takich przypadkach rozsądniejszy jest półautomatyczny proces: skrypt przygotowujący listę aktualizacji i ręczne zatwierdzanie po analizie.
Aktualizacja jądra: must-have czy potencjalna mina?
Jądro Linuxa to obszar, w którym bezpieczeństwo i stabilność często są w napięciu. Łatki bezpieczeństwa potrafią łatać krytyczne dziury, ale jednocześnie wprowadzać regresje dla konkretnych sterowników czy obciążeń.
Aktualizacja kernela jest bezdyskusyjnie wymagana, gdy:
- pojawia się poważna podatność z exploitem w obiegu (np. eskalacja uprawnień lokalnych używana w dziczy),
- serwer jest wystawiony bezpośrednio do Internetu (np. edge router, VPN gateway),
- kernel posiada błędy powodujące paniki lub zawieszanie przy typowym dla nas obciążeniu.
Lepiej poczekać, gdy:
- serwer ma bardzo wysokie SLA i brak jest środowiska testowego,
- nowa wersja kernela przynosi głównie funkcje, których nie używamy, bez krytycznych łatek bezpieczeństwa,
- jesteśmy zależni od sterowników vendorów (np. HBA, storage, specyficzny sprzęt), które nie zostały jeszcze przetestowane z nową wersją.
Dobrym kompromisem jest używanie długoterminowo wspieranych jąder (LTS) oraz planowanie ich aktualizacji w ramach regularnych przeglądów infrastruktury. Dodatkową warstwą bezpieczeństwa jest narzędzie typu kexec-based reboot czy live patching (np. kpatch, livepatch), ale w większości małych i średnich firm wystarczy rozsądna polityka LTS i monitorowanie ogłoszeń o krytycznych podatnościach.
Repozytoria vendorów: wygoda kontra spójność systemu
Wiele produktów serwerowych (bazy danych, systemy backupu, monitoring) oferuje własne repozytoria pakietów dla popularnych dystrybucji. To kuszące, bo zapewnia świeższe wersje niż te z „gołego” systemu, ale wprowadza też ryzyko konfliktów zależności.
Plusy korzystania z repozytoriów vendorów:
- często szybsze łatanie błędów w samej aplikacji,
- dostęp do narzędzi pomocniczych i pluginów, niewystępujących w oficjalnych repo,
- zwykle lepsze wsparcie techniczne („proszę użyć naszego repozytorium X”).
Minusy:
- potencjalne nadpisywanie bibliotek z dystrybucji (np. nowsza glibc, OpenSSL),
- konflikty wersji między różnymi repozytoriami zewnętrznymi,
- utrudnione aktualizacje całego systemu przy zmianie wersji dystrybucji.
Bezpieczniej jest przyjąć zasadę: na serwerze dedykowanym jednemu, krytycznemu produktowi (np. DB) dopuszczalne jest użycie repo vendora, ale pozostałe serwery korzystają głównie z oficjalnych repo dystrybucyjnych. Warto też dokumentować, które repozytoria są włączone na których maszynach, by uniknąć niespodzianek po roku.
LTS vs „świeża” dystrybucja: kiedy nowość szkodzi
„Najnowsza wersja” dystrybucji rzadko jest rozsądnym wyborem dla firmy. Początkowe wydania często zawierają regresje, niedopracowane narzędzia i zmiany, które zaskakują administratorów. Z drugiej strony, wiekowe dystrybucje bez wsparcia to zaproszenie dla atakujących.
W zastosowaniach firmowych bezpieczniejszy jest wybór:
- LTS (Long Term Support) – np. Ubuntu LTS, RHEL z odpowiednikami community. Dłuższy okres wsparcia bezpieczeństwa, mniejsza dynamika zmian, bardziej przewidywalne aktualizacje;
- konserwatywny cykl podnoszenia wersji – np. co 2–4 lata, po dokładnym przetestowaniu kluczowych aplikacji na nowej wersji.
Paradoksalnie, zbyt świeża dystrybucja może być wolniejsza biznesowo: więcej czasu pójdzie na łatanie niestabilnych pakietów i dostosowywanie konfiguracji, zamiast na realne podniesienie wydajności usług.

Twardnienie SSH: szybkie wygrane bez utrudniania życia
Wyłączenie hasła i roota: dobre domyślne, ale z planem awaryjnym
SSH to główna brama do serwerów Linux w firmie. Ataki słownikowe i skanowanie portów są normą. Najprostsze i najskuteczniejsze zmiany konfiguracyjne to:
- wyłączenie logowania hasłem (
PasswordAuthentication no),
Autoryzacja kluczami: prosto, ale z kontrolą dystrybucji
Sama zmiana PasswordAuthentication no bez przemyślenia, skąd biorą się klucze, kończy się często nocnym wyjazdem do serwerowni. Zanim loginy hasłowe znikną, trzeba poukładać kilka elementów:
- format i długość kluczy – współcześnie rozsądny standard to
ed25519lub RSA minimum 3072 bit (na starszych systemach, które nie lubią EdDSA). Dobrze, gdy w firmie istnieje jedna, spójna rekomendacja; - dystrybucja kluczy publicznych – ręczne wklejanie w
~/.ssh/authorized_keysna każdym serwerze kończy się bałaganem. Zdecydowanie lepiej użyć Ansible, Salt, Puppet albo przynajmniej własnego skryptu z Gita, który generuje plikiauthorized_keysna podstawie katalogu kluczy; - rotacja kluczy – popularna rada „kluczy nie trzeba zmieniać, dopóki nie wyciekną” jest wygodna, ale w praktyce nikt nie wie, kiedy laptop z prywatnym kluczem został skopiowany. Prostszym podejściem jest coroczna rotacja (np. przy przeglądzie kont) plus natychmiastowe unieważnianie kluczy przy odejściu pracownika.
Na serwerach, do których dostęp jest krytyczny (np. hypervisory, storage), można wprowadzić dwustopniową kontrolę: zwykli admini logują się z kluczami, ale operacje destrukcyjne wymagają użycia konta „break-glass” z dodatkowymi procedurami – np. dostęp przez bastion, logowanie sesji, potwierdzenie drugiej osoby.
SSHD_config: minimalne zmiany, które dają realny efekt
W sieci krąży wiele „twardych” szablonów sshd_config z długą listą egzotycznych opcji. W praktyce na 90% firmowych serwerów wystarczy kilka konkretnych ustawień:
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
X11Forwarding no
AllowTcpForwarding no
ClientAliveInterval 300
ClientAliveCountMax 2
MaxAuthTries 3
LoginGraceTime 30
Te zmiany:
- obcinają powierzchnię ataku (brak roota, brak haseł, mniej feature’ów typu X11 i forwarding),
- ograniczają „wiszące” sesje SSH, które przez pomyłkę latami zostają na otwartym tmuxie,
- hamują ataki słownikowe, bo kilka nieudanych prób szybko zrywa połączenie.
Głośno polecany zabieg „zmień port 22 na inny” faktycznie ogranicza szum w logach, ale nie jest środkiem bezpieczeństwa. Port niestandardowy ma sens, gdy z jakichś względów logi musi czytać człowiek (a nie SIEM czy skrypt) – dzięki temu będzie mniej śmieci. Nie ma sensu traktować tego jako „obronę” przed kimkolwiek poważnym.
Fail2ban i podobne: kiedy naprawdę coś dają
Fail2ban i jemu podobne narzędzia lubią być instalowane odruchowo. Zanim wylądują na każdym serwerze, trzeba odpowiedzieć na dwa pytania: czy SSH jest naprawdę wystawione do Internetu i czy ruch przechodzi przez firewall centralny.
Zastosowanie ma sens, gdy:
- serwer ma publiczny adres i przyjmuje bezpośrednie połączenia SSH,
- nie korzystasz z jump hosta/bastionu, tylko ludzie logują się „wszędzie zewsząd”,
- logi autoryzacji nie są zbierane do SIEM-a, który sam blokuje IP po serii nieudanych prób.
Jeśli SSH jest dostępnne wyłącznie z VPN-a albo przez pojedynczy bastion, fail2ban na każdym serwerze ryzykuje więcej chaosu (np. zablokowane IP wewnętrzne) niż zysków. W takim modelu sensowniejsza jest:
- twarda konfiguracja SSH (klucze, brak haseł),
- dobre logowanie i alerty na nietypowe loginy,
- ewentualna kontrola na warstwie bastionu/VPN (MFA, polityki urządzeń).
Bastion SSH: dodatkowy krok czy realne wzmocnienie
Centralny bastion (jump host) bywa postrzegany jako „kolejna rzecz do utrzymania”. Tymczasem przy kilku–kilkunastu adminach i dziesiątkach serwerów upraszcza życie bardziej niż komplikuje:
- logi z jednej maszyny obejmują faktyczny ruch SSH do całej infrastruktury,
- na serwerach aplikacyjnych można ograniczyć
AllowUsers/AllowGroupstylko do kont technicznych używanych z bastionu, - podłączając bastion do SSO (np. LDAP/Kerberos/SAML+MFA), dostaje się centralne wyłączanie dostępu przy odejściu pracownika.
Popularny błąd: postawienie bastionu „na bok” i jednoczesne pozostawienie otwartych portów SSH na wszystkich serwerach. Model ma sens dopiero wtedy, gdy tylko bastion przyjmuje połączenia z Internetu, a pozostałe maszyny są dostępne wyłącznie z sieci wewnętrznej lub samego bastionu.
Sysctl: co faktycznie przyspiesza, a co psuje serwer
Filtracja „magicznych pastebinów” z sysctl.conf
Najczęstszy „tuningu” sysctl w firmach to wklejenie przypadkowego bloku z bloga: dziesiątki parametrów, z których większości nikt nie rozumie. Problemem nie jest sama zmiana, ale brak świadomości efektu. Bez kontekstu te same ustawienia mogą przyspieszyć jeden system i zabić inny.
Rozsądna strategia to ograniczenie się do parametrów, które:
- adresują konkretny, zaobserwowany problem (np. zbyt mało portów efemerycznych),
- są wspólne i dobrze opisane w dokumentacji vendorów (np. rekomendacje PostgreSQL, Elastic, Nginx),
- są łatwe do cofnięcia i nie zmieniają semantyki bezpieczeństwa (np. cache ARP, TCP backlog).
TCP i sieć: zmiany, które widać w metrykach
Przy serwerach HTTP, proxy, load balancerach czy brokerach wiadomości warto skupić się na kilku obszarach zamiast ustawiać hurtowo kilkadziesiąt opcji TCP:
- zakres portów efemerycznych – przy dużej liczbie krótkich połączeń (np. reverse proxy) porty potrafią się wyczerpywać:
net.ipv4.ip_local_port_range = 10240 65535Przyda się też monitorowanie liczby połączeń w stanie TIME_WAIT i aktywnych gniazd;
- wielkości kolejek (backlog) – gdy w logach serwera aplikacyjnego pojawiają się timeouty podczas szczytów ruchu, a CPU i RAM są spokojne, przydatne mogą być:
net.core.somaxconn = 1024 net.ipv4.tcp_max_syn_backlog = 4096Nadmierne zawyżanie tych wartości na słabych VM-kach zwiększy za to opóźnienia i kolejki;
- recykling TIME_WAIT – często kopiowany pakiet ustawień z
tcp_tw_recycleitcp_tw_reusejest przestarzały i w nowoczesnych kernelach nawet nie działa lub powoduje trudne do wykrycia problemy z load balancerami. Bez dokładnego zrozumienia działania NAT i topologii ruchu lepiej ich nie ruszać.
Przy łączach o dużej przepustowości i opóźnieniu (np. replikacja między DC w różnych krajach) pojawia się temat okien TCP i buforów. Zamiast ręcznie grzebać w tcp_rmem / tcp_wmem, rozsądniej jest skorzystać z narzędzi typu iperf3 i zalecanych wartości opartych na Bandwidth-Delay Product, dostosowanych do konkretnego linku.
VM i pamięć: kiedy „wyłącz swappiness” robi krzywdę
Popularna porada „ustaw vm.swappiness = 0 i będzie szybciej” jest kusząca, ale rzadko ma sens na serwerach firmowych. Agresywne wyłączanie swapa często kończy się panicznym zabijaniem procesów przez OOM-killera, zamiast planowego wypchnięcia mało używanych stron do swapu.
Bardziej zrównoważone podejście to:
vm.swappiness = 10–30– system unika nadmiernego swapowania, ale ma margines bezpieczeństwa przy nagłych pikach pamięci;vm.vfs_cache_pressure = 50–100– dla serwerów plików i baz danych, gdzie cache katalogów jest cenny. Zbyt niska wartość (np. 10) może natomiast utrudnić odzyskiwanie pamięci;- monitoring page faultów i io wait – bez danych zmiana swappiness to zgadywanie.
Radykalne ustawienia swapu mają sens na wąskiej klasie systemów: maszynach o ściśle kontrolowanych procesach, gdzie każdy daemon ma surowe limity pamięci, a aplikacje są przewidywalne (np. appliance’y, urządzenia sieciowe na Linuksie). W typowej firmowej VM-ce lepszy jest umiarkowany kompromis.
Bezpieczeństwo przez sysctl: RDNS, source routing i „szum tła”
Kilka parametrów sysctl daje wyraźne wzmocnienie bezpieczeństwa przy minimalnym ryzyku ubocznym, szczególnie na hostach wystawionych do Internetu lub pełniących rolę routerów:
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.default.accept_source_route = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
Te ustawienia:
- blokują archaiczne i rzadko używane mechanizmy (source routing), które są de facto nośnikiem ataków,
- ograniczają manipulowanie trasami przez nieautoryzowane ICMP redirect,
- zwiększają szanse, że nieprawidłowo routowany pakiet zostanie odrzucony.
Trzeba natomiast uważać z agresywnymi filtrami RP na hostach z wieloma interfejsami i asymetrycznym routingiem; w takich środowiskach niekiedy lepszy jest rp_filter=2 (loose mode) zamiast twardego 1.

Pamięć i swap: szybka reakcja zamiast „więcej RAM”
Monitoring zamiast intuicji: jak odróżnić „RAM pełny” od „RAM zdrowy”
„Wykres RAM na 95%, trzeba dokupić pamięci” – to klasyczny moment, w którym wykresy mylą zjawisko z problemem. Linux używa wolnej pamięci na cache page cache i buffery, więc pełny RAM nie oznacza kłopotu. Prawdziwego obrazu szuka się w kilku miejscach:
- page cache vs anon – ile RAM-u zużywają dane stron anonimowych (procesy) w porównaniu do cache dysku,
- major/minor page faults – czy procesy stale „szukają” danych na dysku czy w RAM-ie,
- procent czasu w iowait – wysokie iowait przy jednoczesnym wzroście użycia swapu to recepta na „przymulenie”.
Zamiast odruchowego skalowania pionowego często wystarczy:
- zidentyfikować 1–2 procesy „rozlewające się” w pamięci (np. JVM bez limitu heap, Elastic bez
ES_JAVA_OPTS), - dostosować ich limity (cgroups, systemd
MemoryMax), - skorygować rozmiar cache lub swappiness, jeśli kernel zbyt wcześnie wyrzuca strony aplikacji.
Swap: mały, szybki i pod kontrolą
Swap nie jest wrogiem wydajności, tylko poduszką bezpieczeństwa. Problem zaczyna się, gdy:
- znajduje się na wolnym, współdzielonym storage (np. stary SAN współdzielony z backupem),
- rozmiar jest przesadny, więc system trzyma w swapie dane, które lepiej byłoby wymusić do RAM-u lub zabić proces.
Przy większości serwerów aplikacyjnych lepiej działa konfiguracja:
- niewielki, ale szybki swap (np. kilkanaście–kilkadziesiąt GB na lokalnym SSD NVMe zamiast gigantycznej przestrzeni na wolnym NFS),
- monitoring tempo przyrostu użycia swapu, a nie tylko jego procent zapełnienia,
- alarmy przy długotrwałym wysokim iowait i wzroście swap in/out – to oznacza, że system jest już w „strefie cierpienia”.
Na hostach z krytycznymi bazami danych (np. PostgreSQL, Oracle) zdarza się rekomendacja „zero swapu”. W praktyce bezpieczniej jest:
- ustawić mały swap i agresywny monitoring,
- ograniczyć proces DB do rozsądnego maksymalnego zużycia RAM zamiast pozwalać mu zjeść wszystko,
- wykorzystać mechanizmy DB (work_mem, shared_buffers, cache) do lepszego zarządzania pamięcią.
OOM-killer: lepiej przygotować się zawczasu
Kontrolowane awarie zamiast losowej rzezi procesów
OOM-killer nie jest złośliwy, tylko bezradny: ma za mało informacji, żeby wiedzieć, które procesy są naprawdę krytyczne biznesowo. Jeśli zostawić wszystko domyślnym heurystykom, w kryzysie pamięci zniknie akurat ten daemon, którego restart zaboli najbardziej.
Bardziej przewidywalny model to połączenie kilku warstw:
- priorytety OOM – dla procesów, które mogą zostać zabite (np. batchowe joby, pomocnicze workery), ustaw niższy priorytet:
systemd-run --unit=worker.service --property=OOMScoreAdjust=500 <komenda>dla usług krytycznych z kolei
OOMScoreAdjust=-500w pliku unit. Nie chodzi o „niezabijalność”, tylko o sterowanie kolejnością; - cgroups / systemd MemoryMax – zamiast czekać, aż cały host wpadnie w OOM, lepiej odseparować usługi pamięciowo:
[Service] MemoryMax=4G MemoryHigh=3GMemoryHighwymusza early reclaim i throttling,MemoryMaxjest twardym limitem z sygnałem dla procesu; - lokalne „bezpieczniki” – skrypty watchdog, które przy przekroczeniu określonego użycia RAM przez usługę restartują ją kontrolowanie, zamiast czekać na globalny OOM.
Dopiero przy takim podejściu sens zyskują ustawienia typu niższy vm.swappiness czy agresywniejszy reclaim cache. W przeciwnym razie każda optymalizacja pamięci kończy się losową awarią w najgorszym momencie.
HugePages, transparent hugepages i bazy danych
Popularna recepta „wyłącz transparent hugepages (THP), bo spowalnia” jest z grubsza słuszna dla większości serwerów bazodanowych, ale źle zastosowana potrafi spowodować regres zamiast zysku.
Racjonalne kroki wyglądają tak:
- dla klasycznych baz SQL (PostgreSQL, Oracle) – zwykle:
echo never > /sys/kernel/mm/transparent_hugepage/enableda następnie skonfigurowanie statycznych HugePages zgodnie z rekomendacją vendora. Daje to przewidywalne zachowanie i mniejsze fragmentowanie pamięci;
- dla JVM, Elastic, big data – trzeba najpierw sprawdzić, czy aplikacja potrafi sensownie korzystać z hugepages. Część stosów big data preferuje THP w trybie
madvisezamiast całkowitego wyłączenia:echo madvise > /sys/kernel/mm/transparent_hugepage/enabledTo ogranicza overhead na alloc/free bez globalnego „betonowania” pamięci;
- w VM-kach z małym RAM (2–4 GB) – odpalanie hugepages często nie ma realnego zysku, za to zmniejsza elastyczność allocatorów. Tam THP=never bywa po prostu rozsądniejszą prostotą.
Zanim zmieni się cokolwiek przy hugepages, dobrze jest zebrać profil: ilość page faultów, fragmentację, latencje alokacji pamięci (perf, bcc-tools). Bez tego decyzja sprowadza się do „działa / nie działa” w oparciu o anegdoty z forów.
System plików i I/O: ext4, XFS, mount options, kolejkowanie
ext4 vs XFS: mniej religii, więcej profilu I/O
Spór „ext4 czy XFS” przypomina dyskusję o najlepszym edytorze. Oba systemy plików są dojrzałe i bezpieczne; różnice wychodzą dopiero przy określonych wzorcach I/O.
- ext4 zwykle sprawdza się:
- na mniejszych filesystemach (np. rootfs VM, kilkaset GB),
- przy mieszanym obciążeniu: logi, małe pliki, domy użytkowników,
- gdy liczy się prosta administracja i „wszędzie tak mamy”.
- XFS ma przewagę:
- przy dużych wolumenach (TB+) i systemach backupu / archiwizacji,
- w środowiskach z masą równoległych zapisów sekwencyjnych (np. logi, monitoring),
- gdy narzędzia vendora (np. niektóre appliance’y) są pod XFS optymalizowane.
Popularna rada „przenieś logi na XFS, będzie szybciej” ma sens, jeśli CPU faktycznie spędza czas na journalingu i alokacji inodów, a dysk jest w stanie obsłużyć większą równoległość. Na starych, przeciążonych SAN-ach różnica bywa kosmetyczna w porównaniu z poprawą cache’owania i ograniczeniem liczby synców.
Opcje montowania: gdzie są realne zyski
Zamiast kombinować z egzotycznymi flagami, lepsze efekty daje kilka dobrze rozumianych opcji montowania:
- noatime / relatime – wyłączanie aktualizacji atime przy każdym odczycie:
/dev/sda1 /data ext4 defaults,noatime 0 2relatimejest już domyślne w wielu dystrybucjach; przejście nanoatimema sens tam, gdzie metadane nie są wykorzystywane (cache, tymczasowe dane aplikacji). Dla niektórych narzędzi backupu i MTA pełne atime nadal bywa potrzebne; - barrier / nobarrier – wyłączanie barier zapisu może przyspieszyć I/O, ale tylko jeżeli:
- warstwa storage ma własny, bateryjnie chroniony cache (BBU, supercap),
- jest pewność, że kontroler nie „udaje” synchronizacji.
W przeciwnym razie ryzykuje się utratą danych po zaniku zasilania. To nie jest flaga do stosowania „bo na blogu mieli +20% IOPS”;
- discard / fstrim – dla SSD lepiej zwykle unikać mountu z
discardi zamiast tego robić okresowe:fstrim -avnp. z crona lub timerem systemd. Ciągły discard potrafi zwiększyć latencję przy intensywnym zapisie;
- data=ordered / writeback / journal – na ext4 ustawienie
data=orderedjest sensownym kompromisem. Przechodzenie nawritebackdla „więcej IOPS” kosztem spójności danych ma sens tylko na czysto tymczasowych wolumenach (cache, scratch).
Najpierw trzeba ustalić, co boli: opóźnienia zapisu, odczytu, metadane czy blokujące sync() z aplikacji. Dopiero pod to dobiera się flagi, zamiast stosować jeden zestaw „magicznych” opcji wszędzie.
Rozmiar bloków i alignowanie pod storage
Linux nadal zbyt często jest instalowany „na pamięć”: domyślne 4k bloki, domyślna geometria, zero refleksji nad tym, co jest pod spodem. Na współczesnych macierzach i SSD wyrównanie ma realne znaczenie.
- alignowanie partycji – przy tworzeniu LVM na RAID/SSD trzeba upewnić się, że pierwsza partycja zaczyna się na granicy 1 MB / 4 MB, a nie np. 63 sektorze:
parted /dev/sdb --align optimal mkpart primary 1MiB 100%Błędne wyrównanie skutkuje podwójnymi zapisami i wyższym zużyciem SSD;
- stripe size / stripe width dla RAID – przy macierzach sprzętowych:
mkfs.ext4 -E stride=<k>,stripe-width=<m> /dev/mapper/vg-lvgdzie
strideto liczba bloków FS w jednym pasku RAID, astripe-width– w całej macierzy. W praktyce poprawna konfiguracja zmniejsza losowe skoki latencji przy dużym obciążeniu; - duże bloki (mkfs.xfs -b size=…) – rozsądne przy dominacji dużych plików (backupy, multimedia). Dla systemów, które trzymają miliony drobnych plików (miniatury, pliki tymczasowe), nadmierne zwiększanie bloku powoduje marnowanie przestrzeni i gorsze cache’owanie.
Journaling i logi: mniej fsync, więcej batching
Klasyczny schemat „aplikacja loguje każdy request z fsync()” dobrze wygląda w teorii – pełna trwałość, nic nie ginie. W praktyce kończy się lawiną małych synchronicznych zapisów, które zabijają latency całego filesystemu.
Łagodniejsza strategia to:
- przekierowanie logów na osobny filesystem (nawet mały LV), aby rozsypanie I/O na logach nie degradowało reszty danych,
- konfiguracja loggera (rsyslog, journald, logback) na batchowanie i grupowanie zapisów zamiast fsync na każdy wpis,
- asynchroniczny forwarding logów do centralnego systemu (Loki, Elasticsearch, Splunk) i trzymanie na hostach tylko bufora.
Popularne „rozwiązanie” w postaci przerzucenia logów na NFS potrafi pogorszyć sytuację: wąskie gardło przenosi się w sieć, a każda awaria serwera logów blokuje lokalne I/O. Bezpośredni zapis na lokalny SSD + replikacja logów agentem zwykle jest szybsza i stabilniejsza niż centralny NFS „bo tak wygodniej adminowi”.
Czytanie kolejkowania I/O: scheduler, QD, QoS
Na nowoczesnych systemach z NVMe kernel coraz częściej ustawia scheduler na none i oddaje optymalizację kontrolerowi. Dorzucanie ręcznie deadline czy bfq ma sens tylko w konkretnych przypadkach:
- HDD / starsze SAN –
deadlineczęsto daje bardziej przewidywalne czasy odpowiedzi przy mieszanych obciążeniach (baza + backup). Można to ustawić per-device:echo deadline > /sys/block/sdX/queue/scheduler - Desktop / stacje robocze –
bfqpotrafi poprawić „responsywność” przy jednoczesnym intensywnym I/O w tle, ale na serwerach zazwyczaj wprowadza zbyt wiele magii; - NVMe w VM – najczęściej lepiej zostawić scheduler na
nonei zamiast tego zadbać o poprawną konfigurację storage po stronie hypervisora (cache, writeback, QoS).
Przed zmianą schedulera ważniejsze jest zrozumienie głębokości kolejki (queue depth, QD) i limitów po stronie macierzy. W środowiskach, gdzie wiele hostów „bije” w jeden storage, można zyskać więcej, ograniczając agresywność I/O na poziomie hosta (np. cgroups blkio, IOReadBandwidthMax/IOWriteBandwidthMax w systemd) niż dopieszczając scheduler.
NFS w firmie: wygoda kontra opóźnienia
NFS bywa traktowany jako uniwersalny „dysk sieciowy”. Dla wielu obciążeń to działa, ale przy krytycznych usługach różnice w zachowaniu NFS vs lokalne I/O są znacznie większe niż różnice między ext4 a XFS.
Przy serwerach aplikacyjnych:
- unikaj trzymania katalogów roboczych aplikacji na NFS (cache, temp, upload w trakcie obróbki). To powinno być na lokalnym, szybkim storage, a na NFS ewentualnie trafiać wynik końcowy,
- dla współdzielonych zasobów ustaw rozsądne wartości rsize/wsize oraz tryb
async/synczależnie od ważności danych:server:/share /mnt/share nfs rw,noatime,rsize=1048576,wsize=1048576,hard,intr 0 0Zwiększanie rsize/wsize ponad zdolności sieci + storage prowadzi do retransmisji i „mikro-freezów”;
- zapewnij oddzielne łącze lub przynajmniej QoS dla NFS. Sytuacja, w której backup saturuje link i blokuje I/O aplikacji, to klasyk.
Lokalne SSD jako akcelerator: cache zamiast migracji
Częsta pokusa przy powolnym storage to całkowita migracja danych na lokalne SSD. Nie zawsze jest to możliwe (koszt, pojemność, HA). Zamiast tego można traktować SSD jako warstwę cache dla gorących danych:
- na poziomie aplikacji – cache’owanie plików w /var/cache, trzymanych na SSD, i okresowe odświeżanie z macierzy,
- na poziomie systemu plików – bcache, dm-cache, czy mechanizmy wbudowane w rozwiązania SDS (Ceph, lvmcache). Wymagają one jednak solidnego monitoringu i planu awaryjnego, gdy SSD zacznie zgłaszać błędy,
- na poziomie baz danych – przeniesienie plików WAL / redo logów na SSD, pozostawiając tabele na wolniejszym, ale pojemnym storage.
Ta strategia dobrze działa w środowiskach, gdzie profil obciążenia ma wyraźne „gorące” i „zimne” dane. Jeżeli całość datasetu jest permanentnie „gorąca”, cache zamieni się w kolejny wąski gardło, a problemem jest po prostu zbyt wolny storage względem wymagań.






