Tarantool Cartridge: Lua backend sharding in drie regels

Tarantool Cartridge: Lua backend sharding in drie regels

Bij Mail.ru Group hebben we Tarantool - dit is een applicatieserver in Lua, die ook dienst doet als database (of andersom?). Het is snel en cool, maar de mogelijkheden van één server zijn nog steeds niet onbeperkt. Verticaal schalen is ook geen wondermiddel, dus Tarantool heeft tools voor horizontaal schalen: de vshard-module [1]. Hiermee kunt u gegevens over verschillende servers verdelen, maar u moet eraan sleutelen om het in te stellen en de bedrijfslogica eraan te koppelen.

Goed nieuws: we hebben een aantal grote foto's verzameld (bijv [2], [3]) en creëerde een ander raamwerk dat de oplossing voor dit probleem aanzienlijk zal vereenvoudigen.

Tarantool-cartridge is een nieuw raamwerk voor het ontwikkelen van complexe gedistribueerde systemen. Hiermee kunt u zich concentreren op het schrijven van bedrijfslogica in plaats van op het oplossen van infrastructuurproblemen. Hieronder zal ik je vertellen hoe dit raamwerk werkt en hoe je gedistribueerde services kunt schrijven die er gebruik van maken.

En wat is precies het probleem?

We hebben een tarantula, we hebben vshard - wat wil je nog meer?

Ten eerste is het een kwestie van gemak. De vshard-configuratie wordt geconfigureerd via Lua-tabellen. Om een ​​gedistribueerd systeem van meerdere Tarantool-processen correct te laten werken, moet de configuratie overal hetzelfde zijn. Niemand wil dit handmatig doen. Daarom worden allerlei scripts, Ansible en implementatiesystemen gebruikt.

Cartridge beheert zelf de vshard-configuratie, dit doet hij op basis van zijn eigen gedistribueerde configuratie. Het is in wezen een eenvoudig YAML-bestand, waarvan een kopie wordt opgeslagen in elke Tarantool-instantie. De vereenvoudiging is dat het raamwerk zelf de configuratie bewaakt en ervoor zorgt dat deze overal hetzelfde is.

Ten tweede is het opnieuw een kwestie van gemak. De vshard-configuratie heeft niets te maken met de ontwikkeling van bedrijfslogica en leidt de programmeur alleen maar af van zijn werk. Wanneer we de architectuur van een project bespreken, hebben we het meestal over de afzonderlijke componenten en hun interactie. Het is nog te vroeg om na te denken over de uitrol van een cluster naar drie datacenters.

We hebben deze problemen keer op keer opgelost en op een gegeven moment zijn we erin geslaagd een aanpak te ontwikkelen die het werken met de applicatie gedurende de hele levenscyclus ervan vereenvoudigde: creatie, ontwikkeling, testen, CI/CD, onderhoud.

Cartridge introduceert het concept van een rol voor elk Tarantool-proces. Rollen zijn een concept waarmee een ontwikkelaar zich kan concentreren op het schrijven van code. Alle beschikbare rollen in het project kunnen op één Tarantool-instantie worden uitgevoerd, en dit is voldoende voor tests.

Belangrijkste kenmerken van Tarantool-cartridge:

  • geautomatiseerde clusterorkestratie;
  • het uitbreiden van de functionaliteit van de applicatie met behulp van nieuwe rollen;
  • applicatiesjabloon voor ontwikkeling en implementatie;
  • ingebouwde automatische sharding;
  • integratie met het Luatest-testframework;
  • clusterbeheer met behulp van WebUI en API;
  • verpakkings- en implementatietools.

Hallo Wereld!

Ik kan niet wachten om het raamwerk zelf te laten zien, dus laten we het verhaal over de architectuur voor later achter en beginnen met iets eenvoudigs. Als we aannemen dat Tarantool zelf al is geïnstalleerd, hoeft u alleen nog maar te doen

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

Met deze twee opdrachten worden de opdrachtregelhulpprogramma's geïnstalleerd, zodat u uw eerste toepassing op basis van de sjabloon kunt maken:

$ cartridge create --name myapp

En dit is wat we krijgen:

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/

Dit is een git-repository met een kant-en-klaar “Hello, World!” sollicitatie. Laten we proberen het meteen uit te voeren, nadat we eerder de afhankelijkheden hebben geïnstalleerd (inclusief het raamwerk zelf):

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

We hebben dus één knooppunt actief voor de toekomstige shard-toepassing. Een nieuwsgierige leek kan onmiddellijk de webinterface openen, een cluster van één knooppunt configureren met de muis en genieten van het resultaat, maar het is te vroeg om zich te verheugen. Tot nu toe kan de applicatie niets nuttigs doen, dus ik zal je later over de implementatie vertellen, maar nu is het tijd om code te schrijven.

Applicatie ontwikkeling

Stel je voor: we ontwerpen een project dat gegevens moet ontvangen, opslaan en één keer per dag een rapport moet opstellen.

Tarantool Cartridge: Lua backend sharding in drie regels

We beginnen een diagram te tekenen en plaatsen er drie componenten op: gateway, opslag en planner. We werken verder aan de architectuur. Omdat we vshard als opslag gebruiken, voegen we vshard-router en vshard-storage toe aan het schema. Noch de gateway, noch de planner hebben rechtstreeks toegang tot de opslag; daar is de router voor, daar is hij voor gemaakt.

Tarantool Cartridge: Lua backend sharding in drie regels

Dit diagram geeft nog steeds niet precies weer wat we in het project gaan bouwen, omdat de componenten er abstract uitzien. We moeten nog zien hoe dit op de echte Tarantool zal worden geprojecteerd - laten we onze componenten op proces groeperen.

Tarantool Cartridge: Lua backend sharding in drie regels

Het heeft weinig zin om vshard-router en gateway op aparte instances te houden. Waarom moeten we opnieuw op het netwerk surfen als dit al de verantwoordelijkheid van de router is? Ze moeten binnen hetzelfde proces worden uitgevoerd. Dat wil zeggen dat zowel gateway als vshard.router.cfg in één proces worden geïnitialiseerd en lokaal kunnen communiceren.

In de ontwerpfase was het handig om met drie componenten te werken, maar ik als ontwikkelaar wil er tijdens het schrijven van de code niet aan denken om drie exemplaren van Tarnatool te lanceren. Ik moet tests uitvoeren en controleren of ik de gateway correct heb geschreven. Of misschien wil ik een functie aan mijn collega's demonstreren. Waarom zou ik de moeite nemen om drie exemplaren te implementeren? Zo ontstond het concept van rollen. Een rol is een reguliere luash-module waarvan de levenscyclus wordt beheerd door Cartridge. In dit voorbeeld zijn er vier: gateway, router, opslag, planner. Mogelijk zit er nog meer in een ander project. Alle rollen kunnen in één proces worden uitgevoerd, en dit zal voldoende zijn.

Tarantool Cartridge: Lua backend sharding in drie regels

En als het gaat om implementatie voor staging of productie, zullen we elk Tarantool-proces zijn eigen set rollen toewijzen, afhankelijk van de hardwaremogelijkheden:

Tarantool Cartridge: Lua backend sharding in drie regels

Topologiebeheer

Informatie over waar welke rollen draaien moet ergens worden opgeslagen. En dit “ergens” is de gedistribueerde configuratie, die ik hierboven al noemde. Het belangrijkste is de clustertopologie. Hier zijn 3 replicatiegroepen van 5 Tarantool-processen:

Tarantool Cartridge: Lua backend sharding in drie regels

Wij willen geen gegevens kwijtraken en gaan daarom zorgvuldig om met informatie over lopende processen. Cartridge houdt de configuratie bij met behulp van een commit in twee fasen. Zodra we de configuratie willen bijwerken, wordt eerst gecontroleerd of alle instanties beschikbaar zijn en klaar zijn om de nieuwe configuratie te accepteren. Hierna past de tweede fase de config. Dus zelfs als een exemplaar tijdelijk niet beschikbaar blijkt te zijn, zal er niets ergs gebeuren. De configuratie wordt simpelweg niet toegepast en u krijgt vooraf een foutmelding te zien.

Ook in de topologiesectie wordt zo'n belangrijke parameter als de leider van elke replicatiegroep aangegeven. Meestal is dit de kopie die wordt opgenomen. De rest is meestal alleen-lezen, hoewel er uitzonderingen kunnen zijn. Soms zijn dappere ontwikkelaars niet bang voor conflicten en kunnen ze gegevens parallel naar verschillende replica's schrijven, maar er zijn enkele bewerkingen die hoe dan ook niet twee keer mogen worden uitgevoerd. Hiervoor is er een teken van een leider.

Tarantool Cartridge: Lua backend sharding in drie regels

Leven van rollen

Wil er in een dergelijke architectuur een abstracte rol bestaan, dan moet het raamwerk deze op de een of andere manier beheren. Uiteraard vindt de controle plaats zonder het Tarantool-proces opnieuw te starten. Er zijn 4 callbacks om rollen te beheren. Cartridge zelf zal ze oproepen, afhankelijk van wat er in de gedistribueerde configuratie is geschreven, waardoor de configuratie op specifieke rollen wordt toegepast.

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

Elke rol heeft een functie init. Het wordt één keer aangeroepen wanneer de rol wordt ingeschakeld of wanneer Tarantool opnieuw wordt opgestart. Het is daar bijvoorbeeld handig om box.space.create te initialiseren, of de planner kan een achtergrondvezel starten die op bepaalde tijdsintervallen werk zal uitvoeren.

Eén functie init is misschien niet genoeg. Met Cartridge kunnen rollen profiteren van de gedistribueerde configuratie die wordt gebruikt om de topologie op te slaan. We kunnen een nieuwe sectie in dezelfde configuratie declareren en daarin een fragment van de bedrijfsconfiguratie opslaan. In mijn voorbeeld zou dit een gegevensschema of planningsinstellingen voor de rol van planner kunnen zijn.

Clusteroproepen validate_config и apply_config elke keer dat de gedistribueerde configuratie verandert. Wanneer een configuratie wordt toegepast door middel van een commit in twee fasen, controleert het cluster of elke rol klaar is om deze nieuwe configuratie te accepteren en rapporteert het indien nodig een fout aan de gebruiker. Als iedereen het erover eens is dat de configuratie normaal is, dan wordt de apply_config.

Ook rollen hebben een methode stop, wat nodig is om de uitvoer van de rol op te schonen. Als we zeggen dat de planner niet langer nodig is op deze server, kan deze de vezels stoppen waarmee deze is begonnen init.

Rollen kunnen met elkaar interacteren. We zijn gewend om functieaanroepen in Lua te schrijven, maar het kan gebeuren dat een bepaald proces niet de rol heeft die we nodig hebben. Om oproepen over het netwerk te vergemakkelijken, gebruiken we de rpc-hulpmodule (remote procedure call), die is gebouwd op basis van de standaard netbox die in Tarantool is ingebouwd. Dit kan bijvoorbeeld handig zijn als uw gateway de planner direct wil vragen de klus nu te klaren, in plaats van een dag te wachten.

Een ander belangrijk punt is het waarborgen van fouttolerantie. Cartridge gebruikt het SWIM-protocol om de gezondheid te bewaken [4]. Kortom, processen wisselen ‘geruchten’ met elkaar uit via UDP: elk proces vertelt zijn buren het laatste nieuws, en zij reageren. Als het antwoord plotseling niet komt, begint Tarantool te vermoeden dat er iets mis is, en na een tijdje reciteert hij de dood en begint hij iedereen over dit nieuws te vertellen.

Tarantool Cartridge: Lua backend sharding in drie regels

Op basis van dit protocol organiseert Cartridge de automatische storingsverwerking. Elk proces bewaakt zijn omgeving, en als de leider plotseling niet meer reageert, kan de replica zijn rol overnemen, en Cartridge configureert de lopende rollen dienovereenkomstig.

Tarantool Cartridge: Lua backend sharding in drie regels

Hier moet u voorzichtig zijn, omdat veelvuldig heen en weer schakelen tijdens de replicatie tot gegevensconflicten kan leiden. Natuurlijk mag u automatische failover niet willekeurig inschakelen. We moeten duidelijk begrijpen wat er gebeurt en er zeker van zijn dat de replicatie niet zal breken nadat de leider is hersteld en de kroon aan hem is teruggegeven.

Door dit alles krijg je misschien het gevoel dat rollen vergelijkbaar zijn met microservices. In zekere zin zijn ze precies dat, alleen als modules binnen Tarantool-processen. Maar er zijn ook een aantal fundamentele verschillen. Ten eerste moeten alle projectrollen in dezelfde codebasis leven. En alle Tarantool-processen moeten vanuit dezelfde codebasis worden gestart, zodat er geen verrassingen zijn zoals die wanneer we proberen de planner te initialiseren, maar deze bestaat simpelweg niet. Ook moet u geen verschillen in codeversies toestaan, omdat het gedrag van het systeem in een dergelijke situatie zeer moeilijk te voorspellen en te debuggen is.

In tegenstelling tot Docker kunnen we niet zomaar een "image"-rol aannemen, deze naar een andere machine brengen en daar uitvoeren. Onze rollen zijn niet zo geïsoleerd als Docker-containers. We kunnen ook niet twee identieke rollen op één exemplaar uitvoeren. Een rol bestaat of bestaat niet; in zekere zin is het een singleton. En ten derde moeten de rollen binnen de hele replicatiegroep hetzelfde zijn, omdat het anders absurd zou zijn: de gegevens zijn hetzelfde, maar de configuratie is anders.

Implementatiehulpmiddelen

Ik heb beloofd te laten zien hoe Cartridge helpt bij het implementeren van applicaties. Om het leven voor anderen gemakkelijker te maken, bevatten de raamwerkpakketten RPM-pakketten:

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

Het geïnstalleerde pakket bevat bijna alles wat u nodig heeft: zowel de applicatie als de geïnstalleerde afhankelijkheden. Tarantool zal ook op de server arriveren als afhankelijkheid van het RPM-pakket, en onze service is klaar om te lanceren. Dit gebeurt via systemd, maar eerst moet je een kleine configuratie schrijven. Geef minimaal de URI van elk proces op. Drie is bijvoorbeeld genoeg.

$ 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

Er is hier een interessante nuance. In plaats van alleen de binaire protocolpoort op te geven, specificeren we het volledige openbare adres van het proces, inclusief de hostnaam. Dit is nodig zodat de clusterknooppunten weten hoe ze met elkaar verbinding moeten maken. Het is een slecht idee om 0.0.0.0 te gebruiken als het advert_uri-adres; het moet een extern IP-adres zijn en geen socket-binding. Zonder dit zal niets werken, dus Cartridge laat je eenvoudigweg niet toe dat je een knooppunt start met de verkeerde advert_uri.

Nu de configuratie gereed is, kunt u de processen starten. Omdat een reguliere systemd-eenheid niet toestaat dat meer dan één proces wordt gestart, worden toepassingen op de Cartridge geïnstalleerd door de zogenaamde. geïnstantieerde eenheden die als volgt werken:

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

In de configuratie hebben we de HTTP-poort gespecificeerd waarop Cartridge de webinterface bedient: 8080. Laten we ernaartoe gaan en een kijkje nemen:

Tarantool Cartridge: Lua backend sharding in drie regels

We zien dat de processen weliswaar actief zijn, maar nog niet zijn geconfigureerd. De cartridge weet nog niet wie met wie moet repliceren en kan niet zelf een beslissing nemen, dus wacht op onze acties. Maar we hebben niet veel keuze: het leven van een nieuw cluster begint met de configuratie van het eerste knooppunt. Vervolgens voegen we de anderen toe aan het cluster, wijzen we hen rollen toe, en op dit punt kan de implementatie als succesvol voltooid worden beschouwd.

Laten we een glas van uw favoriete drankje inschenken en ontspannen na een lange werkweek. De applicatie kan worden gebruikt.

Tarantool Cartridge: Lua backend sharding in drie regels

Resultaten van

Wat zijn de resultaten? Probeer het, gebruik het, laat feedback achter, maak tickets op Github.

referenties

[1] Tarantool » 2.2 » Referentie » Rocks referentie » Module vshard

[2] Hoe we de kern van de beleggingsactiviteiten van Alfa-Bank op basis van Tarantool implementeerden

[3] Nieuwe generatie factureringsarchitectuur: transformatie met de transitie naar Tarantool

[4] SWIM - clusterconstructieprotocol

[5] GitHub - tarantool/cartridge-cli

[6] GitHub - tarantool/cartridge

Bron: www.habr.com

Voeg een reactie