
Vi har Tarantool hos Mail.ru Group â en applikationsserver skrevet i Lua, som ogsĂ„ fungerer som en database (eller omvendt?). Den er hurtig og fantastisk, men kapaciteten pĂ„ en enkelt server er stadig ikke ubegribelig. Vertikal skalerbarhed er heller ikke en kur mod alt, sĂ„ Tarantool har vĂŠrktĂžjer til horisontal skalerbarhed â modulet vshard. . Det giver mulighed for at shardere data pĂ„ flere servere, men det krĂŠver lidt arbejde at fĂ„ det konfigureret og integrere forretningslogikken.
Gode nyheder: Vi har lavet mange fejl (for eksempel , ) og har udviklet endnu et framework, som gĂžr det betydeligt lettere at lĂžse dette problem.
â er et nyt framework til udvikling af komplekse distribuerede systemer. Det tillader at fokusere pĂ„ at skrive forretningslogik i stedet for at lĂžse infrastrukturelle problemer. Nedenfor vil jeg forklare, hvordan dette framework fungerer, og hvordan man bruger det til at skrive distribuerede tjenester.
Hvad er egentlig problemet?
Vi har Tarantool, vi har vshard â hvad mere kan man Ăžnske?
For det fÞrste handler det om bekvemmelighed. Konfigurationen af vshard sÊttes op via Lua-tabeller. For at et distribueret system bestÄende af flere Tarantool-processer kan fungere korrekt, skal konfigurationen vÊre ens overalt. Ingen Þnsker at gÞre dette manuelt. Derfor benyttes forskellige scripts, Ansible, deploymentsystemer.
Cartridge hÄndterer selv konfigurationen af vshard, baseret pÄ sin egen distribuerede konfiguration. I bund og grund er det en simpel YAML-fil, hvis kopi opbevares i hver Tarantool-instans. Forenklingen ligger i, at frameworket selv overvÄger sin konfiguration og sikrer, at den er ens overalt.
For det andet handler det igen om bekvemmelighed. Vshard-konfigurationen har ingen relation til udviklingen af forretningslogik og distraherer programmÞren fra arbejdet. NÄr vi diskuterer arkitekturen for et givent projekt, drejer det sig oftest om individuelle komponenter og deres interaktion. Det er for tidligt at tÊnke pÄ at starte et cluster med 3 datacentre.
Vi har gentagne gange arbejdet med disse problemer, og pÄ et tidspunkt fandt vi en tilgang, der gÞr arbejdet med applikationen mere ligetil i hele dens livscyklus: oprettelse, udvikling, test, CI/CD, vedligeholdelse.
Cartridge introducerer begrebet rolle for hver proces i Tarantool. Roller er det koncept, der gÞr det muligt for udvikleren at fokusere pÄ at skrive kode. Alle roller i projektet kan kÞres pÄ en enkelt instans af Tarantool, og til testformÄl vil det vÊre tilstrÊkkeligt.
Hovedfunktioner i Tarantool Cartridge:
- automatiseret orkestrering af klyngen;
- udvidelse af applikationens funktionalitet med nye roller;
- applikationsskabelon til udvikling og distribution;
- indbygget automatisk sharding;
- integration med testframeworket Luatest;
- styring af klyngen via WebUI og API;
- pakke- og deployvĂŠrktĂžjer.
Hej, verden!
Jeg kan ikke vente med at vise frameworket, sÄ vi sparer arkitekturdiskussionen til senere og starter med det enkle. Hvis vi antager, at Tarantool allerede er installeret, er der kun tilbage at gÞre:
$ tarantoolctl rocks install cartridge-cli
$ export PATH=$PWD/.rocks/bin/:$PATHDisse to kommandoer vil installere kommandolinjevĂŠrktĂžjer og gĂžre det muligt at oprette din fĂžrste applikation fra en skabelon:
$ cartridge create --name myappOg her 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-repositorium med en fĂŠrdig "Hello, World!" applikation. Lad os straks prĂžve at kĂžre den, efter at vi har installeret afhĂŠngigheder (inklusive selve frameworket):
$ tarantoolctl rocks make
$ ./init.lua --http-port 8080SÄ vi har en node af den kommende sharded applikation kÞrende. Den nysgerrige bruger kan straks Äbne webgrÊnsefladen, konfigurere klyngen fra en node med musen og nyde resultatet, men glÊden mÄ vente. Indtil videre kan applikationen ikke gÞre noget nyttigt, sÄ jeg vil tale om deployment senere, men nu er det tid til at skrive kode.
Udvikling af applikationer
Forestil dig, at vi designer et projekt, der skal modtage data, gemme dem og generere en rapport én gang om dagen.

Vi begynder at skitsere diagrammet og placerer tre komponenter: gateway, storage og scheduler. Vi arbejder videre med arkitekturen. Da vi bruger vshard som lager, tilfÞjer vi vshard-router og vshard-storage til diagrammet. Hverken gateway eller scheduler vil fÄ adgang til lageret direkte; det vil routeren gÞre, som er netop hvad den er til.

Denne skema afspejler stadig ikke helt prĂŠcist, hvad vi skal skabe i projektet, fordi komponenterne ser abstrakte ud. Vi skal ogsĂ„ se pĂ„, hvordan det projiceres pĂ„ den virkelige Tarantool â lad os gruppere vores komponenter efter processer.

Det giver ikke mening at holde vshard-router og gateway pÄ separate instanser. Hvorfor skal vi gÄ gennem netvÊrket unÞdigt, nÄr det alligevel er en del af routerens opgaver? De bÞr kÞre inden for én proces. Det vil sige, at bÄde gateway, vshard.router.cfg initialiseres i én proces, og lad dem interagere lokalt.
I designfasen var det praktisk at arbejde med tre komponenter, men som udvikler Ăžnsker jeg ikke at bekymre mig om at kĂžre tre instanser af Tarantool, mens jeg skriver kode. Jeg har brug for at kĂžre tests og sikre, at jeg har skrevet gateway korrekt. Eller mĂ„ske vil jeg demonstrere en funktion for mine kolleger. Hvorfor skulle jeg bĂžvle med at implementere tre instanser? SĂ„ledes blev konceptet med roller fĂždt. En rolle er et almindeligt Lua-modul, hvis livscyklus styres af Cartridge. I dette eksempel er der fire â gateway, router, storage, scheduler. I et andet projekt kan der vĂŠre flere. Alle roller kan kĂžres i Ă©n proces, og det vil vĂŠre tilstrĂŠkkeligt.

NÄr det kommer til implementering i staging eller produktion, vil vi tildele hver Tarantool-proces sit eget sÊt roller afhÊngigt af den hardware, der er tilgÊngelig:

Topology Management
Oplysninger om, hvor hvilke roller der kĂžrer, skal opbevares et sted. Og dette 'et sted' er den distribuerede konfiguration, som jeg allerede har nĂŠvnt tidligere. Det vigtigste i den er cluster-topologien. Her ses 3 replikationsgrupper med 5 Tarantool-processer:

Vi Þnsker ikke at miste data, sÄ vi tager stor omhu med informationerne om de kÞrende processer. Cartridge overvÄger konfigurationen ved hjÊlp af en to-faset commit. SÄ snart vi Þnsker at opdatere konfigurationen, kontrollerer den fÞrst tilgÊngeligheden af alle instanser og deres parathed til at modtage den nye konfiguration. Herefter anvendes konfigurationen i anden fase. Dermed, selv hvis en instans midlertidigt er utilgÊngelig, vil der ikke ske noget alvorligt. Konfigurationen vil blot ikke blive anvendt, og du vil pÄ forhÄnd se en fejl.
I sektionen for topologi er der ogsÄ angivet en vigtig parameter, nemlig lederen af hver replikationsgruppe. Normalt er dette den instans, hvortil der skrives. De andre er ofte read-only, 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 visse operationer, der under ingen omstÊndigheder bÞr udfÞres to gange. Dette for det formÄl har vi en leder-flag.

Rolles liv
For at en abstrakt rolle kan eksistere i en sÄdan arkitektur, skal rammevÊrket pÄ en eller anden mÄde styre dem. Naturligvis sker styringen uden at genstarte Tarantool-processen. Der findes 4 callbacks til rolleadministration. Cartridge vil selv kalde dem afhÊngigt af det, der er skrevet i den distribuerede konfiguration, og dermed anvende konfigurationen til de specifikke roller.
function init()
function validate_config()
function apply_config()
function stop()
Hver rolle har en funktion init. Den kaldes én gang enten ved aktivering af rollen eller ved genstart af Tarantool. Her kan man eksempelvis nemt initialisere box.space.create, eller scheduler kan starte en eller anden baggrundsfiber, som udfÞrer opgaver med bestemte tidsintervaller.
Ăn funktion init kan vĂŠre utilstrĂŠkkelig. Cartridge tillader roller at bruge den distribuerede konfiguration, som den benytter til at gemme topologien. Vi kan i denne konfiguration erklĂŠre en ny sektion og opbevare et fragment af forretningskonfigurationen i den. I mit eksempel kan dette vĂŠre dataskemaet eller indstillingerne for tidsplanen til rollen scheduler.
Klyngen kalder validate_config og apply_config ved hver Êndring af den distribuerede konfiguration. NÄr konfigurationen anvendes med to-phased commit, kontrollerer klyngen, at hver rolle er klar til at acceptere denne nye konfiguration, og informerer brugeren om en fejl, hvis nÞdvendigt. NÄr alle er enige om, at konfigurationen er normal, udfÞres apply_config.
Derudover har rollerne en metode stop, der er nÞdvendig for at rydde op efter rolleaktivitetens resultater. Hvis vi siger, at scheduler ikke lÊngere er nÞdvendig pÄ denne server, kan den stoppe de fibere, som den har startet med hjÊlp af init.
Roller kan interagere med hinanden. Vi er vant til at skrive funktionsopkald i Lua, men det kan ske, at den rolle, vi har brug for, ikke er til stede i den pÄgÊldende proces. For at lette netvÊrksanmodninger bruger vi et hjÊlpmodule, rpc (remote procedure call), der er bygget pÄ standard netbox'en indbygget i Tarantool. Dette kan vÊre nyttigt, hvis, for eksempel, din gateway Þnsker at anmode scheduler om at udfÞre en opgave lige nu i stedet for at vente i et dÞgn.
En anden vigtig faktor er at sikre fejltolerance. Til overvÄgning af sundhed i Cartridge bruges SWIM-protokollen. Kort sagt, sÄ udveksler processerne "rygter" med hinanden via UDP - hver proces fortÊller sine naboer de seneste nyheder, og de svarer. Hvis der pludselig ikke kom et svar, begynder Tarantool at mistÊnke, at der er noget galt, og efter et stykke tid erklÊrer den dÞden og begynder at fortÊlle alle i nÊrheden denne nyhed.

Baseret pÄ denne protokol organiserer Cartridge automatisk hÄndtering af fejl. Hver proces overvÄger sit miljÞ, og hvis lederen pludselig holder op med at svare, kan en replica tage hans rolle pÄ sig, og Cartridge konfigurerer de kÞrende roller derefter.

Her mÄ man vÊre forsigtig, fordi hyppig skift frem og tilbage kan fÞre til datakonflikter under replikation. Det er selvfÞlgelig ikke en god idé at tÊnde for automatisk failover uden videre. Man skal have en klar forstÄelse af, hvad der foregÄr, og vÊre sikre pÄ, at replikationen ikke gÄr i stykker, nÄr lederen genopstÄr og fÄr sin krone tilbage.
Fra alt, hvad der er sagt, kan man fÄ indtrykket af, at rollerne ligner mikrotjenester. PÄ en mÄde er de det, kun som moduler inden for Tarantool-processer. Men der er ogsÄ en rÊkke vÊsentlige forskelle. For det fÞrste skal alle roller i projektet leve i én kodebase. Og alle Tarantool-processer skal startes fra én kodebase for at undgÄ overraskelser som de situationer, hvor vi prÞver at initialisere scheduler, men den simpelthen ikke er til stede. Desuden bÞr der ikke vÊre forskelle i kodeversioner, fordi systemets adfÊrd i sÄdan en situation er meget svÊr at forudsige og fejlfinde.
I modsĂŠtning til Docker kan vi ikke bare tage et 'billede' af en rolle, tage det til en anden maskine og starte det der. Vores roller er ikke sĂ„ isolerede som Docker-containere. Vi kan heller ikke kĂžre to identiske roller pĂ„ Ă©n instans. En rolle eksisterer enten, eller den gĂžr ikke; pĂ„ sin vis er det et singleton. Og for det tredje skal rollerne inden for hele replikationsgruppen vĂŠre ens, for ellers ville det vĂŠre absurd â dataene er ens, men konfigurationen er forskellig.
UdrulningsvĂŠrktĂžjer
Jeg lovede at vise, hvordan Cartridge hjĂŠlper med at udrulle applikationer. For at gĂžre livet lettere for alle pakker rammevĂŠrket RPM-pakker:
$ cartridge pack rpm myapp -- pakker for os .\/myapp-0.1.0-1.rpm
$ sudo yum install .\/myapp-0.1.0-1.rpmDen installerede pakke indeholder nÊsten alt nÞdvendigt: bÄde applikationen og de installerede Lua-afhÊngigheder. Tarantool vil ogsÄ blive installeret pÄ serveren som en afhÊngighed af RPM-pakken, og vores service er klar til at starte. Dette gÞres gennem systemd, men fÞrst skal der skrives en smule konfiguration. Mindst skal vi angive URI for hver proces. Tre eksempler er tilstrÊkkelige.
$ 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}
CONFIGHer er der en interessant nuance. I stedet for kun at angive porten for binÊrprotokollen, angiver vi den offentlige adresse for processen helt, inklusive hostname. Dette er nÞdvendigt for, at cluster-noderne ved, hvordan de skal forbinde med hinanden. Det er en dÄrlig idé at bruge adressen 0.0.0.0 som advertise_uri; det skal vÊre en ekstern IP-adresse, ikke en bind-socket. Uden dette vil intet fungere, og derfor vil Cartridge simpelthen ikke tillade at starte en node med et forkert advertise_uri.
Nu hvor konfigurationen er klar, kan processerne startes. Da en almindelig systemd-enhed ikke tillader at starte mere end én proces, bruger applikationerne pÄ Cartridge sÄkaldte instantiated-enheder, som fungerer sÄdan her:
$ sudo systemctl start myapp@router
$ sudo systemctl start myapp@storage_A
$ sudo systemctl start myapp@storage_BI konfigurationen har vi angivet HTTP-porten, hvor Cartridge leverer webgrĂŠnsefladen â 8080. Lad os gĂ„ ind pĂ„ den og se:

Vi ser, at processerne er startet, men endnu ikke konfigureret. Patronen ved endnu ikke, hvem der skal replikere med hvem og kan ikke trÊffe beslutninger selv, derfor venter den pÄ vores handlinger. Vores valg er ikke stort: Livet for det nye cluster starter med konfigurationen af den fÞrste node. Derefter tilfÞjer vi de andre til clusteren, tildeler dem roller, og sÄ kan implementeringen betragtes som vellykket.
Lad os hĂŠlde en kop af vores yndlingsdrik og slappe af efter en lang arbejdsuge. Applikationen kan nu bruges.

Resumé
Hvad med resultaterne? PrÞv det, brug det, giv feedback, opret tickets pÄ GitHub.
Links
[1]
[2]
[3]
[4]
[5]
[6]
Kilde: habr.com
