Chybějící swapový soubor, který zamrazil naše servery
- září jeden z našich produkčních serverů přestal odpovídat. Load balancer ho označil jako nezdravý a náš nástroj pro vzdálené příkazy se k němu nedostal – ale nic nespadlo a nic nebylo zabito kvůli příliš velké spotřebě paměti. Server thrashoval: bez swapového prostoru a s kontejnery, které mohly použít víc paměti, než stroj měl, Linux trávil čas vystěhováváním a znovu načítáním souborů, které potřeboval k běhu, a skoro nepostupoval. Swapový soubor o velikosti 2 GB na každém serveru to vyřešil. Zajímavější ponaučení se týkalo zdravotních kontrol.
Jak vypadalo „zamrznutí“
- Zdravotní kontroly load balanceru na tom serveru vypršely.
- Příkazy poslané přes AWS Systems Manager zůstávaly ve stavu „Pending“ a nikdy neproběhly.
- V logách nebylo žádné zabití kvůli nedostatku paměti. Žádný proces nezemřel.
O týden dřív jsme přesunuli produkci na menší ARM servery se 2 GB RAM na každém. Náš stagingový server – ještě menší – se stejným způsobem zamrzl při rozbalování image kontejneru během nasazení.
Proč žádná chyba paměti?
Linux zabije proces až tehdy, když skutečně dojde paměť, kterou může uvolnit. Dřív uvolňuje paměť z page cache – kopií souborů, které drží v RAM, včetně kódu každého běžícího programu.
Na našich serverech součet paměťových limitů kontejnerů činil asi 3,3 GB na stroji s přibližně 1,8 GB využitelnými. Normálně nevyužijí všechny svůj limit najednou. Když ano, jádro neustále vystěhovávalo kód programů z cache, aby uvolnilo místo, a pak ho muselo číst zpět z disku, jakmile zase běžel. Ten cyklus – thrashing – může stroj udělat téměř úplně neodpovídajícím, přitom technicky nikdy nedojde paměť. Takže nic není zabito a nic se nezotaví.
Bez swapu nemá jádro kam odložit paměť, která je alokovaná, ale zřídka používaná, takže ji musí vzít z cache.
Oprava
Skript pro bootstrap serveru nyní vytvoří swapový soubor o velikosti 2 GB s nízkou hodnotou swappiness (10) při každém startu nebo opravě serveru. Nízká swappiness znamená, že jádro stále dává přednost držení pracovní paměti v RAM, ale zřídka používané stránky se mohou přesunout na disk, místo aby vynucovaly vystěhování kódu programů z cache. Aplikovali jsme to živě na oba produkční servery a na staging a pipeline nasazení znovu publikuje bootstrap skript při každém nasazení do produkce, takže i nahrazený server ho dostane.
Co nás znepokojilo víc: nezdravý server zůstal
Naše skupina serverů používala EC2 zdravotní kontroly – „běží stroj?“ – a ne zdravotní kontroly load balanceru – „odpovídá na požadavky?“. Server, který load balancer vzdal, byl pro skupinu stále zdravý. Nikdy nebyl nahrazen. Produkce by tiše běžela na jednom serveru, dokud by si toho někdo nevšiml a druhý nerestartoval.
Přepnout skupinu na zdravotní kontroly load balanceru zní jako zjevná oprava, ale není to pro nás přímočaré: během nasazení servery záměrně odpovídají na svůj zdravotní endpoint chybou, aby je balancer vyprázdnil (viz jak jsme dosáhli nula 502 při nasazení). Skupina, která by servery nahrazovala na základě tohoto signálu, by je nahrazovala uprostřed každého nasazení.
Proto jsme prozatím přidali do kontrolního seznamu nasazení krok: po každém nasazení zkontrolovat zdraví každého cíle na load balanceru, ne jen to, zda úloha nasazení prošla. Pokud příkazy na server visí ve stavu „Pending“, thrashuje – restartujte ho; je už mimo rotaci.
Co jsme si z toho odnesli
- „Nepadá“ neznamená „zdravé“. Thrashing nevyvolá žádnou chybu, jen zpomalení tak extrémní, že stroj přestane odpovídat.
- Malé servery potřebují swap. I trochu, s nízkou swappiness, dá jádru místo, kam uložit studenou paměť.
- Paměťové limity, jejichž součet převyšuje stroj, jsou sázka. Někdy ji prohrajete. Vězte, kdy jste ji uzavřeli.
- Vědět, které zdravotní kontrole vaše autoškálování věří. Zdravotní kontrola, která se ptá jen „je VM zapnutá?“, nenahradí server, který přestal fungovat.