Tarantool Cartridge: Lua backend-skæring i tre linjer

Tarantool Cartridge: Lua backend-skæring i tre linjer

Vi hos Mail.ru Group har Tarantool - dette er en applikationsserver i Lua, som også er en database i kombination (eller omvendt?). Det er hurtigt og sejt, men mulighederne for en enkelt server er stadig ikke ubegrænsede. Vertikal skalering er heller ikke et vidundermiddel, så Tarantool har værktøjer til horisontal skalering - vshard-modulet [1]. Det giver dig mulighed for at sønderdele data på tværs af flere servere, men du skal pille ved det for at sætte det op og skrue i forretningslogik.

Gode ​​nyheder: vi har samlet kogler (f.eks [2], [3]) og indgivet en anden ramme, der væsentligt vil forenkle løsningen af ​​dette problem.

Tarantool patron er en ny ramme til udvikling af komplekse distribuerede systemer. Det giver dig mulighed for at fokusere på at skrive forretningslogik i stedet for at løse infrastrukturproblemer. Under klippet vil jeg fortælle dig, hvordan denne ramme fungerer, og hvordan du skriver distribuerede tjenester med den.

Og hvad er problemet egentlig?

Vi har en tarantel, vi har en vshard - hvad mere kan du ønske dig?

For det første er det et spørgsmål om bekvemmelighed. Vshard-konfigurationen konfigureres via Lua-tabeller. For at et distribueret system med flere Tarantool-processer skal fungere korrekt, skal konfigurationen være den samme overalt. Ingen ønsker at gøre det manuelt. Derfor bruges alle slags scripts, Ansible, implementeringssystemer.

Cartridge administrerer selv vshard-konfigurationen, den gør dette baseret på sin egen egen distribueret konfiguration. Grundlæggende er det en simpel YAML-fil, hvoraf en kopi er gemt i hver forekomst af Tarantool. Forenklingen ligger i, at rammen selv overvåger sin konfiguration og sikrer, at den er ens overalt.

For det andet er det igen et spørgsmål om bekvemmelighed. Vshard-konfigurationen har intet at gøre med udviklingen af ​​forretningslogik og distraherer kun programmøren fra arbejdet. Når vi diskuterer et projekts arkitektur, taler vi oftest om individuelle komponenter og deres interaktion. Det er for tidligt at tænke på at udrulle en klynge til 3 datacentre.

Vi løste disse problemer gang på gang, og på et tidspunkt lykkedes det os at udvikle en tilgang, der giver os mulighed for at forenkle arbejdet med applikationen gennem hele dens livscyklus: oprettelse, udvikling, test, CI/CD, vedligeholdelse.

Cartridge introducerer konceptet med en rolle for hver Tarantool-proces. Roller er konceptet, der gør det muligt for udvikleren at fokusere på at skrive kode. Alle tilgængelige roller i projektet kan køres på en enkelt forekomst af Tarantool, og dette vil være nok til test.

Hovedtræk ved Tarantool Cartridge:

  • automatiseret klyngeorkestrering;
  • udvidelse af applikationsfunktionalitet med nye roller;
  • applikationsskabelon til udvikling og implementering;
  • indbygget automatisk skæring;
  • integration med Luatest testramme;
  • klyngestyring ved hjælp af WebUI og API;
  • pakke- og implementeringsværktøjer.

Hej Verden!

Jeg kan ikke vente med at vise selve rammeværket, så vi lader historien om arkitekturen stå til senere, og starter enkelt. Forudsat at selve Tarantool allerede er installeret, er det eneste, der er tilbage at gøre

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

Disse to kommandoer installerer kommandolinjeværktøjerne og giver dig mulighed for at oprette din første applikation fra skabelonen:

$ cartridge create --name myapp

Og dette er hvad vi får:

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/

Dette er et git-lager med "Hello, World!" Ansøgning. Lad os straks prøve at køre det, efter at have installeret afhængighederne (inklusive selve rammen):

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

Så vi har en node af den fremtidige sharded-applikation kørende. En nysgerrig lægmand kan straks åbne webgrænsefladen, konfigurere en klynge fra én node med musen og nyde resultatet, men det er for tidligt at glæde sig. Indtil videre ved applikationen ikke, hvordan man gør noget nyttigt, så jeg vil tale om implementering senere, og nu er det tid til at skrive kode.

Applikationsudvikling

Forestil dig, at vi designer et projekt, der skal modtage data, gemme dem og bygge en rapport én gang om dagen.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Vi begynder at tegne diagrammet og placerer tre komponenter på det: gateway, lager og planlægger. Vi arbejder på arkitekturen. Da vi bruger vshard som lager, tilføjer vi vshard-router og vshard-storage til skemaet. Hverken gatewayen eller planlæggeren får direkte adgang til lageret, der er en router til dette, den blev oprettet til dette.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Dette diagram afspejler stadig ikke helt præcist, hvad vi vil skabe i projektet, fordi komponenterne ser abstrakte ud. Vi mangler stadig at se, hvordan dette projiceres på det rigtige Tarantool - lad os gruppere vores komponenter efter processer.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Det giver ikke meget mening at beholde vshard-router og gateway på separate forekomster. Hvorfor skal vi gå over netværket igen, hvis dette allerede er routerens ansvar? De skal køres inden for samme proces. Det vil sige, at både gateway og vshard.router.cfg initialiseres i én proces, og lader dem interagere lokalt.

På designstadiet var det praktisk at arbejde med tre komponenter, men som udvikler, mens jeg skriver kode, vil jeg ikke tænke på at køre tre forekomster af Tarnatool. Jeg skal køre test og kontrollere, at jeg har stavet gateway korrekt. Eller måske vil jeg demonstrere en funktion for mine kolleger. Hvorfor skulle jeg bøvle med at implementere tre forekomster? Sådan blev begrebet roller født. En rolle er et almindeligt luash-modul, hvis livscyklus styres af Cartridge. I dette eksempel er der fire af dem - gateway, router, storage, scheduler. Der kan være flere i et andet projekt. Alle roller kan køres i én proces, og det vil være nok.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Og når det kommer til udrulning til iscenesættelse eller produktion, så vil vi tildele hver Tarantool-proces sit eget sæt roller afhængigt af hardwarefunktionerne:

Tarantool Cartridge: Lua backend-skæring i tre linjer

Topologistyring

Oplysninger om, hvor hvilke roller der kører, skal gemmes et sted. Og dette "et sted" er en distribueret konfiguration, som jeg allerede nævnte ovenfor. Det vigtigste i det er klyngens topologi. Her er 3 replikationsgrupper af 5 Tarantool-processer:

Tarantool Cartridge: Lua backend-skæring i tre linjer

Vi ønsker ikke at miste data, så vi tager os af information om kørende processer. Cartridge holder styr på konfigurationen med en to-faset commit. Så snart vi ønsker at opdatere konfigurationen, tjekker den først, at alle forekomster er tilgængelige og klar til at acceptere den nye konfiguration. Derefter anvender den anden fase konfigurationen. Selv hvis en kopi er midlertidigt utilgængelig, vil der således ikke ske noget dårligt. Konfigurationen vil simpelthen ikke gælde, og du vil se en fejl på forhånd.

Topologiafsnittet indeholder også en så vigtig parameter som lederen af ​​hver replikationsgruppe. Dette er normalt den instans, der bliver skrevet til. Resten er oftest skrivebeskyttet, selvom der kan være undtagelser. Nogle gange er modige udviklere ikke bange for konflikter og kan skrive data til flere replikaer parallelt, men der er nogle operationer, som trods alt ikke bør udføres to gange. For dette er der et tegn på en leder.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Rollernes liv

For at der kan eksistere en abstrakt rolle i en sådan arkitektur, skal rammen forvalte dem på en eller anden måde. Naturligvis sker styring uden at genstarte Tarantool-processen. Der er 4 tilbagekald til håndtering af roller. Cartridge selv vil kalde dem afhængigt af hvad den har skrevet i den distribuerede konfiguration, og derved anvende konfigurationen til specifikke roller.

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

Hver rolle har en funktion init. Det kaldes én gang, enten når en rolle er aktiveret, eller når Tarantool genstartes. Der er det praktisk, for eksempel at initialisere box.space.create, eller planlæggeren kan starte en baggrundsfiber, der vil udføre arbejde med bestemte intervaller.

enkelt funktion init er måske ikke nok. Cartridge giver roller mulighed for at drage fordel af den distribuerede konfiguration, som den bruger til at gemme topologien. Vi kan erklære en ny sektion i den samme konfiguration og gemme et forretningskonfigurationsfragment i den. I mit eksempel kan dette være et dataskema eller tidsplanindstillinger for planlæggerrollen.

Klyngeopkald validate_config и apply_config hver gang den distribuerede konfiguration ændres. Når en konfiguration anvendes af en to-faset commit, kontrollerer klyngen, at hver rolle er klar til at acceptere denne nye konfiguration og rapporterer en fejl til brugeren, hvis det er nødvendigt. Når alle var enige om, at konfigurationen er normal, så apply_config.

Roller har også en metode stopDet der er nødvendigt for at rydde op i rollens vitale. Hvis vi siger, at planlæggeren ikke længere er nødvendig på denne server, kan den stoppe de fibre, som den startede med init.

Roller kan interagere med hinanden. Vi er vant til at skrive funktionskald i Lua, men det kan ske, at der i denne proces ikke er nogen rolle, vi har brug for. For at lette opkald over netværket bruger vi rpc (remote procedure call) hjælpemodulet, som er bygget på basis af standard netbox indbygget i Tarantool. Dette kan være nyttigt, hvis f.eks. din gateway ønsker at bede planlæggeren direkte om at udføre jobbet lige nu, i stedet for at vente på en dag.

Et andet vigtigt punkt er at sikre fejltolerance. Cartridge bruger SWIM-protokollen til at overvåge sundheden [4]. Kort sagt, processer udveksler "rygter" med hinanden over UDP - hver proces fortæller sine naboer de seneste nyheder, og de reagerer. Hvis svaret pludselig ikke kom, begynder Tarantool at have mistanke om, at der er noget galt, og efter et stykke tid reciterer han døden og begynder at fortælle alle omkring denne nyhed.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Baseret på denne protokol organiserer Cartridge automatisk fejlhåndtering. Hver proces overvåger sit miljø, og hvis lederen pludselig holder op med at reagere, så kan replikaen overtage dens rolle, og Cartridge konfigurerer de kørende roller i overensstemmelse hermed.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Du skal være forsigtig her, fordi hyppig skift frem og tilbage kan føre til datakonflikter under replikering. At aktivere automatisk failover tilfældigt er selvfølgelig ikke det værd. Du skal klart forstå, hvad der sker, og være sikker på, at replikeringen ikke vil bryde, efter at lederen er genoprettet og kronen er returneret til ham.

Ud fra alt det ovenstående kan du få fornemmelsen af, at roller er som mikrotjenester. På en måde er de kun som moduler inde i Tarantool-processer. Men der er også en række grundlæggende forskelle. For det første skal alle projektroller leve i den samme kodebase. Og alle Tarantool-processer skal køre fra den samme kodebase, så der ikke er nogen overraskelser, som når vi forsøger at initialisere planlæggeren, men den eksisterer simpelthen ikke. Du bør heller ikke tillade forskelle i kodeversioner, fordi systemets opførsel i en sådan situation er meget vanskelig at forudsige og fejlfinde.

I modsætning til Docker kan vi ikke bare tage et "billede" af en rolle, tage den til en anden maskine og køre den der. Vores roller er ikke så isolerede som Docker-containere. Vi kan heller ikke køre to identiske roller på samme instans. En rolle er enten der, eller også er den ikke, på en måde er det en singleton. Og for det tredje, inden for hele replikeringsgruppen skal rollerne være de samme, for ellers ville det være latterligt – dataene er de samme, men konfigurationen er anderledes.

Implementeringsværktøjer

Jeg lovede at vise, hvordan Cartridge hjælper med at implementere applikationer. For at gøre livet lettere for andre, pakker rammepakkerne RPM-pakker:

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

Den installerede pakke indeholder næsten alt hvad du behøver: både applikationen og de installerede luash-afhængigheder. Tarantool kommer også på serveren som en afhængighed af RPM-pakken, og vores service er klar til at blive lanceret. Dette gøres gennem systemd, men først skal du skrive en lille konfiguration. Angiv som minimum URI'en for hver proces. Tre er nok for et eksempel.

$ 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

Der er en interessant nuance her. I stedet for kun at angive den binære protokolport, angiver vi hele processens offentlige adresse, inklusive værtsnavnet. Dette er nødvendigt, for at klyngeknuderne ved, hvordan de forbindes til hinanden. Det er en dårlig idé at bruge 0.0.0.0 som advertise_uri, det skal være den offentlige IP, ikke bind-socket. Uden det fungerer intet, så Cartridge vil simpelthen ikke lade dig starte en node med en forkert advertise_uri.

Nu hvor konfigurationen er klar, kan du starte processerne. Da den sædvanlige systemd-enhed ikke tillader mere end én proces at starte, installeres applikationer på patronen af ​​den såkaldte. instansierede enheder, der fungerer som dette:

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

I konfigurationen specificerede vi den HTTP-port, som Cartridge betjener webgrænsefladen på - 8080. Lad os gå til det og se:

Tarantool Cartridge: Lua backend-skæring i tre linjer

Vi ser, at processerne, selvom de kører, endnu ikke er konfigureret. Patronen ved endnu ikke, hvem der skal replikere med hvem og kan ikke træffe en beslutning på egen hånd, så den venter på vores handlinger. Og vi har ikke meget valg: livet for en ny klynge begynder med konfigurationen af ​​den første knude. Så føjer vi resten til klyngen, tildeler roller til dem, og på dette tidspunkt kan implementeringen anses for at være afsluttet.

Skænk et glas af din yndlingsdrink og slap af efter en lang arbejdsuge. Applikationen kan betjenes.

Tarantool Cartridge: Lua backend-skæring i tre linjer

Resultaterne af

Og hvad med resultaterne? Prøv, brug, giv feedback, start billetter på github.

RЎSЃS <P "RєRё

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

[2] Hvordan vi implementerede kernen i Alfa-Banks investeringsforretning baseret på Tarantool

[3] Ny generation af faktureringsarkitektur: transformation med overgangen til Tarantool

[4] SWIM - klyngeopbygningsprotokol

[5] GitHub - tarantool/patron-cli

[6] GitHub - tarantool/patron

Kilde: www.habr.com

Tilføj en kommentar