Seznámení s databází
Obrázková databáze Seznamu obsahuje 900 milionů obrázků, které se vydávají na doméně obrazky.cz
. Zobrazují se jako favicony v Skliku nebo jako náhledové obrázky článků ve výsledcích Vyhledání na Seznam.cz. Obrázky se v databázi drží v trojnásobné replikaci a v takovém režimu jde celkově o 90 TB dat. Každý týden se zhruba 5 % databáze obnoví. Ve špičce se obsluhuje až 3000 požadavků za sekundu a za týden se na web vydá 800 milionů obrázků.
Pod dohledem adminů byla databáze implementována technologií TiDB,což je distribuovaná SQL databáze, ale dá se použít i jako pouhá key-value storage. Běžela z části přímo na virtuálních serverech Openstacku a z části v Kubernetes clusteru (dále jen k8s), a to ve všech třech datových centrech: Kokura, Ósaka a Nagano. Protože není lepšího způsobu, jak nějakou službu poznat, než si ji postavit od začátku, rozhodli se vývojáři vybudovat nový TiDB cluster čistě v k8s.
Všechno v clusterech
Obrátili se s požadavkem na vytvoření nového CaaSu (Cluster as a Service) na tým infrastruktury – SCIF (Seznam Computing Infrastructure). Výsledkem byl čistý k8s cluster pouze s control-plane nody a jinými elementárními věcmi pro zdárný chod clusteru, jako třeba DNS a síťová konektivita do zbytku Seznamu. Do správy clusteru měl z vývoje přístup pouze tým Obrázků.
Technicky vzato se jednalo o osm nezávislých clusterů. Jeden pro každé datové centrum a jejich spojením později vzniklo cross-cluster prostředí. Byli celkem tři: master, staging a production. Tři vývojářská prostředí se velice osvědčila především na začátku. Nikdo z vývojářů neměl větší znalost k8s, protože doteď všechny jejich aplikace běžely ve sdíleném clusteru, o který se staral SCIF a v podstatě si vystačili pouze s commitem do GitLabu a CI pipeline se postarala o všechno další.
Nejprve bylo potřeba v clusteru rozběhnout worker nody, na které k8s umisťuje pody s našimi aplikacemi. Ty se vytváří pomocí Kubermatic/machine-controller, který byl rovněž nainstalován v clusteru od SCIFu. Worker nody se v tomto případě spouštějí jako virtuální servery v Openstacku. Na vývojářích bylo definovat, jaké parametry měl server mít. Zvolili více kusů menších serverů s jedním extra diskem, který budou mountovat výhradně pro TiDB databázi. Na každém serveru tak poběží právě jeden TiDB peer. Když vám machine-controller vytvoří node s extra diskem, nestará se o to, v jakém je stavu, ani jaká data na něm najdete.
Disk je potřeba vhodně naformátovat a přimountovat tam, kam potřebujete. Aby to nemuseli dělat ručně pro každý node, vzali si na pomoc Ansible. Ten dokonce nabízí specifické playbooky pro tyto účely. Definovali sadu nodů pro master, staging a produkci. Ansible nad takovou sadou umí hromadně pracovat. Dokáže zjistit, v jakém stavu se nody nacházejí a upraví je na požadovaný stav podle konkrétního playbooku. Playbook, který dokáže zformátovat disky v celé sadě produkčních nodů, je docela nebezpečná věc. S takovou mírou odpovědnosti se vývojáři dosud nesetkali. Obezřetnost a kontrolní mechanismy byly zcela namístě.
Následovalo vytvoření namespace a service accountů tak, aby byl k8s schopen stahovat dockerové image z repozitáře. Z bezpečnostních a výkonových důvodů používají interní repozitář, kde jsou po kontrole umístěny i veřejně dostupné image z upstreamu.
Když měli připravené nody, bylo třeba nějak zpřístupnit jejich disky aplikacím v k8s. K tomu si vzali Local Path Provisioner od Rancheru. Jedná se o technologii, která umožňuje uživatelům Kubernetes využívat místní disk v každém nodu. Na základě konfigurace vytvoří Local Path Provisioner v nodu automaticky buď hostPath, a nebo místní perzistentní svazek. Využívá zavedené funkce k8s Local Persistent Volume, ale představuje jednodušší řešení než vestavěná funkce lokálního svazku v systému Kubernetes.
TiDB cluster bude pro veškerou komunikaci používat TLS. Za tím účelem museli zajistit vydávání certifikátů, jejich distribuci jednotlivým komponentám TiDB clusteru a obnovu v době před expirací. Nejjednodušší cestou bylo zavedení Cert Managera, který toto vše v prostředí k8s elegantně automatizuje.
TiDB cluster bude využíván v režimu cross-cluster a vznikne spojením clusterů všech tří data center. V každé lokalitě je uložená právě jedna replika dat. Placement Driver (PD) má přehled o tom, kde se požadovaná data nachazejí a také v jaké lokalitě se nachází tzv. leader. Ten obsluhuje jakoukoliv změnu dat, která je následně propagována na všechny repliky. Leader se v TiDB volí, a pokud dojde k výpadku lokality s leaderem, dokáže si zbytek clusteru zvolit leadera nového. Výpadek jedné lokality tedy neovlivní dostupnost služby jako celku.
Z bezpečnostních důvodu je síťová komunikace mezi k8s clustery ve výchozím stavu zakázaná. Tu museli povolit definováním bezpečnostních pravidel v Openstacku. V zásadě se jednalo o povolení příchozí i odchozí komunikace na IP rozsazích použitých v clusterech, které chtěli spojit.
Instalace TiDB clusteru
Teď už bylo vše připraveno pro zdárnou instalaci TiDB clusteru. Použili TiDB operátor, který automatizuje nasazení a správu celého TiDB clusteru. Na týmu vývojářů bylo definování placement rules a nastavení parametrů tak, aby cluster optimálně fungoval v požadovaných podmínkách.
Placement rules stanovují, kde se jaká data mohou v clusteru vyskytovat. Jak už bylo zmíněno: v každé lokalitě právě jedna replika. V každém datovém centru dále existují tři availability zóny (AZ), přičemž každý worker node v k8s cluteru spadal právě do jedné z nich. Redundance se v TiDB clusteru týká nejen dat, ale i Placement Driverů. Do každé AZ nasadili jeden driver. K nedostupnosti AZ obvykle dochází v rámci servisních zásahů při údržbě infrastruktury v datových centrech. Je proto vhodné mít aplikace rovnoměrně rozložené přes všechny AZ a počítat s odstávkou.
Nastavení clusteru a optimalizace
Optimální nastavení clusteru byl běh na dlouhou trať. Při adopci open-source technologie je typickým problémem zvládnutí našeho rozsahu zátěže. Svobodné technologie jsou sice podrobovány zátěžovým testům ze strany autorů, ale jako takové bývají typicky prováděny v ideálních podmínkách a v izolovaném prostředí.
V Seznamu bývá nejlepším testem replikování skutečného špičkového provozu. To se dnes už dělá snadno přidáním pravidel na load balanceru kdy řekneme, že kromě klasického odbavení se má požadavek od uživatele ještě odeslat jinam, třeba na nový cluster. Odpověď na takový požadavek je potom zahozena. Tímto způsobem podstoupí nový cluster reálnou zátěž s reálnými požadavky od uživatelů.
Zdárná optimalizace vyžadovala pochopení technologie TiDB a všech jejích komponent do hloubky. TiDB je dnes už relativně komplikovaná záležitost a ladění probíhalo v několika iteracích.
Kromě zvládnutí typické zátěže bylo potřeba otestovat i zmíněný výpadek data centra. Ty sice v Seznamu provádíme zhruba dvakrát za měsíc, ale když testujete novou technologii, potřebujete takový výpadek nasimulovat častěji. Ideálně několikrát denně. Vzhledem k tomu, že máme přístup na worker nody v Openstacku a síťová komunikace probíhá právě skrz ně, tak je možné pomocí IPtables odstavit IP rozsahy konkrétní lokality, nebo přímo nějaké služby a efektivně tak nasimulovat nedostupnost.
Optimalizace clusteru se neobejde bez sběru a grafování metrik. Pryč jsou doby, kdy jsme se dívali do chybového výstupu aplikace a četli co dělá. V distribuovaném prostředí se neobejdete bez vizuální zpětné vazby. TiDB v tomto směru přichází dobře připravena a na svých komponentách vystavuje ohromné množství metrik, které popisují chování jak jednotlivých komponent, tak celého clusteru včetně reportování chyb. Data z metrik se ukládají na persistentní disk v každém clusteru a odtud propagují dále do interního systému pro sběr a agregaci metrik zvaný Monixoring (spojení monitoring a NixOS). Jedná se o jednotné místo pro analýzu metrik a implementaci upozornění.
Takzvané alerty nás upozorňují na neobvyklý nebo rovnou nevyhovující stav služby. V rámci převzetí provozní odpovědnosti vývojem je i dohled nad službou 24/7. Upozornění reportují do Opsgenie, kde je definována politika, jak se s má s konkrétním upozorněním naložit a také, kdo má zrovna pohotovost. Upozornění mají různou závažnost a mohou se vyskytnout v různou dobu. Typicky se rozlišují tři doby: běžná pracovní doba, běžný pracovní den a noc, nebo svátek.
V závislosti na závažnosti události a době pak Opsgenie upozorní toho, kdo zrovna drží pohotovost. V rámci běžné pracovní doby se upozorňuje i na události s nízkou závažností. V noci, nebo ve svátek se naopak upozorňuje pouze ty nejzávažnější.
Nečekané výzvy během ladění clusteru
Vývojáři se během práce s clusterem setkali se spoustou záludností. Bojovali například s neexistujicí dokumentací a prošlapávali cestičky.
Například dlouho hledali chybu u HTTPS spojení pro reportování metrik. Spojení se často (ne vždy a náhodně) přerušilo a klient zahlásil obskurní chybu. Nezbylo nic jiného, než jít na worker node, spustit tam tcpdump a prozkoumat, co přesně se tam odehrává. Zjistili, že server klientovi zavíral spojení ve chvíli, když odeslal nový požadavek na již otevřeném spojení. Dohledali, že by se mohlo jednat o takzvaný TLS session reuse, tedy opakované využití relace. Na serveru byl ve výchozím stavu povolený, ale z nějakého důvodu nefungoval. Proto jej explicitně zakázali a od té doby to funguje.
Jindy zase, po přechodu na novější verzi, řešili nefunkčního open source klienta TiDB API. Opět se jednalo o problém s TLS. Vypadalo to, že se jedná opět o session reuse, ale vypnutí tentokrát nezabralo. Ani tcpdum nic neporadil. Server se s klientem vůbec nechtěl bavit a nové spojení mu ihned zavřel. Naštěstí zdrojový kód klienta hodně věcí prozradil. V nové verzi přibyl health check, který implementoval TLS spojení jinou knihovnou a používal odlišný způsob konfigurace. Health check si nenabral TLS certifikát a server se s ním zcela oprávněně odmítl bavit. Samozřejmě to nebylo nikde dokumentované. Protože to celé bylo po HTTP/2, které nutně nemusí být šifrované, tak ani klient chybějící certifikát nereportoval.
Protože je v prostředí cross-cluster nutné překládat adresy i z jiného clusteru, musel vzniknout vlastní systém DNS. V každém clusteru běží lokální DNS, které umí překládat adresy z místního clusteru. Když dostane požadavek na překlad domény z jiného clusteru, předá požadavek právě tam. Pro vývoj to z počátku byla velká neznámá, protože DNS tu pro ně vždycky bylo, fungovalo a staral se o něj někdo jiný. Jelikož se komponenty v TiDB clusteru vzájemně připojují přes adresy a nikoliv přímo přes IP, nefunkční DNS znamenala nefunkční TiDB cluster. Záznamy v DNS sice mají nějaký TTL, ale v prostředí k8s se IP mohou měnit velmi často. Proto se TTL drží nízko – třeba jen pět sekund.
Už na novém
Poté, co cluster nějakou dobu fungoval jak má, byl výdej obrázků přepnut ze starého na nový. Protože už se nějakou dobu replikovaly požadavky na load balanceru, stačilo jen nastavit cluster jako hlavní. Obrázky se začaly vydávat na web z nového clusteru. Ten starý jsme ještě nějakou dobu zachovali jako zálohu, ale dnes už neexistuje.