Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

V skupine Mail.ru máme Tarantool - to je aplikačný server v Lua, ktorý slúži aj ako databáza (alebo naopak?). Je to rýchle a cool, ale možnosti jedného servera stále nie sú neobmedzené. Vertikálne škálovanie tiež nie je všeliekom, preto má Tarantool nástroje na horizontálne škálovanie – modul vshard [1]. Umožňuje vám zdieľať údaje na viacerých serveroch, ale musíte si s nimi pohrať, aby ste ich nastavili a pripojili k nim obchodnú logiku.

Dobrá správa: zhromaždili sme niekoľko veľkých záberov (napr [2], [3]) a vytvoril ďalší rámec, ktorý výrazne zjednoduší riešenie tohto problému.

Tarantolová kazeta je nový rámec pre vývoj komplexných distribuovaných systémov. Umožňuje vám sústrediť sa na písanie obchodnej logiky namiesto riešenia problémov s infraštruktúrou. Pod rezom vám poviem, ako tento rámec funguje a ako pomocou neho písať distribuované služby.

V čom presne je problém?

Máme tarantulu, máme vshard - čo viac si priať?

Po prvé, je to vec pohodlia. Konfigurácia vshard sa konfiguruje pomocou tabuliek Lua. Aby distribuovaný systém viacerých procesov Tarantool fungoval správne, konfigurácia musí byť všade rovnaká. Nikto to nechce robiť ručne. Preto sa používajú najrôznejšie skripty, Ansible a systémy nasadenia.

Samotná kazeta spravuje konfiguráciu vshard, robí to na základe svojej vlastná distribuovaná konfigurácia. Ide v podstate o jednoduchý súbor YAML, ktorého kópia je uložená v každej inštancii Tarantool. Zjednodušenie spočíva v tom, že samotný framework sleduje svoju konfiguráciu a zabezpečuje, aby bola všade rovnaká.

Po druhé, je to opäť otázka pohodlia. Konfigurácia vshard nemá nič spoločné s vývojom obchodnej logiky a iba odvádza pozornosť programátora od jeho práce. Keď hovoríme o architektúre projektu, najčastejšie hovoríme o jednotlivých komponentoch a ich interakcii. Je príliš skoro uvažovať o zavedení klastra do 3 dátových centier.

Tieto problémy sme riešili znova a znova a v určitom bode sa nám podarilo vyvinúť prístup, ktorý zjednodušil prácu s aplikáciou počas celého jej životného cyklu: tvorba, vývoj, testovanie, CI/CD, údržba.

Cartridge predstavuje koncept úlohy pre každý proces Tarantool. Roly sú koncept, ktorý umožňuje vývojárovi sústrediť sa na písanie kódu. Všetky roly dostupné v projekte môžu byť spustené na jednej inštancii Tarantool a to bude stačiť na testy.

Kľúčové vlastnosti kazety Tarantol:

  • automatizovaná klastrová orchestrácia;
  • rozšírenie funkčnosti aplikácie pomocou nových rolí;
  • šablóna aplikácie pre vývoj a nasadenie;
  • vstavané automatické sharding;
  • integrácia s testovacím rámcom Luatest;
  • správa klastrov pomocou WebUI a API;
  • nástroje na balenie a nasadenie.

Dobrý deň, Svet!

Nemôžem sa dočkať, až ukážem samotný rámec, takže príbeh o architektúre necháme na neskôr a začneme niečím jednoduchým. Ak predpokladáme, že samotný Tarantool je už nainštalovaný, zostáva už len urobiť

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

Tieto dva príkazy nainštalujú nástroje príkazového riadka a umožnia vám vytvoriť vašu prvú aplikáciu zo šablóny:

$ cartridge create --name myapp

A toto 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 git repozitár s pripraveným „Ahoj, svet!“ aplikácie. Skúsme to spustiť hneď po nainštalovaní závislostí (vrátane samotného rámca):

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

Takže máme spustený jeden uzol pre budúcu sharded aplikáciu. Zvedavý laik môže okamžite otvoriť webové rozhranie, nakonfigurovať zhluk jedného uzla pomocou myši a vychutnať si výsledok, ale na radosť je priskoro. Aplikácia zatiaľ nemôže robiť nič užitočné, takže o nasadení vám poviem neskôr, ale teraz je čas napísať kód.

Vývoj aplikácií

Len si predstavte, navrhujeme projekt, ktorý musí prijímať dáta, ukladať ich a zostavovať report raz denne.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Začneme kresliť diagram a umiestnime naň tri komponenty: bránu, úložisko a plánovač. Ďalej pracujeme na architektúre. Keďže ako úložisko používame vshard, do schémy pridáme vshard-router a vshard-storage. Brána ani plánovač nebudú priamo pristupovať k úložisku; na to je smerovač určený, na to bol vytvorený.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Tento diagram stále presne nereprezentuje to, čo budeme v projekte stavať, pretože komponenty vyzerajú abstraktne. Stále musíme vidieť, ako sa to premietne do skutočného Tarantoolu - zoskupme naše komponenty podľa procesu.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Udržiavanie smerovača vshard a brány v samostatných inštanciách nemá zmysel. Prečo musíme znova surfovať po sieti, ak je to už zodpovednosť smerovača? Musia byť spustené v rámci rovnakého procesu. To znamená, že brána aj vshard.router.cfg sú inicializované v jednom procese a umožňujú im lokálnu interakciu.

Vo fáze návrhu bolo pohodlné pracovať s tromi komponentmi, ale ja ako vývojár pri písaní kódu nechcem myslieť na spustenie troch inštancií Tarnatool. Potrebujem spustiť testy a skontrolovať, či som bránu napísal správne. Alebo možno chcem predviesť funkciu svojim kolegom. Prečo by som sa mal trápiť s nasadením troch kópií? Tak sa zrodil koncept rolí. Rola je bežný modul luash, ktorého životný cyklus spravuje Cartridge. V tomto príklade sú štyri z nich – brána, smerovač, úložisko, plánovač. V inom projekte ich môže byť viac. Všetky role môžu byť spustené v jednom procese a to bude stačiť.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

A pokiaľ ide o nasadenie do fázy alebo výroby, potom každému procesu Tarantool pridelíme vlastnú sadu rolí v závislosti od možností hardvéru:

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Správa topológie

Niekde musia byť uložené informácie o tom, kde ktoré roly bežia. A toto „niekde“ je distribuovaná konfigurácia, ktorú som už spomenul vyššie. Najdôležitejšia vec je topológia klastra. Tu sú 3 replikačné skupiny 5 procesov Tarantool:

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Nechceme prísť o dáta, preto s informáciami o bežiacich procesoch zaobchádzame opatrne. Cartridge sleduje konfiguráciu pomocou dvojfázového odovzdania. Keď chceme konfiguráciu aktualizovať, najprv skontroluje, či sú všetky inštancie dostupné a pripravené prijať novú konfiguráciu. Potom druhá fáza použije konfiguráciu. Aj keď sa teda ukáže, že jedna kópia je dočasne nedostupná, nič zlé sa nestane. Konfigurácia sa jednoducho nepoužije a vopred sa vám zobrazí chyba.

Aj v časti topológie je uvedený taký dôležitý parameter, akým je vodca každej replikačnej skupiny. Zvyčajne je to kópia, ktorá sa nahráva. Ostatné sú väčšinou len na čítanie, aj keď môžu existovať výnimky. Odvážni vývojári sa niekedy neboja konfliktov a dokážu zapisovať dáta do niekoľkých replík paralelne, no existujú operácie, ktoré by sa bez ohľadu na to nemali vykonávať dvakrát. Na to existuje znamenie vodcu.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Život rolí

Aby v takejto architektúre existovala abstraktná rola, framework ich musí nejako riadiť. Prirodzene, kontrola prebieha bez reštartovania procesu Tarantool. Na správu rolí existujú 4 spätné volania. Samotná kazeta ich zavolá v závislosti od toho, čo je napísané v jej distribuovanej konfigurácii, čím sa konfigurácia aplikuje na konkrétne roly.

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

Každá rola má svoju funkciu init. Volá sa raz, keď je rola povolená alebo keď sa reštartuje Tarantool. Tam je vhodné napríklad inicializovať box.space.create, prípadne môže plánovač spustiť nejaké vlákno na pozadí, ktoré bude vykonávať prácu v určitých časových intervaloch.

Jedna funkcia init nemusí stačiť. Cartridge umožňuje rolám využívať výhody distribuovanej konfigurácie, ktorú používa na ukladanie topológie. Môžeme deklarovať novú sekciu v rovnakej konfigurácii a uložiť do nej fragment obchodnej konfigurácie. V mojom príklade by to mohla byť dátová schéma alebo nastavenia plánu pre rolu plánovača.

Klastrové hovory validate_config и apply_config zakaždým, keď sa distribuovaná konfigurácia zmení. Keď je konfigurácia aplikovaná dvojfázovým odovzdaním, klaster skontroluje, či je každá rola pripravená prijať túto novú konfiguráciu, a ak je to potrebné, ohlási chybu používateľovi. Keď všetci súhlasia, že konfigurácia je normálna, potom apply_config.

Aj role majú svoju metódu stop, ktorý je potrebný na vyčistenie výstupu roly. Ak povieme, že plánovač už na tomto serveri nie je potrebný, môže zastaviť tie vlákna, s ktorými začal init.

Roly sa môžu navzájom ovplyvňovať. Sme zvyknutí písať volania funkcií v Lua, ale môže sa stať, že daný proces nemá rolu, ktorú potrebujeme. Na uľahčenie hovorov cez sieť používame pomocný modul rpc (remote procedure call), ktorý je postavený na báze štandardného netboxu zabudovaného v Tarantool. To môže byť užitočné, ak napríklad vaša brána chce priamo požiadať plánovača, aby vykonal úlohu práve teraz, namiesto toho, aby čakal deň.

Ďalším dôležitým bodom je zabezpečenie odolnosti voči chybám. Kazeta používa protokol SWIM na monitorovanie zdravia [4]. Stručne povedané, procesy si medzi sebou vymieňajú „fámy“ cez UDP – každý proces oznamuje svojim susedom najnovšie správy a oni reagujú. Ak zrazu odpoveď neprichádza, Tarantool začne tušiť, že niečo nie je v poriadku, a po chvíli zarecituje smrť a začne všetkým naokolo rozprávať túto správu.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Na základe tohto protokolu Cartridge organizuje automatické spracovanie zlyhania. Každý proces monitoruje svoje prostredie a ak vedúci náhle prestane reagovať, replika môže prevziať jeho úlohu a kazeta podľa toho nakonfiguruje bežiace roly.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Tu musíte byť opatrní, pretože časté prepínanie tam a späť môže viesť ku konfliktom údajov počas replikácie. Samozrejme, nemali by ste povoliť automatické prepnutie pri zlyhaní náhodne. Musíme jasne pochopiť, čo sa deje, a byť si istí, že po obnovení vodcu a vrátení koruny sa replikácia nezlomí.

Z toho všetkého môžete mať pocit, že roly sú podobné mikroslužbám. V istom zmysle sú práve také, iba ako moduly vnútri procesov Tarantool. Existuje však aj niekoľko zásadných rozdielov. Po prvé, všetky roly projektu musia žiť v rovnakej kódovej základni. A všetky procesy Tarantool by mali byť spustené z rovnakej kódovej základne, aby nedošlo k žiadnym prekvapeniam, ako sú tie, keď sa pokúsime inicializovať plánovač, ale jednoducho neexistuje. Tiež by ste nemali pripustiť rozdiely vo verziách kódu, pretože správanie systému v takejto situácii je veľmi ťažké predvídať a ladiť.

Na rozdiel od Dockera nemôžeme len vziať „obraz“ role, preniesť ju na iný stroj a spustiť tam. Naše úlohy nie sú také izolované ako kontajnery Docker. Taktiež nemôžeme spustiť dve rovnaké roly na jednej inštancii. Rola buď existuje, alebo neexistuje; v istom zmysle je to singleton. A do tretice, roly musia byť rovnaké v rámci celej replikačnej skupiny, lebo inak by to bolo absurdné – dáta sú rovnaké, ale konfigurácia je iná.

Nástroje nasadenia

Sľúbil som, že ukážem, ako Cartridge pomáha nasadzovať aplikácie. Aby sme uľahčili život ostatným, rámec obsahuje balíčky RPM:

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

Nainštalovaný balík obsahuje takmer všetko, čo potrebujete: aplikáciu aj nainštalované závislosti. Tarantool tiež dorazí na server ako závislosť balíka RPM a naša služba je pripravená na spustenie. To sa deje cez systemd, ale najprv musíte napísať malú konfiguráciu. Minimálne zadajte URI každého procesu. Stačia napríklad tri.

$ 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 tu zaujímavá nuansa. Namiesto zadávania iba portu binárneho protokolu zadávame celú verejnú adresu procesu vrátane názvu hostiteľa. Je to potrebné, aby sa uzly klastra vedeli navzájom spojiť. Je zlý nápad použiť 0.0.0.0 ako adresu advertise_uri; mala by to byť externá adresa IP, nie väzba soketu. Bez nej nebude nič fungovať, takže vám Cartridge jednoducho nedovolí spustiť uzol s nesprávnym advertise_uri.

Teraz, keď je konfigurácia pripravená, môžete spustiť procesy. Keďže bežná systemd jednotka neumožňuje spustenie viac ako jedného procesu, aplikácie na Cartridge sú inštalované tzv. inštanciované jednotky, ktoré fungujú takto:

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

V konfigurácii sme zadali HTTP port, na ktorom Cartridge obsluhuje webové rozhranie - 8080. Poďme na to a pozrime sa:

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Vidíme, že hoci procesy bežia, ešte nie sú nakonfigurované. Kazeta ešte nevie, kto má s kým replikovať a nemôže sa sama rozhodnúť, takže čaká na naše činy. Nemáme však veľa možností: život nového klastra začína konfiguráciou prvého uzla. Potom do klastra pridáme ostatných, pridelíme im roly a v tomto bode možno nasadenie považovať za úspešne dokončené.

Nalejme si pohár vášho obľúbeného nápoja a oddýchnite si po dlhom pracovnom týždni. Aplikáciu je možné použiť.

Tarantolová kazeta: rozrezanie Lua backendu v troch riadkoch

Výsledky

Aké sú výsledky? Vyskúšajte to, použite to, zanechajte spätnú väzbu, vytvorte lístky na Github.

referencie

[1] Tarantool » 2.2 » Referencie » Referencie pre skaly » Modul vshard

[2] Ako sme implementovali jadro investičného podnikania Alfa-Bank založené na Tarantool

[3] Architektúra fakturácie novej generácie: transformácia s prechodom na Tarantool

[4] SWIM - protokol konštrukcie klastra

[5] GitHub - tarantool/cartridge-cli

[6] GitHub - tarantool/kazeta

Zdroj: hab.com

Pridať komentár