Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions

Dobrý den, Habr! Jsem Artem Karamyshev, vedoucí týmu správy systému Cloudová řešení Mail.Ru (MCS). Za poslední rok jsme uvedli na trh mnoho nových produktů. Chtěli jsme zajistit, aby služby API byly snadno škálovatelné, odolné proti chybám a připravené na rychlý růst uživatelské zátěže. Naše platforma je implementována na OpenStack a chci vám říci, jaké problémy s odolností proti chybám součástí jsme museli vyřešit, abychom získali systém odolný proti chybám. Myslím, že to bude zajímavé pro ty, kteří také vyvíjejí produkty na OpenStacku.

Celková odolnost platformy vůči chybám spočívá v odolnosti jejích komponent. Postupně tedy projdeme všechny úrovně, kde jsme identifikovali rizika a uzavřeli je.

Videoverze tohoto příběhu, jehož primárním zdrojem byla zpráva na konferenci Uptime day 4, pořádané společností ITSumma, můžeš vidět na YouTube kanálu Uptime Community.

Odolnost fyzické architektury

Veřejná část cloudu MCS nyní sídlí ve dvou datových centrech Tier III, mezi nimi je vlastní temné vlákno, rezervované na fyzické úrovni různými trasami, s propustností 200 Gbit/s. Úroveň III poskytuje potřebnou úroveň odolnosti proti chybám pro fyzickou infrastrukturu.

Tmavé vlákno je vyhrazeno na fyzické i logické úrovni. Proces rezervace kanálů byl opakovaný, vyskytly se problémy a neustále zlepšujeme komunikaci mezi datovými centry.

Například nedávno při práci ve studni poblíž jednoho z datových center bagr přetrhl potrubí a uvnitř tohoto potrubí byl hlavní i záložní optický kabel. Náš komunikační kanál odolný proti chybám s datovým centrem se ukázal být zranitelný v jednom bodě, ve studni. V důsledku toho jsme ztratili část infrastruktury. Vyvodili jsme závěry a provedli řadu akcí, včetně instalace další optiky do přilehlé studny.

V datových centrech jsou místa přítomnosti poskytovatelů komunikace, kterým vysíláme naše prefixy prostřednictvím BGP. Pro každý směr sítě je vybrána nejlepší metrika, která umožňuje různým klientům poskytovat nejlepší kvalitu připojení. Pokud komunikace přes jednoho poskytovatele selže, přebudujeme naše směrování přes dostupné poskytovatele.

Pokud některý poskytovatel selže, automaticky přecházíme na dalšího. V případě výpadku jednoho z datových center máme zrcadlovou kopii našich služeb ve druhém datovém centru, které přebírá veškerou zátěž.

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions
Odolnost fyzické infrastruktury

Co používáme pro odolnost proti chybám na úrovni aplikace

Naše služba je postavena na řadě opensource komponent.

ExaBGP je služba, která implementuje řadu funkcí pomocí dynamického směrovacího protokolu založeného na BGP. Aktivně jej využíváme k inzerci našich IP adres na seznamu povolených, přes které uživatelé přistupují k API.

HAProxy je high-load balancer, který umožňuje konfigurovat velmi flexibilní pravidla pro vyvažování provozu na různých úrovních modelu OSI. Využíváme jej k bilancování před všemi službami: databáze, zprostředkovatelé zpráv, API služby, webové služby, naše interní projekty – vše je za HAProxy.

API aplikace — webová aplikace napsaná v pythonu, pomocí které uživatel spravuje svou infrastrukturu a své služby.

Pracovní aplikace (dále jen pracovník) - ve službách OpenStack se jedná o infrastrukturního démona, který umožňuje vysílat API příkazy do infrastruktury. Například k vytvoření disku dojde v pracovním prostředí a požadavek na vytvoření se objeví v rozhraní API aplikace.

Standardní aplikační architektura OpenStack

Většina služeb, které jsou vyvinuty pro OpenStack, se snaží dodržovat jediné paradigma. Služba se obvykle skládá ze 2 částí: API a pracovníků (backendových exekutorů). API je zpravidla aplikace WSGI v pythonu, která se spouští buď jako nezávislý proces (démon), nebo pomocí hotového webového serveru Nginx nebo Apache. Rozhraní API zpracuje požadavek uživatele a předá další pokyny pracovní aplikaci k provedení. K přenosu dochází pomocí zprostředkovatele zpráv, obvykle RabbitMQ, ostatní jsou špatně podporováni. Když zprávy dorazí zprostředkovateli, pracovníci je zpracují a v případě potřeby vrátí odpověď.

Toto paradigma zahrnuje izolované společné body selhání: RabbitMQ a databázi. Ale RabbitMQ je izolovaný v rámci jedné služby a teoreticky může být pro každou službu individuální. V MCS tedy tyto služby maximálně oddělujeme, pro každý jednotlivý projekt vytváříme samostatnou databázi, samostatnou RabbitMQ. Tento přístup je dobrý, protože v případě nehody na některých zranitelných místech se neporouchá celá služba, ale jen její část.

Počet pracovních aplikací je neomezený, takže API lze snadno horizontálně škálovat za balancery, aby se zvýšil výkon a odolnost proti chybám.

Některé služby vyžadují koordinaci v rámci služby, když mezi rozhraními API a pracovníky dochází ke složitým sekvenčním operacím. V tomto případě se používá jediné koordinační centrum, klastrový systém, jako je Redis, Memcache atd., který umožňuje jednomu pracovníkovi říci druhému, že je tento úkol přidělen jemu („prosím, neberte to“). Používáme etcd. Pracovníci zpravidla aktivně komunikují s databází, zapisují a čtou z ní informace. Jako databázi používáme mariadb, který je umístěn v multimaster clusteru.

Tato klasická jediná služba je organizována způsobem obecně akceptovaným pro OpenStack. Lze jej považovat za uzavřený systém, pro který jsou metody škálování a odolnosti proti chybám zcela zřejmé. Například pro odolnost proti chybám API stačí před ně postavit balancer. Škálování pracovníků se dosahuje zvýšením jejich počtu.

Slabým místem v celém schématu je RabbitMQ a MariaDB. Jejich architektura si zaslouží samostatný článek.V tomto článku se chci zaměřit na odolnost proti chybám API.

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions
Aplikační architektura Openstack. Vyvážení a odolnost proti chybám cloudové platformy

Vytvoření HAProxy balanceru odolného vůči chybám pomocí ExaBGP

Aby naše API byla škálovatelná, rychlá a odolná proti chybám, postavili jsme před ně nástroj pro vyrovnávání zatížení. Vybrali jsme HAProxy. Podle mého názoru má všechny potřebné vlastnosti pro náš úkol: vyvažování na několika úrovních OSI, rozhraní pro správu, flexibilitu a škálovatelnost, velké množství metod vyvažování, podporu tabulek relací.

První problém, který bylo potřeba vyřešit, byla odolnost samotného balanceru proti poruchám. Pouhá instalace balanceru také vytváří bod selhání: balancer se rozbije a služba se zhroutí. Abychom tomu zabránili, použili jsme HAProxy ve spojení s ExaBGP.

ExaBGP vám umožňuje implementovat mechanismus pro kontrolu stavu služby. Tímto mechanismem jsme zkontrolovali funkčnost HAProxy a v případě problémů službu HAProxy z BGP zakázali.

Schéma ExaBGP+HAProxy

  1. Potřebný software ExaBGP a HAProxy nainstalujeme na tři servery.
  2. Na každém serveru vytvoříme rozhraní zpětné smyčky.
  3. Na všech třech serverech přidělujeme tomuto rozhraní stejnou bílou IP adresu.
  4. Bílá IP adresa je inzerována na internetu prostřednictvím ExaBGP.

Tolerance chyb je dosaženo inzerováním stejné IP adresy ze všech tří serverů. Z hlediska sítě je stejná adresa přístupná ze tří různých dalších přeskoků. Router vidí tři stejné trasy, vybere z nich nejvyšší prioritu na základě své vlastní metriky (obvykle jde o stejnou možnost) a provoz jde pouze na jeden ze serverů.

V případě problémů s provozem HAProxy nebo výpadku serveru ExaBGP přestane oznamovat trasu a provoz plynule přejde na jiný server.

Tím jsme dosáhli odolnosti vyvažovače vůči chybám.

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions
Odolnost proti poruchám balancérů HAProxy

Schéma se ukázalo jako nedokonalé: naučili jsme se, jak rezervovat HAProxy, ale nenaučili jsme se, jak rozložit zátěž v rámci služeb. Proto jsme toto schéma trochu rozšířili: přešli jsme k balancování mezi několika bílými IP adresami.

Vyvažování založené na DNS plus BGP

Otázka vyrovnávání zátěže pro naše HAProxy zůstává nevyřešena. Dá se to však vyřešit docela jednoduše, jako jsme to udělali zde.

K vyvážení tří serverů budete potřebovat 3 bílé IP adresy a staré dobré DNS. Každá z těchto adres je určena na zpětnovazebním rozhraní každého HAProxy a inzerována na internetu.

V OpenStack se ke správě prostředků používá adresář služeb, který určuje rozhraní API koncového bodu konkrétní služby. V tomto adresáři registrujeme doménové jméno - public.infra.mail.ru, které se pomocí DNS překládá třemi různými IP adresami. Výsledkem je rozložení zátěže mezi tři adresy prostřednictvím DNS.

Ale protože při oznamování bílých IP adres nekontrolujeme priority výběru serveru, není to zatím vyvážené. Obvykle bude vybrán pouze jeden server na základě seniority IP adresy a další dva budou nečinné, protože v BGP nejsou zadány žádné metriky.

Začali jsme posílat trasy přes ExaBGP s různými metrikami. Každý balancer inzeruje všechny tři bílé IP adresy, ale jedna z nich, hlavní pro tento balancer, je inzerována s minimální metrikou. Takže zatímco jsou všechny tři balancery v provozu, volání na první IP adresu jdou na první balancer, volání na druhý na druhý a volání na třetí na třetí.

Co se stane, když jeden z balancérů spadne? Pokud některý balancer selže, jeho hlavní adresa je stále inzerována z ostatních dvou a provoz je přerozdělován mezi ně. Uživateli tak dáváme přes DNS několik IP adres najednou. Vyvažováním pomocí DNS a různých metrik získáme rovnoměrné rozložení zátěže mezi všechny tři balancery. A přitom neztrácíme odolnost vůči chybám.

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions
Vyvažování HAProxy na základě DNS + BGP

Interakce mezi ExaBGP a HAProxy

Implementovali jsme tedy odolnost proti chybám v případě odchodu serveru na základě zastavení oznamování tras. Ale HAProxy se může vypnout z jiných důvodů, než je selhání serveru: chyby administrace, selhání služby. I v těchto případech chceme odstranit poškozený vyvažovač zpod zátěže a potřebujeme jiný mechanismus.

Proto jsme rozšířili předchozí schéma a implementovali jsme srdeční tep mezi ExaBGP a HAProxy. Jedná se o softwarovou implementaci interakce mezi ExaBGP a HAProxy, kdy ExaBGP používá vlastní skripty ke kontrole stavu aplikací.

Chcete-li to provést, musíte v konfiguraci ExaBGP nakonfigurovat kontrolu stavu, která může kontrolovat stav HAProxy. V našem případě jsme backend zdraví nakonfigurovali v HAProxy a ze strany ExaBGP kontrolujeme jednoduchým požadavkem GET. Pokud hlášení přestane probíhat, pak HAProxy s největší pravděpodobností nefunguje a není třeba jej inzerovat.

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions
Kontrola stavu HAProxy

HAProxy Peers: synchronizace relace

Další věcí, kterou bylo třeba udělat, bylo synchronizovat relace. Při práci s distribuovanými balancéry je obtížné organizovat ukládání informací o klientských relacích. Ale HAProxy je jedním z mála balancérů, které to umí díky funkcionalitě Peers – schopnosti přenášet tabulky relací mezi různými procesy HAProxy.

Existují různé způsoby vyvažování: jednoduché jako např kulatý robina prodloužena, když se klientova relace zapamatuje a pokaždé, když skončí na stejném serveru jako předtím. Chtěli jsme implementovat druhou možnost.

HAProxy používá stick-tables k uložení klientských relací tohoto mechanismu. Ukládají původní IP adresu klienta, vybranou cílovou adresu (backend) a některé informace o službě. Obvykle se k uložení páru zdrojová-IP + cíl-IP používají tabulky stick, což je užitečné zejména pro aplikace, které nemohou přenést kontext uživatelské relace při přepnutí na jiný balancer, například v režimu vyvažování RoundRobin.

Pokud se tyčový stůl naučí pohybovat se mezi různými procesy HAProxy (mezi kterými dochází k vyvažování), naše balancery budou schopny pracovat s jedním fondem tyčových stolů. To umožní bezproblémové přepnutí klientské sítě, pokud jeden z balancerů selže; práce s klientskými relacemi bude pokračovat na stejných backendech, které byly vybrány dříve.

Pro správnou funkci musí být vyřešen problém se zdrojovou IP adresou balanceru, ze kterého byla relace navázána. V našem případě se jedná o dynamickou adresu na rozhraní zpětné smyčky.

Správné práce vrstevníků je dosaženo pouze za určitých podmínek. To znamená, že časové limity TCP musí být dostatečně velké nebo přepínání musí být dostatečně rychlé, aby relace TCP nestihla ukončit. Umožňuje však bezproblémové přepínání.

V IaaS máme službu postavenou pomocí stejné technologie. Tento Load Balancer jako služba pro OpenStack, která se jmenuje Octavia. Je založen na dvou procesech HAProxy a zpočátku zahrnuje podporu pro peery. V této službě se výborně osvědčili.

Obrázek schematicky ukazuje pohyb peer tabulek mezi třemi instancemi HAProxy, je navržena konfigurace, jak to lze nakonfigurovat:

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions
HAProxy Peers (synchronizace relace)

Pokud implementujete stejné schéma, musí být jeho provoz pečlivě otestován. Není pravda, že to bude 100% času fungovat stejným způsobem. Ale alespoň nepřijdete o tabulky s klíči, když si potřebujete zapamatovat zdrojovou IP klienta.

Omezení počtu současných požadavků od stejného klienta

Jakékoli služby, které jsou veřejně dostupné, včetně našich API, mohou podléhat lavině požadavků. Důvody pro ně mohou být úplně jiné, od chyb uživatelů až po cílené útoky. Jsme pravidelně DDoSed podle IP adres. Klienti často dělají chyby ve svých skriptech a dávají nám mini-DDoS.

Tak či onak je třeba zajistit dodatečnou ochranu. Samozřejmým řešením je omezit počet požadavků API a neztrácet čas CPU zpracováním škodlivých požadavků.

K implementaci takových omezení používáme rychlostní limity organizované na základě HAProxy pomocí stejných tabulek. Nastavení limitů je poměrně jednoduché a umožňuje omezit uživatele počtem požadavků na API. Algoritmus si pamatuje zdrojovou IP, ze které jsou požadavky odesílány, a omezuje počet současných požadavků od jednoho uživatele. Samozřejmě jsme vypočítali průměrný profil zatížení API pro každou službu a nastavili jsme limit ≈ 10násobek této hodnoty. Situaci nadále bedlivě sledujeme a držíme palce.

Jak to vypadá v praxi? Máme zákazníky, kteří naše rozhraní API pro automatické škálování používají neustále. Ráno vytvoří přibližně dvě až tři sta virtuálních strojů a večer je smažou. Pro OpenStack vyžaduje vytvoření virtuálního stroje, také se službami PaaS, alespoň 1000 požadavků API, protože k interakci mezi službami dochází také prostřednictvím API.

Takový přenos úkolů způsobuje poměrně velkou zátěž. Vyhodnotili jsme tuto zátěž, shromáždili denní špičky, navýšili je desetinásobně, a to se stalo naším limitem. Držíme prst na tepu. Často vidíme roboty a skenery, kteří se na nás snaží podívat, zda máme nějaké CGA skripty, které lze spustit, aktivně je řežeme.

Jak aktualizovat kódovou základnu, aniž by si toho uživatelé všimli

Implementujeme také odolnost proti chybám na úrovni procesů nasazení kódu. Během zavádění může docházet k závadám, ale jejich dopad na dostupnost služby lze minimalizovat.

Neustále aktualizujeme naše služby a musíme zajistit, aby byla kódová základna aktualizována, aniž by to ovlivnilo uživatele. Tento problém se nám podařilo vyřešit pomocí manažerských schopností HAProxy a implementací Graceful Shutdown v našich službách.

K vyřešení tohoto problému bylo nutné zajistit kontrolu balanceru a „správné“ vypnutí služeb:

  • V případě HAProxy se řízení provádí prostřednictvím souboru statistik, což je v podstatě soket a je definován v konfiguraci HAProxy. Můžete mu posílat příkazy přes stdio. Ale náš hlavní konfigurační kontrolní nástroj je k dispozici, takže má vestavěný modul pro správu HAProxy. Které aktivně využíváme.
  • Většina našich služeb API a Engine podporuje technologie elegantního vypínání: při vypínání čekají na dokončení aktuální úlohy, ať už jde o požadavek http nebo nějakou servisní úlohu. Totéž se děje s pracovníkem. Zná všechny úkoly, které dělá, a končí, když vše úspěšně dokončí.

Díky těmto dvěma bodům vypadá bezpečný algoritmus pro naše nasazení takto.

  1. Vývojář sestaví nový balíček kódu (pro nás je to RPM), otestuje jej ve vývojovém prostředí, otestuje jej ve fázi a ponechá v úložišti fáze.
  2. Vývojář nastaví úkol pro nasazení s co nejpodrobnějším popisem „artefaktů“: verze nového balíčku, popis nové funkčnosti a případně další podrobnosti o nasazení.
  3. Správce systému zahájí aktualizaci. Spustí knihu Ansible, která zase dělá následující:
    • Vezme balíček z úložiště fáze a použije jej k aktualizaci verze balíčku v úložišti produktu.
    • Sestaví seznam backendů aktualizované služby.
    • Vypne první službu, která má být aktualizována v HAProxy, a čeká, až její procesy skončí. Díky ladnému odstavení jsme si jisti, že všechny aktuální požadavky klientů budou úspěšně dokončeny.
    • Po úplném zastavení API a pracovníků a vypnutí HAProxy se kód aktualizuje.
    • Ansible provozuje služby.
    • Pro každou službu jsou vytaženy určité „držadla“, které provádějí testování jednotek na řadě předdefinovaných klíčových testů. Proběhne základní kontrola nového kódu.
    • Pokud v předchozím kroku nebyly nalezeny žádné chyby, aktivuje se backend.
    • Pojďme k dalšímu backendu.
  4. Po aktualizaci všech backendů se spustí funkční testy. Pokud chybí, vývojář se podívá na jakoukoli novou funkci, kterou vytvořil.

Tím je nasazení dokončeno.

Jak je implementována webová architektura odolná proti chybám v platformě Mail.ru Cloud Solutions
Cyklus aktualizace služby

Toto schéma by nefungovalo, kdybychom neměli jedno pravidlo. V bitvě podporujeme starou i novou verzi. Předem ve fázi vývoje softwaru je stanoveno, že i když dojde ke změnám v databázi služeb, neporuší předchozí kód. V důsledku toho se základna kódu postupně aktualizuje.

Závěr

Sdílím své vlastní myšlenky o WEB architektuře odolné proti chybám a rád bych ještě jednou poznamenal její klíčové body:

  • fyzická odolnost proti chybám;
  • odolnost proti chybám sítě (balancery, BGP);
  • odolnost proti chybám používaného a vyvinutého softwaru.

Stabilní doba provozu pro každého!

Zdroj: www.habr.com

Přidat komentář