Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

V Mail.ru Group máme Tarantool - to je aplikační server v Lua, který také funguje jako databáze (nebo naopak?). Je to rychlé a cool, ale možnosti jednoho serveru stále nejsou neomezené. Vertikální škálování také není všelék, takže Tarantool má nástroje pro horizontální škálování - modul vshard [1]. Umožňuje vám sdílet data na několika serverech, ale musíte si s nimi pohrát, abyste je nastavili a připojili obchodní logiku.

Dobrá zpráva: shromáždili jsme několik velkých záběrů (např [2], [3]) a vytvořil další framework, který výrazně zjednoduší řešení tohoto problému.

Tarantolová kazeta je nový framework pro vývoj komplexních distribuovaných systémů. Umožňuje vám soustředit se na psaní obchodní logiky namísto řešení problémů s infrastrukturou. Pod řezem vám řeknu, jak tento framework funguje a jak pomocí něj psát distribuované služby.

A v čem přesně je problém?

Máme tarantuli, máme vshard - co víc si přát?

Za prvé je to otázka pohodlí. Konfigurace vshard se konfiguruje pomocí tabulek Lua. Aby distribuovaný systém více procesů Tarantool fungoval správně, musí být konfigurace všude stejná. Nikdo to nechce dělat ručně. Proto se používají všemožné skripty, Ansible a systémy nasazení.

Cartridge sama spravuje konfiguraci vshard, dělá to na základě své vlastní distribuovaná konfigurace. Je to v podstatě jednoduchý soubor YAML, jehož kopie je uložena v každé instanci Tarantool. Zjednodušení spočívá v tom, že framework sám hlídá svou konfiguraci a zajišťuje, aby byla všude stejná.

Za druhé je to opět otázka pohodlí. Konfigurace vshard nemá nic společného s vývojem obchodní logiky a pouze odvádí pozornost programátora od jeho práce. Když probíráme architekturu projektu, mluvíme nejčastěji o jednotlivých komponentách a jejich vzájemném působení. Je příliš brzy uvažovat o zavedení clusteru do 3 datových center.

Tyto problémy jsme řešili znovu a znovu a v určitém okamžiku se nám podařilo vyvinout přístup, který zjednodušil práci s aplikací během celého jejího životního cyklu: vytvoření, vývoj, testování, CI/CD, údržba.

Cartridge zavádí koncept role pro každý proces Tarantool. Role jsou koncept, který umožňuje vývojáři soustředit se na psaní kódu. Všechny role dostupné v projektu lze spustit na jedné instanci Tarantool a to bude pro testy stačit.

Klíčové vlastnosti kazety Tarantool:

  • automatizovaná klastrová orchestrace;
  • rozšíření funkčnosti aplikace pomocí nových rolí;
  • šablona aplikace pro vývoj a nasazení;
  • vestavěné automatické sharding;
  • integrace s testovacím rámcem Luatest;
  • správa clusterů pomocí WebUI a API;
  • nástroje pro balení a nasazení.

Ahoj světe!

Nemůžu se dočkat, až ukážu samotný framework, takže příběh o architektuře necháme na později a začneme něčím jednoduchým. Pokud předpokládáme, že samotný Tarantool je již nainstalován, pak zbývá jen udělat

$ tarantoolctl rocks install cartridge-cli
$ export PATH=$PWD/.rocks/bin/:$PATH

Tyto dva příkazy nainstalují nástroje příkazového řádku a umožní vám vytvořit vaši první aplikaci ze šablony:

$ cartridge create --name myapp

A dostaneme toto:

myapp/
├── .git/
├── .gitignore
├── app/roles/custom.lua
├── deps.sh
├── init.lua
├── myapp-scm-1.rockspec
├── test
│   ├── helper
│   │   ├── integration.lua
│   │   └── unit.lua
│   ├── helper.lua
│   ├── integration/api_test.lua
│   └── unit/sample_test.lua
└── tmp/

Toto je git repozitář s připraveným „Hello, World!“ aplikace. Zkusme to hned spustit po předchozí instalaci závislostí (včetně samotného rámce):

$ tarantoolctl rocks make
$ ./init.lua --http-port 8080

Takže máme jeden uzel spuštěný pro budoucí sdílenou aplikaci. Zvídavý laik může okamžitě otevřít webové rozhraní, nakonfigurovat cluster jednoho uzlu pomocí myši a vychutnat si výsledek, ale na radost je příliš brzy. Aplikace zatím neumí nic užitečného, ​​takže o nasazení vám řeknu později, ale nyní je čas napsat kód.

Vývoj aplikací

Jen si to představte, navrhujeme projekt, který musí jednou denně přijmout data, uložit je a vytvořit sestavu.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Začneme kreslit diagram a umístíme na něj tři komponenty: bránu, úložiště a plánovač. Na architektuře dále pracujeme. Protože jako úložiště používáme vshard, přidáme do schématu vshard-router a vshard-storage. Brána ani plánovač nebudou přímo přistupovat k úložišti; k tomu slouží router, k tomu byl vytvořen.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Tento diagram stále přesně nepředstavuje to, co budeme v projektu stavět, protože komponenty vypadají abstraktně. Ještě musíme vidět, jak se to promítne do skutečného Tarantoolu – seskupme naše komponenty podle procesu.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Ponechat vshard-router a bránu na samostatných instancích nemá smysl. Proč musíme znovu surfovat po síti, když je to již odpovědností routeru? Musí být spuštěny v rámci stejného procesu. To znamená, že brána i vshard.router.cfg jsou inicializovány v jednom procesu a nechají je interagovat lokálně.

Ve fázi návrhu bylo pohodlné pracovat se třemi komponentami, ale já jako vývojář při psaní kódu nechci myslet na spuštění tří instancí Tarnatool. Potřebuji spustit testy a zkontrolovat, že jsem bránu napsal správně. Nebo možná chci kolegům předvést nějakou funkci. Proč bych měl podstupovat potíže s nasazením tří kopií? Tak se zrodil koncept rolí. Role je běžný modul luash, jehož životní cyklus spravuje Cartridge. V tomto příkladu jsou čtyři - brána, router, úložiště, plánovač. V jiném projektu jich může být více. Všechny role lze spustit v jednom procesu a to bude stačit.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

A pokud jde o nasazení do stagingu nebo výroby, pak každému procesu Tarantool přidělíme vlastní sadu rolí v závislosti na možnostech hardwaru:

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Správa topologie

Informace o tom, kde které role běží, musí být někde uloženy. A to „někde“ je distribuovaná konfigurace, kterou jsem již zmínil výše. Nejdůležitější na něm je topologie clusteru. Zde jsou 3 replikační skupiny 5 procesů Tarantool:

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Nechceme přijít o data, proto s informacemi o běžících procesech zacházíme opatrně. Cartridge sleduje konfiguraci pomocí dvoufázového potvrzení. Jakmile chceme konfiguraci aktualizovat, nejprve zkontroluje, zda jsou všechny instance dostupné a připravené přijmout novou konfiguraci. Poté druhá fáze použije konfiguraci. I když se tedy ukáže, že jedna kopie je dočasně nedostupná, nic špatného se nestane. Konfigurace se prostě nepoužije a předem se vám zobrazí chyba.

Také v sekci topologie je uveden tak důležitý parametr, jako je vůdce každé replikační skupiny. Obvykle se jedná o kopii, která se nahrává. Zbytek je většinou jen pro čtení, i když mohou existovat výjimky. Někdy se odvážní vývojáři nebojí konfliktů a mohou zapisovat data do několika replik paralelně, ale existují operace, které by se bez ohledu na to neměly provádět dvakrát. K tomu existuje znamení vůdce.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Život rolí

Aby v takové architektuře existovala abstraktní role, musí je framework nějak spravovat. Kontrola přirozeně probíhá bez restartování procesu Tarantool. Pro správu rolí existují 4 zpětná volání. Samotná kazeta je bude volat v závislosti na tom, co je napsáno v její distribuované konfiguraci, čímž se konfigurace aplikuje na konkrétní role.

function init()
function validate_config()
function apply_config()
function stop()

Každá role má svou funkci init. Volá se jednou, když je role povolena, nebo když je Tarantool restartován. Tam je vhodné např. inicializovat box.space.create nebo plánovač může spustit nějaké vlákno na pozadí, které bude v určitých časových intervalech vykonávat práci.

Jedna funkce init nemusí stačit. Cartridge umožňuje rolím využívat výhod distribuované konfigurace, kterou používá k ukládání topologie. Můžeme deklarovat novou sekci ve stejné konfiguraci a uložit do ní fragment obchodní konfigurace. V mém příkladu by to mohlo být datové schéma nebo nastavení plánu pro roli plánovače.

Clusterová volání validate_config и apply_config pokaždé, když se distribuovaná konfigurace změní. Když je konfigurace aplikována dvoufázovým potvrzením, cluster zkontroluje, zda je každá role připravena přijmout tuto novou konfiguraci, a v případě potřeby oznámí uživateli chybu. Když všichni souhlasí, že konfigurace je normální, pak apply_config.

Také role mají metodu stop, který je potřebný k vyčištění výstupu role. Pokud řekneme, že plánovač již na tomto serveru není potřeba, může zastavit ta vlákna, se kterými začínal init.

Role se mohou vzájemně ovlivňovat. Jsme zvyklí psát volání funkcí v Lua, ale může se stát, že daný proces nemá roli, kterou potřebujeme. Pro usnadnění volání po síti používáme pomocný modul rpc (remote procedure call), který je postaven na bázi standardního netboxu zabudovaného v Tarantool. To může být užitečné, pokud například vaše brána chce přímo požádat plánovače, aby provedl úlohu právě teď, místo aby čekal den.

Dalším důležitým bodem je zajištění odolnosti proti chybám. Kazeta používá protokol SWIM ke sledování zdraví [4]. Stručně řečeno, procesy si mezi sebou vyměňují „fámy“ přes UDP – každý proces sděluje svým sousedům nejnovější zprávy a ti reagují. Pokud najednou odpověď nepřichází, Tarantool začne tušit, že něco není v pořádku, a po chvíli odříká smrt a začne tuto novinu sdělovat všem kolem.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Na základě tohoto protokolu Cartridge organizuje automatické zpracování selhání. Každý proces monitoruje své prostředí, a pokud vedoucí náhle přestane reagovat, replika může převzít jeho roli a Cartridge podle toho nakonfiguruje běžící role.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Zde musíte být opatrní, protože časté přepínání tam a zpět může vést ke konfliktům dat během replikace. Samozřejmě byste neměli povolovat automatické převzetí služeb při selhání náhodně. Musíme jasně pochopit, co se děje, a být si jisti, že replikace se nezlomí poté, co bude vůdce obnoven a koruna mu bude vrácena.

Z toho všeho můžete mít pocit, že role jsou podobné mikroslužbám. V jistém smyslu jsou právě takové, pouze jako moduly uvnitř procesů Tarantool. Existuje ale také řada zásadních rozdílů. Za prvé, všechny role projektu musí žít ve stejné základně kódu. A všechny procesy Tarantool by měly být spouštěny ze stejné kódové základny, aby nedošlo k žádným překvapením, jako jsou ta, když se pokusíme inicializovat plánovač, ale prostě neexistuje. Také byste neměli připustit rozdíly ve verzích kódu, protože chování systému v takové situaci je velmi obtížné předvídat a ladit.

Na rozdíl od Dockeru nemůžeme jen vzít „obraz“ role, přenést jej na jiný stroj a spustit jej tam. Naše role nejsou tak izolované jako kontejnery Docker. Také nemůžeme provozovat dvě stejné role na jedné instanci. Role buď existuje, nebo neexistuje; v jistém smyslu je to singleton. A do třetice musí být role v rámci celé replikační skupiny stejné, protože jinak by to bylo absurdní – data jsou stejná, ale konfigurace je jiná.

Nástroje pro nasazení

Slíbil jsem, že ukážu, jak Cartridge pomáhá nasazovat aplikace. Aby se ostatním usnadnil život, obsahuje rámec balíčky RPM:

$ cartridge pack rpm myapp -- упакует для нас ./myapp-0.1.0-1.rpm
$ sudo yum install ./myapp-0.1.0-1.rpm

Nainstalovaný balíček obsahuje téměř vše, co potřebujete: jak aplikaci, tak nainstalované závislosti. Tarantool také dorazí na server jako závislost balíčku RPM a naše služba je připravena ke spuštění. To se provádí přes systemd, ale nejprve musíte napsat malou konfiguraci. Minimálně zadejte URI každého procesu. Tři stačí například.

$ sudo tee /etc/tarantool/conf.d/demo.yml <<CONFIG
myapp.router: {"advertise_uri": "localhost:3301", "http_port": 8080}
myapp.storage_A: {"advertise_uri": "localhost:3302", "http_enabled": False}
myapp.storage_B: {"advertise_uri": "localhost:3303", "http_enabled": False}
CONFIG

Je zde zajímavá nuance. Namísto zadání pouze portu binárního protokolu zadáme celou veřejnou adresu procesu včetně názvu hostitele. To je nezbytné, aby uzly clusteru věděly, jak se k sobě navzájem připojit. Je špatný nápad používat 0.0.0.0 jako adresu advertise_uri; měla by to být externí adresa IP, nikoli vazba soketu. Bez něj nebude nic fungovat, takže vám Cartridge jednoduše nedovolí spustit uzel se špatným advertise_uri.

Nyní, když je konfigurace připravena, můžete spustit procesy. Protože běžná jednotka systemd neumožňuje spuštění více než jednoho procesu, aplikace na Cartridge se instalují tzv. vytvořené jednotky, které fungují takto:

$ sudo systemctl start myapp@router
$ sudo systemctl start myapp@storage_A
$ sudo systemctl start myapp@storage_B

V konfiguraci jsme zadali HTTP port, na kterém Cartridge obsluhuje webové rozhraní - 8080. Pojďme na něj a mrkneme se:

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Vidíme, že ačkoli procesy běží, ještě nejsou nakonfigurovány. Kazeta zatím neví, kdo s kým má replikovat a nemůže se sama rozhodnout, takže čeká na naše akce. Ale nemáme moc na výběr: život nového clusteru začíná konfigurací prvního uzlu. Poté do clusteru přidáme ostatní, přidělíme jim role a v tomto okamžiku lze nasazení považovat za úspěšně dokončené.

Nalijme si sklenici vašeho oblíbeného nápoje a odpočiňte si po dlouhém pracovním týdnu. Aplikaci lze použít.

Tarantool Cartridge: rozřezání Lua backendu ve třech řádcích

Výsledky

jaké jsou výsledky? Vyzkoušejte to, použijte to, zanechte zpětnou vazbu, vytvořte vstupenky na Github.

reference

[1] Tarantool » 2.2 » Reference » Reference Rocks » Modul vshard

[2] Jak jsme implementovali jádro investičního podnikání Alfa-Bank založené na Tarantool

[3] Architektura fakturace nové generace: transformace s přechodem na Tarantool

[4] SWIM - protokol konstrukce clusteru

[5] GitHub - tarantool/cartridge-cli

[6] GitHub - tarantool/cartridge

Zdroj: www.habr.com

Přidat komentář