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

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

V Mail.ru Group máme Tarantool — server aplikací na Lua, který je zároveň také databází (nebo naopak?). Je rychlý a skvělý, ale možnosti jednoho serveru nejsou nekonečné. Vertikální škálování také není všelék, proto má Tarantool nástroje pro horizontální škálování — modul vshard. [1]. Umožňuje shardování dat na několika serverech, ale bude potřeba se s tím pořádně potrápit, abyste to nastavili a přidali obchodní logiku.

Dobré zprávy: nasbírali jsme spoustu zkušeností (například [2], [3]) a vytvořili jsme další framework, který výrazně usnadní řešení tohoto problému.

Tarantool Cartridge — je nový framework pro vývoj složitých distribuovaných systémů. Umožňuje soustředit se na psaní obchodní logiky místo řešení infrastrukturních problémů. Pod tímto textem vám povím, jak je tento framework uspořádán a jak s jeho pomocí psát distribuované služby.

A co je vlastně problém?

Máme tarrantula, máme vshard — co víc si přát?

Za prvé, jde o pohodlí. Konfigurace vshard se nastavuje pomocí Lua tabulek. Aby distribuovaný systém složený z několika procesů Tarantool fungoval správně, musí být konfigurace všude stejná. Nikdo to nechce dělat ručně. Proto se používají různé skripty, Ansible a nasazovací systémy.

Cartridge sám spravuje konfiguraci vshard, dělá to na základě své vlastní distribuované konfigurace.V podstatě je to jednoduchý YAML soubor, jehož kopie se uchovává v každém exempláři Tarantool. Zjednodušení spočívá v tom, že framework sám dohlíží na svou konfiguraci a zajišťuje, aby byla všude stejná.

Za druhé, opět jde o pohodlí. Konfigurace vshard nemá žádný vztah k vývoji obchodní logiky a jen odvádí programátora od práce. Když diskutujeme o architektuře určitého projektu, obvykle se hovoří o jednotlivých komponentech a jejich interakci. O nasazení clusteru na 3 datová centra zatím nemáme důvod přemýšlet.

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ý umožňuje zjednodušit práci s aplikací během celého jejího životního cyklu: vytváření, vývoj, testování, CI/CD, údržba.

Cartridge zavádí pojem rolí pro každý proces Tarantool. Role jsou koncept, který umožňuje vývojáři soustředit se na psaní kódu. Všechny role v projektu lze spustit na jedné instanci Tarantool, což bude pro testování dostatečné.

Hlavní funkce Tarantool Cartridge:

  • automatizované orkestraci clastru;
  • rozšíření funkcionality aplikace pomocí nových rolí;
  • šablona aplikace pro vývoj a nasazení;
  • integrované automatické shardování;
  • integrování s testovacím rámcem Luatest;
  • správa clastru pomocí WebUI a API;
  • nástroje pro balení a nasazení.

Hello, World!

Nemohu se dočkat, až představím samotný rámec, takže o architektuře si povíme později a začneme s jednoduchým. Pokud předpokládáme, že je Tarantool již nainstalován, zbývá jen udělat

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

Tyto dva příkazy nainstalují příkazové řádky a umožní vytvořit naši první aplikaci ze šablony:

$ cartridge create --name myapp

A tady je, co dostaneme:

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 gitový repozitář s hotovou aplikací 'Hello, World!'. Pojďme ji hned zkusit spustit, předtím nainstalujeme závislosti (včetně samotného rámce):

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

Takže máme spuštěnou jednu uzel budoucí shardované aplikace. Zvídavý jedinec si může ihned otevřít webové rozhraní, konfigurovat klaster z jednoho uzlu myší a vychutnat si výsledek, ale s radováním zatím opatrně. Zatím aplikace neumí dělat nic užitečného, tak o nasazení si povíme později, a nyní nastává čas psát kód.

Vývoj aplikací

Představme si, že navrhujeme projekt, který má přijímat data, ukládat je a jednou denně vytvářet zprávu.

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

Začínáme nakreslit schéma a umístíme na něj tři komponenty: gateway, storage a scheduler. Dále rozpracováváme architekturu. Pokud používáme jako úložiště vshard, přidáváme do schématu vshard-router a vshard-storage. Ani gateway, ani scheduler se nebudou obracet na úložiště přímo, k tomu je router, který byl k tomu vytvořen.

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

Tento diagram stále ještě zcela nepřesně odráží to, co budeme vytvářet v projektu, protože komponenty vypadají abstraktně. Je potřeba se ještě podívat, jak se to promítne do skutečného Tarantoolu — seskupíme naše komponenty podle procesů.

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

Mít vshard-router a gateway na oddělených instancích moc nedává smysl. Proč se opakovaně procházet po síti, když to patří do povinností routeru? Měly by běžet uvnitř jednoho procesu. To znamená, že v jednom procesu se inicializují jak gateway, tak vshard.router.cfg, a ať spolu lokálně komunikují.

Fungovat při návrhu se třemi komponentami bylo pohodlné, ale jako vývojář, když píšu kód, nechci přemýšlet o spuštění tří instancí Tarnatoolu. Potřebuji spustit testy a ověřit, že jsem správně napsal gateway. Nebo možná chci kolegům ukázat nějakou funkci. Proč bych se měl mučit s nasazením tří instancí? Tak vznikla koncepce rolí. Role je běžný lua modul, jehož životním cyklem řídí Cartridge. V tomto příkladu jich je čtyři — gateway, router, storage, scheduler. V jiném projektu jich může být více. Všechny role lze spustit v jednom procesu a to bude dostačující.

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

A když přijde na nasazení do stagingu nebo do výroby, pak přiřadíme každému procesu Tarantool svůj vlastní soubor rolí v závislosti na hardwarových možnostech:

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

Správa topologie

Informace o tom, kde které role běží, je potřeba někde uchovat. A tím „někde“ je distribuovaná konfigurace, o které jsem se již zmínil výše. Nejdůležitější na ní je topologie clusteru. Zde je znázorněno 3 replikové skupiny z 5 procesů Tarantool:

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

Nechceme přijít o data, a proto se pečlivě staráme o informace o běžících procesech. Cartridge sleduje konfiguraci pomocí dvoufázového commitu. Jakmile chceme aktualizovat konfiguraci, nejprve zkontroluje dostupnost všech instancí a jejich připravenost přijmout novou konfiguraci. Poté se ve druhé fázi aplikuje konfigurace. Takže i když jedna instance byla dočasně nedostupná, nic hrozného se nestane. Konfigurace se prostě neaplikuje a vy předem uvidíte chybu.

V sekci topologie je také uveden důležitý parametr, jako je vůdce každé replikované skupiny. Obvykle je to instance, na kterou se zapisuje. Ostatní jsou většinou jen pro čtení, i když zde mohou být výjimky. Někdy se odvážní vývojáři nebojí konfliktů a mohou zapisovat data na několik replik paralelně, ale existují určité operace, které se za žádných okolností nesmí provádět dvakrát. K tomu slouží označení vůdce.

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

Život rolí

Aby mohla abstraktní role existovat v takové architektuře, musí je rámec nějakým způsobem spravovat. Samozřejmě, správa probíhá bez restartování procesu Tarantool. Pro správu rolí existují 4 zpětné volání. Cartridge je automaticky vyvolá v závislosti na tom, co má napsáno v distribuované konfiguraci, čímž aplikuje konfiguraci na konkrétní role.

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

Každá role má funkci init. Ta se vyvolává jednou buď při aktivaci role, nebo při restartování Tarantoolu. Je tam například pohodlné inicializovat box.space.create, nebo může scheduler spustit nějaký pozadový fiber, který provádí úkoly v určitých časových intervalech.

Jedna funkce init nemusí stačit. Cartridge umožňuje rolím využívat tu distribuovanou konfiguraci, kterou používá pro ukládání topologie. V této konfiguraci můžeme také vyhlásit novou sekci a uchovávat v ní fragment obchodní konfigurace. V mém příkladu to může být datový schéma nebo nastavení rozvrhu pro roli scheduler.

Kluster vyvolává validate_config a apply_config při každé změně distribuované konfigurace. Když se konfigurace aplikuje dvoufázovým potvrzením, kluster kontroluje, zda je každá role připravena tuto novou konfiguraci přijmout, a v případě potřeby informuje uživatele o chybě. Když se všichni shodnou na tom, že konfigurace je v pořádku, následuje apply_config.

Také mají role metodu stop, která slouží k vyčištění výsledků činnosti role. Pokud říkáme, že scheduler na tomto serveru již není potřeba, může zastavit ty fibery, které spustil pomocí init.

Role mohou spolu komunikovat. Jsme zvyklí psát volání funkcí v Luě, ale může se stát, že v tomto procesu nám chybí požadovaná role. Abychom usnadnili síťové volání, používáme pomocný modul rpc (remote procedure call), který je postaven na standardním netboxu zabudovaném v Tarantoolu. To se může hodit, pokud například váš gateway chce přímo požádat scheduler, aby okamžitě vykonal práci, místo aby čekal celý den.

Dalším důležitým bodem je zajištění odolnosti vůči výpadkům. Pro monitoring zdraví se v Cartridge používá protokol SWIM. [4]Zjednodušeně řečeno, procesy si mezi sebou vyměňují „zvěsti“ pomocí UDP — každý proces sděluje svým sousedům nejnovější zprávy a oni odpovídají. Pokud náhodou odpověď nepřijde, Tarantool začne podezřívat, že něco není v pořádku, a po nějaké době prohlásí smrt a začne tuto zprávu vyprávět všem kolemjdoucím.

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

Na základě tohoto protokolu organizuje Cartridge automatické zpracování selhání. Každý proces sleduje své okolí, a pokud lídr náhle přestane odpovídat, replika se může ujmout jeho role, a Cartridge příslušně konfigurují spuštěné role.

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

Je třeba být opatrný, protože časté přepínání sem a tam může vést ke konfliktům dat při replikaci. Automatický failover zapínat bezmyšlenkovitě samozřejmě není rozumné. Je nutné mít jasnou představu o tom, co se děje, a být si jisti, že replikace se nezhroutí po tom, co se lídr obnoví a vrátí mu korunu.

Z všeho, co bylo řečeno, se může zdát, že role jsou podobné mikroslužbám. V určitém smyslu jsou jimi, ale jako moduly uvnitř procesů Tarantoolu. Existuje však několik zásadních rozdílů. Zaprvé, všechny role projektu by měly žít v jedné kódové základně. A všechny procesy Tarantoolu by měly být spouštěny z jedné kódové základny, aby nedocházelo k překvapením, jako když se snažíme inicializovat scheduler a ten prostě není. Také je důležité zabránit rozdílům 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 jednoduše vzít ‚obraz‘ role, přenést jej na jiný stroj a tam spustit. Naše role nejsou tak izolované jako Docker kontejnery. Také nemůžeme spustit na jednom instance dvě stejné role. Role buď existuje, nebo ne, v určitém smyslu je to singleton. A konečně, v celé replikované skupině musí být role shodné, protože jinak by to bylo absurdní – data jsou stejná, ale konfigurace různá.

Nástroje pro nasazení

Slíbil jsem ukázat, jak Cartridge pomáhá nasazovat aplikace. Abych usnadnil život ostatním, framework balí RPM balíčky:

$ cartridge pack rpm myapp -- zabalí pro nás .\/myapp-0.1.0-1.rpm
$ sudo yum install .\/myapp-0.1.0-1.rpm

Nainstalovaný balíček obsahuje téměř vše potřebné: jak aplikaci, tak nainstalované Lua závislosti. Tarantool na server také přijde jako závislost RPM balíčku, a naše služba je připravena k spuštění. To se provádí přes systemd, ale nejdříve je třeba napsat trochu konfigurace. Minimálně uvést URI každého procesu. Třech pro příklad stačí.

$ 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

Zde je zajímavý nuance. Místo toho, abychom uvedli pouze port binárního protokolu, uvádíme veřejnou adresu procesu včetně hostname. To je potřeba, aby uzly clustera věděly, jak se navzájem spojit. Špatná idea je použít jako advertise_uri adresu 0.0.0.0, to by měla být externí IP adresa, ne bind socket. Bez něj nic fungovat nebude, proto Cartridge jednoduše nedovolí spustit uzel s nesprávným advertise_uri.

Teď, když je konfigurace připravena, můžeme spustit procesy. Protože běžný systemd unit neumožňuje spustit více než jeden proces, aplikace na Cartridge instalují takzvané instantiated units, které fungují následovně:

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

V konfiguraci jsme uvedli HTTP port, na kterém Cartridge obsluhuje webové rozhraní – 8080. Podíváme se na něj a uvidíme:

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

Vidíme, že procesy jsou sice spuštěny, ale zatím nejsou nakonfigurovány. Cartridge zatím neví, kdo se s kým má replikovat, a nemůže se rozhodnout samostatně, proto čeká na naše kroky. A naše volba není velká: život nového clusteru začíná konfigurací prvního uzlu. Poté přidáme zbytek do clusteru, přiřadíme jim role a tímto může být nasazení považováno za úspěšně dokončené.

Nalijeme si oblíbený nápoj a odpočineme si po dlouhém pracovním týdnu. Aplikaci lze provozovat.

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

Závěry

A jaké jsou závěry? Zkoušejte, používejte, dejte nám zpětnou vazbu, zakládejte tikety na GitHubu.

Odkazy

[1] Tarantool » 2.2 » Reference » Odkaz na moduly » Modul vshard

[2] Jak jsme implementovali jádro investičního byznysu Alfa-Banku na základě Tarantoolu

[3] Architektura nového generace billingových systémů: transformace s přechodem na Tarantool

[4] SWIM — protokol pro vytváření clusteru

[5] GitHub — tarantool/cartridge-cli

[6] GitHub — tarantool/cartridge

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster