Tarantool kazetta: Lua backend sharding három sorban

Tarantool kazetta: Lua backend sharding három sorban

A Mail.ru Groupnál van Tarantool - ez egy Lua alkalmazásszerver, amely adatbázisként is működik (vagy fordítva?). Gyors és menő, de egy szerver képességei még mindig nem korlátlanok. A függőleges skálázás szintén nem csodaszer, így a Tarantool rendelkezik a vízszintes méretezéshez szükséges eszközökkel – a vshard modullal [1]. Lehetővé teszi az adatok szilánkolását több szerveren, de a beállításhoz és az üzleti logikához csatolni kell vele.

Jó hír: összegyűjtöttünk néhány nagy felvételt (pl [2], [3]), és létrehozott egy másik keretrendszert, amely jelentősen leegyszerűsíti a probléma megoldását.

Tarantool patron egy új keretrendszer komplex elosztott rendszerek fejlesztéséhez. Lehetővé teszi, hogy az infrastrukturális problémák megoldása helyett az üzleti logika írására összpontosítson. A vágás alatt elmondom, hogyan működik ez a keretrendszer, és hogyan írhat elosztott szolgáltatásokat a segítségével.

És pontosan mi a probléma?

Van egy tarantulánk, van vshardunk – mit kívánhat még?

Először is, ez kényelmi kérdés. A vshard konfiguráció Lua táblákon keresztül konfigurálható. A több Tarantool folyamatból álló elosztott rendszer megfelelő működéséhez a konfigurációnak mindenhol azonosnak kell lennie. Senki sem akarja ezt manuálisan megtenni. Ezért mindenféle szkriptet, Ansible-t és telepítési rendszert használnak.

A kazetta maga kezeli a vshard konfigurációt, ennek alapján teszi ezt saját elosztott konfiguráció. Lényegében egy egyszerű YAML-fájl, amelynek egy példányát minden Tarantool-példány tárolja. Az egyszerűsítés az, hogy maga a keretrendszer figyeli a konfigurációját, és biztosítja, hogy mindenhol ugyanaz legyen.

Másodszor, ez ismét a kényelem kérdése. A vshard konfigurációnak semmi köze az üzleti logika fejlesztéséhez, és csak elvonja a programozó figyelmét a munkájáról. Amikor egy projekt felépítéséről beszélünk, leggyakrabban az egyes összetevőkről és azok kölcsönhatásáról beszélünk. Túl korai még gondolkodni egy fürt 3 adatközpontból történő kiépítésén.

Újra és újra megoldottuk ezeket a problémákat, és valamikor sikerült egy olyan megközelítést kidolgoznunk, amely leegyszerűsítette az alkalmazással való munkát annak teljes életciklusa során: létrehozás, fejlesztés, tesztelés, CI/CD, karbantartás.

A Cartridge bemutatja a szerep fogalmát minden Tarantool folyamathoz. A szerepek olyan koncepció, amely lehetővé teszi a fejlesztő számára, hogy a kódírásra összpontosítson. A projektben elérhető összes szerepkör futtatható egy Tarantool-példányon, és ez elegendő lesz a tesztekhez.

A Tarantool kazetta főbb jellemzői:

  • automatizált klaszter hangszerelés;
  • az alkalmazás funkcionalitásának bővítése új szerepkörök segítségével;
  • alkalmazássablon fejlesztéshez és telepítéshez;
  • beépített automatikus szilánkolás;
  • integráció a Luatest tesztelési keretrendszerrel;
  • fürtkezelés WebUI és API használatával;
  • csomagolási és telepítési eszközök.

Helló Világ!

Alig várom, hogy megmutassam magát a keretrendszert, így az építészetről szóló történetet későbbre hagyjuk, és valami egyszerűvel kezdjük. Ha feltételezzük, hogy maga a Tarantool már telepítve van, akkor nincs más hátra, mint a teendő

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

Ez a két parancs telepíti a parancssori segédprogramokat, és lehetővé teszi az első alkalmazás létrehozását a sablonból:

$ cartridge create --name myapp

És ezt kapjuk:

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/

Ez egy git adattár, amely készen áll a „Hello, World!” felirattal. Alkalmazás. Próbáljuk meg azonnal futtatni, miután korábban telepítettük a függőségeket (beleértve magát a keretrendszert is):

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

Tehát van egy csomópontunk a jövőbeli megosztott alkalmazás számára. Egy érdeklődő laikus azonnal megnyithatja a webes felületet, konfigurálhat egy csomópontból álló klasztert az egérrel, és élvezheti az eredményt, de még korai örülni. Egyelőre az alkalmazás nem tud semmi hasznosat csinálni, ezért később mesélek a telepítésről, de most itt az ideje a kód írásának.

Alkalmazásfejlesztés

Képzelje el, egy olyan projektet tervezünk, amelynek naponta egyszer adatokat kell fogadnia, el kell mentenie és jelentést kell készítenie.

Tarantool kazetta: Lua backend sharding három sorban

Elkezdünk rajzolni egy diagramot, és három komponenst helyezünk el rajta: átjárót, tárolót és ütemezőt. Dolgozunk tovább az építészeten. Mivel a vshard-ot tárolóként használjuk, a vshard-routert és a vshard-storage-t hozzáadjuk a sémához. Sem az átjáró, sem az ütemező nem fér hozzá közvetlenül a tárolóhoz; erre való az útválasztó, erre készült.

Tarantool kazetta: Lua backend sharding három sorban

Ez a diagram még mindig nem mutatja pontosan, hogy mit fogunk építeni a projektben, mert az összetevők absztraktnak tűnnek. Még látnunk kell, hogyan vetül ez ki a valódi Tarantoolra – csoportosítsuk az összetevőinket folyamatok szerint.

Tarantool kazetta: Lua backend sharding három sorban

Nincs értelme a vshard-routert és az átjárót külön példányokon tartani. Miért kell még egyszer szörfölni a hálózaton, ha ez már a router felelőssége? Ugyanabban a folyamatban kell futtatni őket. Ez azt jelenti, hogy az átjáró és a vshard.router.cfg egy folyamatban inicializálódik, és lehetővé teszi számukra, hogy helyileg kommunikáljanak egymással.

A tervezési szakaszban kényelmes volt három komponenssel dolgozni, de én, mint fejlesztő, a kód írása közben nem akarok a Tarnatool három példányának elindítására gondolni. Futtatnom kell teszteket, és ellenőriznem kell, hogy helyesen írtam-e be az átjárót. Vagy talán egy tulajdonságot szeretnék bemutatni a kollégáimnak. Miért kell végigcsinálnom a három példány telepítésével járó fáradságot? Így született meg a szerepek fogalma. A szerepkör egy normál luash modul, amelynek életciklusát a Cartridge kezeli. Ebben a példában négy közülük van - átjáró, útválasztó, tároló, ütemező. Egy másik projektben több is lehet. Minden szerep egy folyamatban futtatható, és ez elég lesz.

Tarantool kazetta: Lua backend sharding három sorban

Ha pedig a gyártási vagy gyártási folyamatba való bevezetésről van szó, akkor minden Tarantool-folyamathoz hozzárendeljük a saját szerepköreit a hardver képességeitől függően:

Tarantool kazetta: Lua backend sharding három sorban

Topológia kezelés

Valahol tárolni kell az arra vonatkozó információkat, hogy mely szerepkörök hol futnak. És ez a „valahol” az elosztott konfiguráció, amit fentebb már említettem. A legfontosabb dolog a fürt topológia. Íme 3 Tarantool-folyamat 5 replikációs csoportja:

Tarantool kazetta: Lua backend sharding három sorban

Nem szeretnénk adatokat elveszíteni, ezért óvatosan kezeljük a futó folyamatokkal kapcsolatos információkat. A kazetta kétfázisú véglegesítéssel követi nyomon a konfigurációt. Miután frissíteni akarjuk a konfigurációt, először ellenőrzi, hogy minden példány elérhető-e, és készen áll-e az új konfiguráció elfogadására. Ezt követően a második fázis alkalmazza a konfigurációt. Így még ha egy példány átmenetileg elérhetetlennek bizonyul is, semmi rossz nem történik. A konfiguráció egyszerűen nem kerül alkalmazásra, és előre hibaüzenetet fog látni.

Szintén a topológia részben szerepel egy olyan fontos paraméter, mint az egyes replikációs csoportok vezetője. Általában ez a rögzítés alatt álló másolat. A többi legtöbbször csak olvasható, bár lehetnek kivételek. Néha a bátor fejlesztők nem félnek a konfliktusoktól, és párhuzamosan több replikába is írhatnak adatokat, de vannak olyan műveletek, amelyeket bármitől függetlenül nem szabad kétszer végrehajtani. Erre van egy vezető jele.

Tarantool kazetta: Lua backend sharding három sorban

Szerepek élete

Ahhoz, hogy egy absztrakt szerep létezhessen egy ilyen architektúrában, a keretrendszernek kezelnie kell őket valahogy. A vezérlés természetesen a Tarantool folyamat újraindítása nélkül történik. A szerepek kezeléséhez 4 visszahívás áll rendelkezésre. Maga a kazetta hívja meg őket attól függően, hogy mi van írva az elosztott konfigurációjában, ezáltal alkalmazza a konfigurációt bizonyos szerepkörökre.

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

Minden szerepnek megvan a maga funkciója init. Egyszer hívják meg a szerepkör engedélyezésekor vagy a Tarantool újraindításakor. Ott kényelmes például a box.space.create inicializálása, vagy az ütemező elindíthat valamilyen háttérszálat, amely bizonyos időközönként munkát végez.

Egy funkció init lehet, hogy nem elég. A kazetta lehetővé teszi a szerepkörök számára, hogy kihasználják a topológia tárolására használt elosztott konfiguráció előnyeit. Ugyanabban a konfigurációban deklarálhatunk egy új szakaszt, és tárolhatunk benne az üzleti konfiguráció egy töredékét. Példámban ez lehet egy adatséma vagy az ütemező szerep ütemezési beállításai.

Klaszterhívások validate_config и apply_config minden alkalommal, amikor az elosztott konfiguráció megváltozik. Amikor egy konfigurációt kétfázisú véglegesítéssel alkalmaznak, a fürt ellenőrzi, hogy minden szerep készen áll-e az új konfiguráció elfogadására, és ha szükséges, hibát jelent a felhasználónak. Ha mindenki egyetért abban, hogy a konfiguráció normális, akkor a apply_config.

A szerepeknek is van módszerük stop, amely a szerepkör kimenetének megtisztításához szükséges. Ha azt mondjuk, hogy ezen a szerveren már nincs szükség ütemezőre, akkor leállíthatja azokat a szálakat, amelyekkel elindult init.

A szerepek kölcsönhatásba léphetnek egymással. Megszoktuk, hogy Lua-ban függvényhívásokat írunk, de előfordulhat, hogy egy adott folyamat nem rendelkezik a szükséges szereppel. A hálózaton keresztüli hívások megkönnyítésére az rpc (távoli eljáráshívás) segédmodult használjuk, amely a Tarantoolba épített szabványos netboxra épül. Ez akkor lehet hasznos, ha például az átjáró közvetlenül meg akarja kérni az ütemezőt a feladat azonnali elvégzésére, ahelyett, hogy várna egy napot.

Egy másik fontos szempont a hibatűrés biztosítása. A Cartridge a SWIM protokollt használja az állapot figyelésére [4]. Röviden: a folyamatok „pletykákat” cserélnek egymással az UDP-n keresztül – minden folyamat elmondja szomszédainak a legfrissebb híreket, és azok válaszolnak. Ha hirtelen nem jön a válasz, a Tarantool gyanakodni kezd, hogy valami nincs rendben, majd egy idő után a halált recitálja, és elkezdi elmondani mindenkinek a hírt.

Tarantool kazetta: Lua backend sharding három sorban

E protokoll alapján a Cartridge automatikus hibafeldolgozást szervez. Minden folyamat figyeli a környezetét, és ha a vezető hirtelen leállítja a válaszadást, a replika átveheti a szerepét, és a Cartridge ennek megfelelően konfigurálja a futó szerepeket.

Tarantool kazetta: Lua backend sharding három sorban

Itt óvatosnak kell lenni, mert a gyakori oda-vissza váltás adatütközésekhez vezethet a replikáció során. Természetesen nem szabad véletlenszerűen engedélyezni az automatikus feladatátvételt. Világosan meg kell értenünk, mi történik, és biztosnak kell lennünk abban, hogy a replikáció nem fog megszakadni, miután a vezért helyreállítják és a koronát visszaadják neki.

Mindebből az az érzése támadhat, hogy a szerepek hasonlóak a mikroszolgáltatásokhoz. Bizonyos értelemben ezek csak a Tarantool folyamatokon belüli modulok. De van néhány alapvető különbség is. Először is, minden projektszerepkörnek ugyanabban a kódbázisban kell élnie. És az összes Tarantool folyamatot ugyanarról a kódbázisról kell elindítani, hogy ne érjenek olyan meglepetések, mint amikor megpróbáljuk inicializálni az ütemezőt, de az egyszerűen nem létezik. Ezenkívül nem szabad megengedni a kódverziók közötti különbségeket, mert a rendszer viselkedését ilyen helyzetben nagyon nehéz megjósolni és hibakeresni.

A Dockerrel ellentétben nem tudunk csak úgy felvenni egy szerep "képet", átvinni egy másik gépre és ott futtatni. A mi szerepeink nem olyan elszigeteltek, mint a Docker konténerek. Ezenkívül nem futtathatunk két azonos szerepet egy példányon. Egy szerep vagy létezik, vagy nem; bizonyos értelemben egy egyéniség. Harmadszor pedig, a szerepeknek azonosaknak kell lenniük a teljes replikációs csoporton belül, mert különben abszurd lenne – az adatok ugyanazok, de a konfiguráció más.

Telepítési eszközök

Megígértem, hogy megmutatom, hogyan segít a Cartridge alkalmazások telepítésében. Mások életének megkönnyítése érdekében a keretrendszer RPM-csomagokat csomagol:

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

A telepített csomag szinte mindent tartalmaz, amire szüksége van: az alkalmazást és a telepített függőségeket egyaránt. Az RPM-csomag függőségeként a Tarantool is megérkezik a szerverre, szolgáltatásunk pedig készen áll az indulásra. Ez a systemd-n keresztül történik, de először meg kell írni egy kis konfigurációt. Legalább minden folyamat URI-jét adja meg. Három elég például.

$ 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

Van itt egy érdekes árnyalat. Ahelyett, hogy csak a bináris protokollportot adnánk meg, a folyamat teljes nyilvános címét adjuk meg, beleértve a gazdagép nevét is. Erre azért van szükség, hogy a fürt csomópontjai tudják, hogyan kapcsolódjanak egymáshoz. Rossz ötlet a 0.0.0.0-t használni reklám_uri címként; ennek külső IP-címnek kell lennie, nem pedig socket bind-nek. Enélkül semmi sem fog működni, így a Cartridge egyszerűen nem engedi, hogy elindítson egy csomópontot rossz hirdetési_uri-val.

Most, hogy a konfiguráció készen áll, elindíthatja a folyamatokat. Mivel egy normál rendszeregység nem enged több folyamat elindítását, ezért a kazettán lévő alkalmazások telepítése az ún. példányosított egységek, amelyek így működnek:

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

A konfigurációban megadtuk azt a HTTP portot, amelyen a Cartridge a webes felületet szolgálja - 8080. Menjünk rá, és nézzük meg:

Tarantool kazetta: Lua backend sharding három sorban

Látjuk, hogy bár a folyamatok futnak, még nincsenek konfigurálva. A patron még nem tudja, ki kivel replikáljon, és nem tud önállóan dönteni, ezért a mi cselekedeteinkre vár. De nincs sok választásunk: egy új klaszter élete az első csomópont konfigurációjával kezdődik. Ezután hozzáadjuk a többit a fürthöz, szerepeket rendelünk hozzájuk, és ezen a ponton a telepítés sikeresnek tekinthető.

Öntsünk egy pohár kedvenc italunkból, és lazítsunk egy hosszú munkahét után. Az alkalmazás használható.

Tarantool kazetta: Lua backend sharding három sorban

Eredményei

Mik az eredmények? Próbálja ki, használja, hagyjon visszajelzést, hozzon létre jegyeket a Githubon.

referenciák

[1] Tarantool » 2.2 » Referencia » Rocks referencia » Modul vshard

[2] Hogyan valósítottuk meg az Alfa-Bank Tarantool alapú befektetési üzletágának magját

[3] Új generációs számlázási architektúra: átalakulás a Tarantoolra való átállással

[4] SWIM - klaszterépítési protokoll

[5] GitHub - tarantool/cartridge-cli

[6] GitHub - tarantool/patron

Forrás: will.com

Hozzászólás