ZFS má rád pořádek

Volné pokračování našeho seriálu o pomalém ZFS. Poslední díl byl někdy v červnu, kdy jsem vyzkoušel paralelní přístup k souborům a zdá se, že to pomohlo (pro specifické věci). Od té doby jsem udělal nějaké další změny a potom se věnoval něčemu jinému a nechal jsem to tak. Teď, po půl roce, jsem si opět všiml nápadného zpomalení ZFS.

Předem chci říct, že tento díl nebude obsahovat žádná pevná a měřitelná data, žádné „opakovatelné” testy. Bude to jen takové povídání, co všechno jsem zkusil a co podle mě nejvíce pomohlo.

Už v prvním dílu se řešilo množství souborů. Osobně si myslím, že moderní FS by neměl mít žádný limit (určitě ne tvrdý) na počet souborů a měl by být schopen pojmout mnohem více souborů, než se vůbec vejde na libovolně velký reálně dostupný disk / pole. Máme k tomu log(n) algoritmy, máme b-stromy, které mohou mít velikost listu třeba 1024 položek (nebo i víc), což znamená, že na tři patra („seeky”) je možné adresovat miliardu objektů. O pouhé jedno patro navíc a jsme na biliónu. Není tedy žádný problém pomocí velmi málo vyhledávacích operacích adresovat tolik objektů, kolik se vejde na reálný storage.

Pokud si vezmeme tři patra b-stromu o velikosti listů 1024 položek, tak průměrně v každém uzlu musíme prohledat 512 položek. Což je nic, vejde se to do L2 cache a je to za pár us hotové. Na tři patra potřebujeme 3 seeky (pokud není strom celý nebo částečně v paměti), tedy 30ms. Toto je nejhorší čas na vyhledání libovolné položky z miliardy, přičemž, pokud se to alespoň částečně nacachuje do paměti, může to být 10ms. (První dvě patra, milion položek – i kdyby každá měla 1024B, tak je to jen 1GB – máme v RAM a výlet na disk je potřeba jen pro ten třetí.)

Proto jsem příliš nepředpokládal, že problém bude v počtu souborů. Ale pochopitelně mám své zkušenosti a proto raději pro větší počet (kde větší znamená stovky milionů) objektů používám DB. Hned po zveřejnení minulého dílu jsem tedy začal uklízet.

Tlusté jaily už nejsou tak tlusté. Jsem zastáncem myšlenky, že virtualizace není ojebávka. Virtuální stroj nemá být mrzákem, nedokonalou napodobeninou běhu přímo na HW. Vždy jsem vytvářel plné virtuální stroje, které až na detaily, jsou k nerozeznání od běhu na HW (pochopitelně, ve virtuálce neřeším hw, diskové pole apod. to je asi jasné). Takže všechny virtuálky (plně hw akcelerované – vmware, kvm) vytvářím a chovám se k nim úplně stejně, jako kdyby to byl HW na síti. Prostě je to stroj na síti a je úplně jedno, jakého je typu.

Stejnou filozofii jsem zachoval i pro kontejnery. Platí to pro nspawn, platí to pro jaily. Jasně, u kontejnerů je o něco složitější tvářit se, že to je stejné jako na HW, přece jen kontejner je jen jmenný prostor v jádře, jádro je sdíleno, kontejner nemá vlastní fs, ale žije na fs podkladového stroje apod. Ale co se týče user space služeb, souborů na disku, sítě (vždy kontejnery provozuju s vlastní IP a do sítě přistupuje přes bridge, nikoliv přes port forward / tunel na hostiteli, což považuji za prasárnu), chovám se k tomu zkrátka úplně stejně, jako k OS přímo na HW nebo v plné virtualizaci. Takový kontejner je možné kdykoliv vzít a umístit na libovolný jiný stroj ve stejné síti, IP má svoji, vše běží.

Z tohoto důvodu nepoužívám ezJail ani nic podobného. Všude na netu existují tutoriály, jak nasdílet base system, sdílet template apod. Tohle nedělám. Ano, mám výchozí subvolume / dataset pro výchozí nspawn / jail kontejner pro moje použití, je to v podstatě minimální instalace daného os (debian / freebsd) + základní programy, které používám (vim, mc, wget, apod.) a které by se stejně do kontejneru velmi rychle dostaly.

Co je podstatné a tím se opět dostávám k tématu tohoto článku, v jailu nechávám i kompletní port tree. Každý jail má svůj. Jenže to je spousta souborů. Port tree má cca 400tis souborů, na 20 kontejnerů je to 8mil. souborů. Minimálně jednou za den se dělá zfs snapshot, snapshotů tam je za rok 365 * 20 (+ další pro každý projekt). Že by právě tohle ZFS vadilo? Ale nikde se žádné omezení na počet nepíše, na rozdíl třeba od UFS, kde je v manuálu jasně napsáno omezení na 20 snapshotů.

Takže jsem začal uklízet. Nejdřív takový ten zřejmý nepořádek, dávno zpracované projekty patří do archivu apod. Potom se dostalo i na port tree v jailech i když se mi do toho nechtělo. Nakonec jsem jej umístil na samostatné ssd a do jailů jen mountoval (mount "/usr/ports-svn /tank/jails/$name/usr/ports/ nullfs rw 0 0";). Tím jsem jen v jailech uklidil těch 8 mil. souborů, dalších cca 6 mil. se na zpoolu našlo jinde. Některé read only adresáře jsem zabalil do squashfs a jen je mountuju. Z hlediska userspace žádná změna, ale na fs ubylo dalších cca 500tis. souborů. Celkově jsem uklidil asi tak 15 mil. souborů. Podle mě je to jenom číslo, každý algoritmus by si s tím měl hravě poradit.

Někde mezi tím jsem uklidil i snapshoty. Už od prvních dnů s BTRFS jsem zvyklý dělat snapshoty minimálně jednou denně a moc je nepromazávat až jedině pokud dochází místo a i to raději vyřeším dalším diskem (v BTRFS raid 1 režimu je to hračka). Na ZFS jsem měl cca 25 tis. snapshotů. Už předtím mě štvalo to, jak dlouho trvá výpis zfs list -t snap, takže i toto se tím vyřešilo. (Ale zase, proč by měl výpis 20tis položek trvat minuty?)

To bylo tak někdy v srpnu (co taky ve 40°C dělat jiného, než promazávat důležitá data, že ;-)). Pokud to tupě vynásobím, tak FS přišel o 20 tis. * 400 tis. = 8.000.000.000 „objektů“, ale reálně to není pravda, protože snapshot nekopíruje odkaz na každý soubor, ale (měl by) zkopírovat podstrom patřící k dané subvolume / datasetu.

Každopádně, toto skutečně pomohlo. Pochopitelně jsem všechno dělal současně, jak úklid souborů, tak úklid snapshotů, takže nedokážu říct, co přesně z toho pomohlo. (Toto je takový 101 politické práce, dělejte všechno současně a hlavně ještě paralelně s opozicí, aby nebylo jasné, co přesně pomohlo a co uškodilo a navíc ty chyby můžete hodit na opozici, která klidně může být vaším koaličním partnerem.)

Ale vážně. Nechal jsem to být a věnoval se něčemu jinému. Soubory byly uklizeny, port tree je jinde, snapshoty jsem promazal. A před pár dny, jsem si opět všiml podezřelého zaváhání při listingu souborů v projektech. To, co se po úklidu uklidnilo na nějakých snesitelných 10s z původních tří minut je opět někde na dvou minutách. A vypadá to, že se překročil nějaký práh, protože to opravdu nenarůstalo lineárně. Takže jsem opět uklidil 4 měsíce snapshotů a ono to pomohlo.

Je zcela evidentní že ZFS není stavěno na velký počet objektů. Snapshoty jsou super funkce, ale bohužel nejsou lighweight tak jako na BTRFS. Snapshoty jsou závislé, nelze smazat zdrojový dataset apod. Je to prostě jiný FS. Toto není kritika, ale poučení. K ZFS je potřeba se chovat jinak, mít snapshoty jen pro danou funkci a neskladovat je do nekonečna. Takže aktuálně vždy po týdnu udělám zfs send, uložím je na externí úložiště a z poolu je smažu. Vzhledem k tomu, že se to stalo po cca 4 měsících, tak limit ZFS určitě není na hodnotě 7, možná někde kolem 60-90 (na mém stroji) vynásobené počtem projektů. Možná tvůrci tiše předpokládali, že když se všude jinde snapshoty používají ke konkrétnímu účelu (konzistentní záloha) a počty jsou tak někde v jednotkách (ono i takové starší LVM i s jedním snapshotem jde s výkonem hodně dolů a hodí opravdu jen na tu chvíli pro zálohu a potom hned smazat), tak nikdo nebude takový magor a měl jich tam desítky tisíc.

Příspěvek byl publikován v rubrice FreeBSD, Systémy souborů, ZFS. Můžete si uložit jeho odkaz mezi své oblíbené záložky.