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

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

Hos Mail.ru Group har vi Tarantool - dette er en applikasjonsserver i Lua, som også fungerer som en database (eller omvendt?). Det er raskt og kult, men mulighetene til én server er fortsatt ikke ubegrensede. Vertikal skalering er heller ikke et universalmiddel, så Tarantool har verktøy for horisontal skalering - vshard-modulen [1]. Det lar deg dele data på tvers av flere servere, men du må tukle med det for å sette det opp og legge ved forretningslogikken.

Gode ​​nyheter: vi har samlet noen store skudd (f.eks [2], [3]) og laget et annet rammeverk som vil forenkle løsningen på dette problemet betydelig.

Tarantool-kassett er et nytt rammeverk for å utvikle komplekse distribuerte systemer. Den lar deg fokusere på å skrive forretningslogikk i stedet for å løse infrastrukturproblemer. Under kuttet vil jeg fortelle deg hvordan dette rammeverket fungerer og hvordan du skriver distribuerte tjenester ved å bruke det.

Hva er egentlig problemet?

Vi har en tarantella, vi har vshard - hva mer kan du ønske deg?

For det første er det et spørsmål om bekvemmelighet. Vshard-konfigurasjonen konfigureres gjennom Lua-tabeller. For at et distribuert system med flere Tarantool-prosesser skal fungere riktig, må konfigurasjonen være den samme overalt. Ingen ønsker å gjøre dette manuelt. Derfor brukes alle slags skript, Ansible og distribusjonssystemer.

Cartridge selv administrerer vshard-konfigurasjonen, den gjør dette basert på sin egen distribuert konfigurasjon. Det er egentlig en enkel YAML-fil, en kopi av denne lagres i hver Tarantool-forekomst. Forenklingen er at rammeverket selv overvåker konfigurasjonen og sørger for at den er lik overalt.

For det andre er det igjen et spørsmål om bekvemmelighet. Vshard-konfigurasjonen har ingenting å gjøre med utviklingen av forretningslogikk og distraherer bare programmereren fra arbeidet hans. Når vi diskuterer arkitekturen til et prosjekt, snakker vi oftest om enkeltkomponenter og deres interaksjon. Det er for tidlig å tenke på å rulle ut en klynge til 3 datasentre.

Vi løste disse problemene om og om igjen, og på et tidspunkt klarte vi å utvikle en tilnærming som forenklet arbeidet med applikasjonen gjennom hele livssyklusen: opprettelse, utvikling, testing, CI/CD, vedlikehold.

Cartridge introduserer konseptet med en rolle for hver Tarantool-prosess. Roller er et konsept som lar en utvikler fokusere på å skrive kode. Alle tilgjengelige roller i prosjektet kan kjøres på én Tarantool-instans, og dette vil være nok for tester.

Nøkkelfunksjoner til Tarantool Cartridge:

  • automatisert klyngeorkestrering;
  • utvide funksjonaliteten til applikasjonen ved å bruke nye roller;
  • applikasjonsmal for utvikling og distribusjon;
  • innebygd automatisk skjæring;
  • integrasjon med Luatest testramme;
  • klyngeadministrasjon ved hjelp av WebUI og API;
  • pakke- og distribusjonsverktøy.

Hei Verden!

Jeg gleder meg til å vise selve rammeverket, så vi lar historien om arkitekturen ligge til senere og starter med noe enkelt. Hvis vi antar at selve Tarantool allerede er installert, gjenstår det bare å gjøre

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

Disse to kommandoene vil installere kommandolinjeverktøyene og lar deg lage din første applikasjon fra malen:

$ cartridge create --name myapp

Og dette er hva 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 et ferdig "Hello, World!" applikasjon. La oss prøve å kjøre den med en gang, etter å ha installert avhengighetene tidligere (inkludert selve rammeverket):

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

Så vi har én node som kjører for den fremtidige sønderdelte applikasjonen. En nysgjerrig lekmann kan umiddelbart åpne nettgrensesnittet, konfigurere en klynge med én node med musen og nyte resultatet, men det er for tidlig å glede seg. Så langt kan ikke applikasjonen gjøre noe nyttig, så jeg vil fortelle deg om distribusjon senere, men nå er det på tide å skrive kode.

Programutvikling

Tenk deg at vi designer et prosjekt som må motta data, lagre dem og bygge en rapport en gang om dagen.

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

Vi begynner å tegne et diagram og plasserer tre komponenter på det: gateway, lagring og planlegger. Vi jobber videre med arkitekturen. Siden vi bruker vshard som lagring, legger vi til vshard-ruter og vshard-lagring til ordningen. Verken gatewayen eller planleggeren vil ha direkte tilgang til lagringen; det er det ruteren er til for, det er det den ble laget for.

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

Dette diagrammet representerer fortsatt ikke akkurat det vi skal bygge i prosjektet fordi komponentene ser abstrakte ut. Vi må fortsatt se hvordan dette vil bli projisert på det virkelige Tarantool - la oss gruppere komponentene våre etter prosess.

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

Det er liten vits i å beholde vshard-router og gateway på separate instanser. Hvorfor må vi surfe på nettverket igjen hvis dette allerede er ruterens ansvar? De må kjøres innenfor samme prosess. Det vil si at både gateway og vshard.router.cfg initialiseres i én prosess, og lar dem samhandle lokalt.

På designstadiet var det praktisk å jobbe med tre komponenter, men jeg som utvikler, mens jeg skriver koden, ønsker ikke å tenke på å lansere tre forekomster av Tarnatool. Jeg må kjøre tester og sjekke at jeg skrev gatewayen riktig. Eller kanskje jeg vil demonstrere en funksjon for mine kolleger. Hvorfor skal jeg gå gjennom bryet med å distribuere tre kopier? Slik ble begrepet roller født. En rolle er en vanlig luash-modul hvis livssyklus administreres av Cartridge. I dette eksemplet er det fire av dem - gateway, ruter, lagring, planlegger. Det kan være mer i et annet prosjekt. Alle roller kan kjøres i én prosess, og dette vil være nok.

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

Og når det gjelder distribusjon til iscenesettelse eller produksjon, vil vi tildele hver Tarantool-prosess sitt eget sett med roller avhengig av maskinvarekapasiteten:

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

Topologistyring

Informasjon om hvor hvilke roller kjører må lagres et sted. Og dette "et sted" er den distribuerte konfigurasjonen, som jeg allerede har nevnt ovenfor. Det viktigste med det er klyngetopologien. Her er 3 replikeringsgrupper med 5 Tarantool-prosesser:

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

Vi ønsker ikke å miste data, så vi behandler informasjon om løpende prosesser med forsiktighet. Cartridge holder styr på konfigurasjonen ved hjelp av en to-fase commit. Når vi ønsker å oppdatere konfigurasjonen, sjekker den først at alle forekomster er tilgjengelige og klare til å godta den nye konfigurasjonen. Etter dette bruker den andre fasen konfigurasjonen. Så selv om en kopi viser seg å være midlertidig utilgjengelig, vil det ikke skje noe galt. Konfigurasjonen vil ganske enkelt ikke bli brukt, og du vil se en feil på forhånd.

Også i topologidelen er en så viktig parameter som lederen for hver replikasjonsgruppe angitt. Vanligvis er dette kopien som blir tatt opp. Resten er oftest skrivebeskyttet, selv om det kan være unntak. Noen ganger er modige utviklere ikke redde for konflikter og kan skrive data til flere kopier parallelt, men det er noen operasjoner som uansett ikke bør utføres to ganger. For dette er det et tegn på en leder.

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

Rollelivet

For at en abstrakt rolle skal eksistere i en slik arkitektur, må rammeverket håndtere dem på en eller annen måte. Naturligvis skjer kontroll uten å starte Tarantool-prosessen på nytt. Det er 4 tilbakeringinger for å administrere roller. Cartridge selv vil kalle dem avhengig av hva som er skrevet i den distribuerte konfigurasjonen, og dermed bruke konfigurasjonen til spesifikke roller.

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

Hver rolle har en funksjon init. Den kalles en gang enten når rollen er aktivert eller når Tarantool startes på nytt. Det er praktisk der, for eksempel, å initialisere box.space.create, eller planleggeren kan starte noe bakgrunnsfiber som vil utføre arbeid med bestemte tidsintervaller.

En funksjon init er kanskje ikke nok. Kassett lar roller dra nytte av den distribuerte konfigurasjonen den bruker for å lagre topologien. Vi kan deklarere en ny seksjon i samme konfigurasjon og lagre et fragment av forretningskonfigurasjonen i den. I mitt eksempel kan dette være et dataskjema eller tidsplaninnstillinger for planleggerrollen.

Klyngeanrop validate_config и apply_config hver gang den distribuerte konfigurasjonen endres. Når en konfigurasjon brukes av en to-fase commit, sjekker klyngen at hver rolle er klar til å akseptere denne nye konfigurasjonen, og om nødvendig rapporterer en feil til brukeren. Når alle er enige om at konfigurasjonen er normal, vil den apply_config.

Også roller har en metode stop, som er nødvendig for å rydde opp i rollens produksjon. Hvis vi sier at planleggeren ikke lenger er nødvendig på denne serveren, kan den stoppe de fibrene den startet med init.

Roller kan samhandle med hverandre. Vi er vant til å skrive funksjonskall i Lua, men det kan hende at en gitt prosess ikke har den rollen vi trenger. For å forenkle samtaler over nettverket bruker vi rpc (remote procedure call) hjelpemodulen, som er bygget på basis av standard nettboks innebygd i Tarantool. Dette kan være nyttig hvis for eksempel gatewayen din ønsker å direkte be planleggeren om å gjøre jobben akkurat nå, i stedet for å vente en dag.

Et annet viktig poeng er å sikre feiltoleranse. Kassett bruker SWIM-protokollen for å overvåke helse [4]. Kort sagt, prosesser utveksler "rykter" med hverandre over UDP - hver prosess forteller sine naboer siste nytt, og de svarer. Hvis svaret plutselig ikke kommer, begynner Tarantool å mistenke at noe er galt, og etter en stund resiterer den døden og begynner å fortelle alle rundt denne nyheten.

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

Basert på denne protokollen organiserer Cartridge automatisk feilbehandling. Hver prosess overvåker sitt miljø, og hvis lederen plutselig slutter å svare, kan replikaen ta over rollen sin, og Cartridge konfigurerer de løpende rollene deretter.

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

Du må være forsiktig her, fordi hyppig veksling frem og tilbake kan føre til datakonflikter under replikering. Selvfølgelig bør du ikke aktivere automatisk failover tilfeldig. Vi må tydelig forstå hva som skjer og være sikre på at replikering ikke vil bryte etter at lederen er gjenopprettet og kronen er returnert til ham.

Fra alt dette kan du få følelsen av at roller ligner på mikrotjenester. På en måte er de nettopp det, bare som moduler inne i Tarantool-prosesser. Men det er også en rekke grunnleggende forskjeller. For det første må alle prosjektroller leve i samme kodebase. Og alle Tarantool-prosesser bør startes fra samme kodebase, slik at det ikke er noen overraskelser som når vi prøver å initialisere planleggeren, men den eksisterer rett og slett ikke. Du bør heller ikke tillate forskjeller i kodeversjoner, fordi oppførselen til systemet i en slik situasjon er svært vanskelig å forutsi og feilsøke.

I motsetning til Docker kan vi ikke bare ta et rolle "image", ta det til en annen maskin og kjøre det der. Våre roller er ikke like isolerte som Docker-containere. Vi kan heller ikke kjøre to identiske roller på én instans. En rolle eksisterer enten eller ikke; på en måte er det en singleton. Og for det tredje må rollene være de samme innenfor hele replikeringsgruppen, for ellers ville det vært absurd – dataene er de samme, men konfigurasjonen er annerledes.

Implementeringsverktøy

Jeg lovet å vise hvordan Cartridge hjelper med å distribuere applikasjoner. For å gjøre livet enklere for andre, pakker RPM-pakker med rammeverk:

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

Den installerte pakken inneholder nesten alt du trenger: både applikasjonen og de installerte avhengighetene. Tarantool kommer også på serveren som en avhengighet av RPM-pakken, og tjenesten vår er klar til å starte. Dette gjøres gjennom systemd, men først må du skrive litt konfigurasjon. Angi som et minimum URI for hver prosess. Tre er nok for 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

Det er en interessant nyanse her. I stedet for å spesifisere bare den binære protokollporten, spesifiserer vi hele den offentlige adressen til prosessen inkludert vertsnavnet. Dette er nødvendig slik at klyngenodene vet hvordan de skal kobles til hverandre. Det er en dårlig idé å bruke 0.0.0.0 som advertise_uri-adressen; det bør være en ekstern IP-adresse, ikke en socket-binding. Uten det vil ingenting fungere, så Cartridge lar deg rett og slett ikke starte en node med feil advertise_uri.

Nå som konfigurasjonen er klar, kan du starte prosessene. Siden en vanlig systemd enhet ikke lar mer enn én prosess starte, installeres applikasjoner på kassetten av den såkalte. instansierte enheter som fungerer slik:

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

I konfigurasjonen spesifiserte vi HTTP-porten som Cartridge betjener webgrensesnittet på - 8080. La oss gå til den og ta en titt:

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

Vi ser at selv om prosessene kjører, er de ennå ikke konfigurert. Patronen vet ennå ikke hvem som skal replikere med hvem og kan ikke ta en avgjørelse på egen hånd, så den venter på handlingene våre. Men vi har ikke mye valg: livet til en ny klynge begynner med konfigurasjonen av den første noden. Deretter legger vi de andre til klyngen, tildeler dem roller, og på dette tidspunktet kan distribusjonen anses som fullført.

La oss helle et glass av favorittdrikken din og slappe av etter en lang arbeidsuke. Applikasjonen kan brukes.

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

Resultater av

Hva er resultatene? Prøv det, bruk det, gi tilbakemelding, lag billetter på Github.

referanser

[1] Tarantool » 2.2 » Referanse » Rocks-referanse » Modul vshard

[2] Hvordan vi implementerte kjernen i Alfa-Banks investeringsvirksomhet basert på Tarantool

[3] Ny generasjons faktureringsarkitektur: transformasjon med overgangen til Tarantool

[4] SWIM - klyngekonstruksjonsprotokoll

[5] GitHub - tarantool/cartridge-cli

[6] GitHub - tarantverktøy/patron

Kilde: www.habr.com

Legg til en kommentar