Otázka zní téměř vždy stejně: v noci web odpovídal dvacet sekund a k ránu se všechno samo spravilo. Není už na co se dívat — top odpovídá za aktuální sekundu, zatímco ptáte se na třetí hodinu ranní. Níže je, co čísla zátěže skutečně znamenají, která z nich se musí číst v párech a proč jednorázové měření mate častěji, než pomáhá.
Load average: tři čísla a jeden častý omyl
uptime
cat /proc/loadavg
nproc
Ta tři čísla jsou průměry za jednu, pět a patnáct minut. Neporovnávají se s nulou, ale s počtem jader, který uvede nproc. Hodnota 8 na osmijádrovém stroji je plná, ale zdravá zátěž: práce je přesně tolik, kolik server zvládne odbavit. Hodnota 2 na jednojádrovém VPS je fronta dvakrát delší, než kolik server unese, a každý požadavek čeká, až na něj přijde řada.
Poměr těch tří čísel mezi sebou udává směr. Minutová hodnota výrazně nad patnáctiminutovou znamená, že zátěž právě teď roste. Naopak znamená, že vrchol už minul a vidíte jen jeho konec.
Teď k omylu, kvůli kterému se tato čísla chápou nejčastěji špatně. Load average v Linuxu není „vytížení procesoru“. Na rozdíl od jiných unixových systémů do něj Linux započítává nejen procesy běžící nebo připravené k běhu, ale i ty ve stavu D — v nepřerušitelném spánku. Tedy čekající na disk nebo na síťový souborový systém. Odtud server se zátěží 12 a téměř nečinným procesorem: práce nepostupuje, všichni stojí ve frontě.
Procesor, nebo disk
Oddělit tyto dva případy je první věc, kterou má smysl udělat:
vmstat 1 5
iostat -x 1 3
Ve výstupu vmstat se počítají tři sloupce. r je počet procesů ve frontě na procesor, b počet procesů zablokovaných čekáním na vstup a výstup a wa podíl času, který procesor strávil nečinným čekáním na disk. Trvalé wa nad 10–15 % při skromných us a sy znamená, že server naráží na disk: přidávat jádra nemá smysl, ta stávající jsou stejně volná.
Které procesy čekají, lze vypsat jmenovitě:
ps -eo state,pid,comm,wchan:30 | awk '$1 ~ /^D/'
Příklad z vlastní praxe. Ve výchozí konfiguraci zapisuje Suricata na tři desítky typů událostí a žádnou rotaci si k nim nenastaví — na provozním serveru z toho bylo 15 GB logů za dva dny. Zátěž se přitom neprojevovala jako vytížený procesor, ale právě jako I/O wait: systém psal bez přestání a všechno ostatní stálo ve frontě za ním. Lékem není větší tarif, ale kratší seznam typů událostí a nastavená rotace.
Opačný případ, čistě procesorový a mnohem méně zjevný. Webová stránka volala apt pod uživatelem, pod kterým běží PHP-FPM. Root drží binární cache v /var/cache/apt/pkgcache.bin — na stroji s tuctem repozitářů je to 70 MB — a mapuje si ji do paměti zadarmo. Běžný uživatel do toho adresáře psát nesmí a cache si při každém volání staví znovu. Změřeno na jednom a tomtéž stroji: 0,01 sekundy procesorového času jako root proti 4,2 sekundy jako neprivilegovaný uživatel. Tentýž příkaz, rozdíl čtyřsetnásobný, násobený každým zobrazením stránky.
Závěr z obou případů je stejný: zátěž se měří, nehádá. Změřit každý podezřelý příkaz zvlášť zabere půl hodiny a obvykle to ukáže jinam, než mířilo původní podezření.
Paměť: free neukazuje to, jak vypadá
free -h
Sloupec used sám o sobě říká velmi málo a sloupec free přímo mate: Linux dává nevyužitou paměť stránkové cache a na první vyžádání ji aplikacím vrací. Číst se má sloupec available — kolik lze zabrat, aniž by se šlo do swapu. Velký buff/cache není problém, ale známka systému, který pracuje, jak má.
U swapu nerozhoduje aktuální hodnota, ale tvar. Vystoupal a vrátil se na nulu: byl krátký vrchol. Vystoupal jednou a zůstal: vrchol už proběhl, stránky byly vytlačeny a nikdo je zpět nevrací — server působí klidně, ačkoli paměť mu v jednu chvíli nestačila. Jestli odkládání probíhá právě teď, ukazují sloupce si a so ve vmstat; nenulové hodnoty tam jsou ta podoba zpomalení, které uživatelé vnímají nejvíc.
Pokud během nočního propadu nějaký proces prostě zmizel, vysvětlení bývá tady:
journalctl -k --since yesterday | grep -i "out of memory"
dmesg -T | grep -i "killed process"
Řádek typu Out of memory: Killed process 1234 (mysqld) uzavře věc lépe než jakýkoli graf: databáze „nespadla sama“, zastavilo ji jádro, protože došla paměť.
Kdo to dělá
ps aux --sort=-%cpu | head -10
ps aux --sort=-%mem | head -10
Jedna výhrada: %CPU v ps je průměr za celý život procesu, takže krátký výkyv se v tom seznamu ztratí. Na ten je potřeba top nebo pidstat 1 5, které měří za interval.
Kdy se zátěž stává bezpečnostní otázkou
Rovných 100 % procesoru v noci, s procesem, který má nicneříkající jméno a pracovní adresář v /tmp nebo /dev/shm, je klasický obrázek těžaře, nikoli webu, který povyrostl. Špička příchozího provozu spolu s růstem zápisů do logů je probíhající útok na hesla. Prudce narostlý objem logů bývá bouře falešných poplachů nějakého filtru.
Samostatnou kategorií jsou samotné bezpečnostní nástroje. Na jednom z našich serverů se zátěž držela kolem tří bez jediného návštěvníka a v čele ps podle nasbíraného procesorového času nestál web ani databáze, ale CrowdSec, fail2ban, Falco a Suricata. Není to porucha ani důvod je vypínat, ale cenu ochrany je dobré znát v číslech: na slabém VPS je znát.
K čemu je sledování
Všechno výše uvedené odpovídá na otázku „co se děje teď“. Ranní otázku — co bylo ve tři hodiny v noci — tyto příkazy nepokrývají: o uplynulé noci nejsou žádná data, pokud je nikdo nezaznamenal. A stavět kvůli jedinému VPS Prometheus s Grafanou je sporné, protože sledující stack vyjde těžší než sledovaný server.
Stačí řádek v databázi každých pět minut a jedna stránka, která z něj vykreslí posledních čtyřiadvacet hodin: load average, vytížení procesoru a zvlášť I/O wait, paměť a swap, čtení a zápis disku, zaplnění oddílu, i-uzly, souborové deskriptory, spojení. Na jedné časové ose se dvojice čtou pouhým okem: vysoké wa při klidném procesoru, swap, který se nikdy nevrátil na nulu, deskriptory blížící se limitu — nastupující chyba too many open files je vidět hodiny předtím, než nastane. Jak to vypadá poskládané dohromady, ukazuje ukázková stránka níže.