Dobrá práce, máš můj obdiv.

Zeptám se myslíš, že v Proxmoxu narazili na podobný problém?
Přeci jen je to LXC & ZFS a cgroups? Určitě jsou tam aplikace, které také používají mmap, ne? Několikrát jsem četl, že hodně lidí mmap doporučuje ideálně pro velké soubory.
např.
tady https://blog.askesis.pl/post/2019/02/mmap.html
https://stackoverflow.com/questions/258091/when-should-i-use-mmap-for-file-access/2895799

Zdenek pripravto

čt 13. 8. 2020 v 14:22 odesílatel Ondrej Beranek <rainbof@gmail.com> napsal:
Poutave vysvetleni, dobra prace diky.

čt 13. 8. 2020 v 13:02 odesílatel Pavel Snajdr <snajpa@snajpa.net> napsal:
Ahojte,

sliboval jsem (vetsinou off-list), ze shnu pak resenici s pameti na
vpsAdminOS, tak tady to je :)

Kdyz chce programator dneska z aplikace pracovat s velkym souborem, je
docela rozsirenym pristupem, si takovy soubor namapovat do pameti pomoci
mmap(2) syscallu.

To zpusobi, ze se soubor ocitne ve virtualnim pametovem prostoru
procesu, tj. ten program si pak muze sahat do toho souboru prostym
pristupovanim do pameti (nabizi se teda snadny ukladani napr. Cckovych
structu do souboru, pristup pres pointerovou aritmetiku, atd.).

Linux cachuje takovy napamovany soubor po strankach, protoze samotnou
pamet spravuje po strankach (1 stranka pameti je na x64 velka 4 kB, huge
pages jsou potom dalsi extra story na jindy...). Jakmile aplikace chce
precist nejaka data, Linux na pozadi, pokud uz to neudelal, pro ta data
alokuje stranku fyzicke pameti, aby ta data mela realne, kde sedet,
precte je do te stranky z disku (nebo kde ten soubor je) a pak tu
stranku namapuje do prislusneho mista virtualniho pametoveho prostoru
naseho procesu, ktery ta data cte.

Jadro vede mapovane stranky v evidenci pomoci LRU listu, coz je datova
struktura, seznam, ktery se vyznacuje tim, ze vede v evidenci, ktera
polozka byla pouzita naposled (tak, ze se meni pri jejim pouziti jeji
poradi na zacatek seznamu).

Kdyz vsechno funguje jak ma, v realne fyzicke pameti jsou pouzivana
ctena data a jeste nezapsane "pospinene" (dirty) stranky, do kterych se
psalo, tj. je u nich naplanovano, aby se co nejrychleji dostaly na disk
(pokud teda cely soubor nebyl otevreny s flagem O_SYNC, nebo podobne, co
by vynutilo kazdou zmenu zapsat na disk ihned, nez Linux vrati kontrolu
aplikaci pri tom zapisu do mapovaneho souboru; to neni tak caste a to je
nam ted "jedno").

Zapis je nastesti vyreseny dobre, Linux ma na to mechanismus, kteremu
rika "writeback throttle"; kdyz detekuje, ze se zacina RAM plnit vic,
nez je zdravo, zacne aplikaci ty zapisujici pristupy adekvatne
zpomalovat. Tohle "impedancni prizpusobeni" funguje vcelku dobre, navic
funguje dostatecne dobre i pod memory cgroup.

Memory cgroup je mechanismus, kterym omezujeme pridelenou pamet
kontejnerum pod Linuxem - je to volitelna sada dalsich pocitadel vyuziti
pameti, nad zakladni systemove, plus vydeleni ukazatelu na LRU,
writeback a dalsi cache, aby se dalo pekne vest takovehle seznamy
stranek v oddelene, mimojine aby bylo jasne, co komu patri, kdyz dojde
cas tu pamet jednou odklidit. Ale taky, aby se dalo hlidat maximalni
vyuziti pameti na ruzne caches - zdaleka nejen - kvuli prave mapovanym
souborum.

Potud vsechno dobre.

To nam tak system nabehne, pospousti se na nem stovka VPSek, vsechny
aplikace se krasne rozbehnou, nektere si namapujou soubory, nektere do
nich vesele zapisuji data...

System muze bezet klidne mesice bez problemu, vsechno stiha, v pohode.
Ty seznamy jsou bezne docela kratke, takze sbirka jadernych threadu
"kswapd" je na pozadi pekne stiha prochazet a odklizet, jak se postupne
nektere memory cgroupy dostavaji s pameti do uzkych.

Koneckoncu, 4 GB RAM (na jeden kontejner) prelozeno na 4 kB stranky
znamena teoretickou maximalni delku jednoho seznamu 1M polozek. To se na
2+ gigahertzovych CPU preci stihne projit rychle, ze.

No a pak se stane, ze po treba dvou mesicich behu systemu najednou
zoufaly clen pise, ze mu v kontejneru dochazi pamet, pritom at pocita,
jak pocita, nemuze se dopocitat, ze by to zabiraly aplikace - je videt,
ze je to tim, ze caches nechteji odcouvavat.

Hm, docela spatenka, jak to mame opravit, kdyz to trva tak dlouho, nez
se problem projevi? :-D

Tady nekde bych mel podotknout, ze abych byl schopny to takhle pekne
vysvetlit, musel jsem doprojit celou cestu do vyresena, takze ted se to
jevi zpetne jako trivka, ale nez jsem prisel na to, z ktere strany ten
problem pujde aspon nejak resit...

Kdyz se clovek na takovy trpici system prihlasi, vidi tam zpravidla
kswapd0 na 100% a kdyz ma ta masina dva fyzicke CPU, tak tam vidi
vetsinou i kswapd1 v tom samem stavu.

V dmesgu jsou videt out of memory hlasky z jednotlivych kontejneru, jak
narazeji na neodkliditelne caches a jadro zoufale zabiji stare procesy,
aby udelalo misto pro dalsi.

V tech OOM hlaskach je videt pokazde i stack trace, odkud ta OOM udalost
z jadra prisla - vetsina z nich byla vyvolana kvuli cteni do mmaped
souboru, coz se pozna tak, ze v tom stacku jsou videt funkce pridavajici
LRU stranky na seznam te memory cgroupe.

Tak si rikam, hm, to ma preci snadne reseni, nebudeme uctovat mapovanou
pamet do memory cgroup clenu, ale nechame ji v root memory cgroup...

Okay, to by mohlo fungovat, ze?

..a zase, kswapd0/1 na 100%...

To uz jsem se zacal seriozneji zajimat, co se to vlastne deje, co delaji
tak dlouho a jak to cele funguje, kdyz to neslo smaznout "izy hackem".

Napad to byl dobry, fungoval by, nebyt mensi drobnosti:

kswapd, kdyz odklizeji caches na pozadi, prohledavaji memory cgroupy
stylem "dej mi takovou, ktera zere nejvic a tu odklidime, kdyz to nebude
stacit, pujdem na dalsi".

Tj. pokud se objevi jedna cgroup, ktera je vetsi a ma toho vzdycky vic k
odklizeni, muze se vzdycky kswapd zahojit na ni a k dalsim se ani
nedostat.

Jedine, kdy se odklizi pamet uplne primo z te memory cgroupy, je tzv.
"direct reclaim", cesta kodu primo v momente, kdy je potreba alokovat -
ale v tu chvili neni tolik casu na uklizeni, tak se jadro zas tak
nesnazi a nekdy to muze vzdat predcasne a rict, ze pamet nenaslo a
vyvola OOM situaci v postizene memory cgroupe.

Hmm... okay, takhle by to neslo, tak zkusme mmaped pamet neuctovat
cgroupam vubec a nechme ji v zakladnich systemovych seznamech...

A po trochu zapaseni, bo se v jadre s memory cgroup nepocita, ze by
nahodou mmaped pamet nebyla uctovana zadne memory cgroupe, je vyreseno,
odchod na parek!

...do chvile, nez tim posleme celou masinu out-of-memory a OOM chyby
zacnou prichazet odkudkoliv, ne jen z mmaped readu odzpod z
mem-cgroup...

Totiz kdyz byly mmaped soubory uctovany na jeden seznam, ktery neni v
memory cgroupe, myslelo si jadro, ze ma hodne volnou ruku v tom, co si
muze dovolit nechat nacachovane - ale v tom je potom mensi caveat se
ZFS... postupny nahodny random access pattern k datum mmaped souboru
nadela z ARC slab caches fragmentovane reseto, jeste kdyz se drzi ty
kousky z tech puvodne nactenych dat pri zivote "pripinovanim" na jeden
velky seznam, ktery nema duvod couvat, protoze host ma preci vsechnu
pamet k dispozici bez limitu :D

No pak a chudaci kswapd, kdyz si s tim bordelem maji nejak poradit a
odklidit to, *obzvlast* kdyz jsou jen dva a kdyz pod nima mame (konecne
spravne nastavene se spravnym ashiftem) NVMe pole... na te staging node
(nyni node1.stg) se tak darilo zaplnit RAM az skoro do mrtva.

Takze co s tim? :)

Snadna reseni dosla, bude potreba odklizet ty seznamy
per-limitovana-memory-cgroup.

Na nekolik iteraci jsem nakonec dospel k patchi, ktery spusti
per-NUMA-node "ksoftlimd" thready, pro kazdou memory cgroupu, ktera ma
nastaveny soft_limit.

Ksoftlimd pak dela presne toto - prochazi seznamy svoji memory cgroupy a
drzi si je okolo soft_limitu.

Kswapd maji o praci s memory cgroupama min, pokud je jadro nastavene v
rezimu, ze ma ksoftlimd poustet automaticky (da se tez spoustet jen
rucne).

My jsme zatim defaultne zvolili soft_limit jako watermark, nad ktery se
ma ksoftlimd snazit vic odklizet, nastavujeme ho na 80% pameti
kontejneru - ale do budoucna mozna tohle jeste predelam na nejakou vetsi
automatiku, podle toho, jak kde se ukazou pripadne nedostatky.

Tedy, vysledna situace je, ze pokud aplikace zerou min, jak 80% pameti,
ale je co drzet v RAM jako cache, bude mit kontejner vyuzito okolo tech
80% - bude to videt normalne jako aplikacni pamet a zbytek jako caches.
Uz by se nemelo stat, ze vyuziti stoupne az ke 100% kvuli caches a ze
dojde k OOM a zabijeni procesu.

Zaverem bych jeste zminil ty patche:

Pokus mmaped soubory nauctovat root mem cgroupe:

https://github.com/vpsfreecz/linux/commit/d42232f89795

Pokus mmaped soubory mem cgroupam neuctovat vubec (popis commitu je blbe
a celkove je nedocisteny, nebyl jsem s tim spokojeny a nechtel jsem tim
travit vic casu, radsi jsem koumal, co dal, at the time...) ->

https://github.com/vpsfreecz/linux/commit/c10ae4a7ef95

A finalne, aktualne nasazena verze ksoftlimd patche:

https://github.com/vpsfreecz/linux/commit/e04b3f9cda1d

A uplne-uplne zaverem: linux kernel neni advanced black magic. Je to jen
strasne velka a nekdy dost neforemna kupa C kodu, ktery potrebuje
schopne a ochotne instalatery.

V koncinach memory cgroup + memory managementu je teda hodne, co
zlepsovat, a vubec to neni raketova veda... Teda obecne, na
kontejnerizace v Linuxu je dost co resit.

Takze kdybyste s tim jadernym vyvojem nekdo chtel pomoct, stavte se na
IRC, nebo v Base48 v Brne pokecat, neco vymyslime, bude to zabava, trust
me ;)

/snajpa
_______________________________________________
Community-list mailing list
Community-list@lists.vpsfree.cz
http://lists.vpsfree.cz/listinfo/community-list
_______________________________________________
Community-list mailing list
Community-list@lists.vpsfree.cz
http://lists.vpsfree.cz/listinfo/community-list