Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

PĂ„ Mail.ru Group har vi Tarantool — en applikationsserver i Lua, som Ă€ven fungerar som en databas (eller tvĂ€rtom?). Den Ă€r snabb och fantastisk, men kapaciteten hos en enda server Ă€r trots allt inte oĂ€ndlig. Vertikal skalning Ă€r inte heller en universallösning, sĂ„ i Tarantool finns det verktyg för horisontell skalning — modulen vshard. [1]. Den lĂ„ter dig shardera data över flera servrar, men det krĂ€vs en del arbete för att stĂ€lla in det och koppla pĂ„ affĂ€rslogiken.

Bra nyheter: vi har lÀrt oss av vÄra misstag (till exempel [2], [3]) och utvecklat Ànnu ett ramverk som avsevÀrt förenklar lösningen pÄ detta problem.

Tarantool Cartridge — Ă€r ett nytt ramverk för utveckling av komplexa distribuerade system. Det gör att du kan fokusera pĂ„ att skriva affĂ€rslogik istĂ€llet för att lösa infrastrukturella problem. Under rubriken berĂ€ttar jag hur detta ramverk fungerar och hur man skriver distribuerade tjĂ€nster med dess hjĂ€lp.

Men vad Àr egentligen problemet?

Vi har en tarantula, vi har vshard — vad mer kan man önska?

För det första handlar det om bekvÀmlighet. Vshard-konfigurationen stÀlls in via Lua-tabeller. För att ett distribuerat system bestÄende av flera Tarantool-processer ska fungera korrekt mÄste konfigurationen vara densamma överallt. Ingen vill hantera detta manuellt. DÀrför anvÀnds alla möjliga skript, Ansible, och distributionssystem.

Cartridge hanterar sjÀlv vshard-konfigurationen, och gör det baserat pÄ sin egen distribuerade konfiguration. I princip Àr det en enkel YAML-fil, vars kopia lagras i varje instans av Tarantool. Förenklingen ligger i att ramverket sjÀlv övervakar sin konfiguration och ser till att den Àr densamma överallt.

För det andra handlar det Äterigen om bekvÀmlighet. Vshard-konfigurationen har inget att göra med utvecklingen av affÀrslogik och distraherar bara programmeraren frÄn arbetet. NÀr vi diskuterar arkitekturen för ett visst projekt handlar det oftast om enskilda komponenter och deras interaktion. Det Àr för tidigt att börja tÀnka pÄ distributionen av klustret över 3 datacenter.

Vi har stött pÄ dessa problem gÄng pÄ gÄng, och vid nÄgot tillfÀlle lyckades vi utveckla en metod som förenklar arbetet med applikationen genom hela dess livscykel: skapande, utveckling, testning, CI/CD, underhÄll.

Cartridge introducerar begreppet roller för varje process i Tarantool. Roller Àr ett koncept som gör det möjligt för utvecklaren att fokusera pÄ att skriva kod. Alla roller som finns i projektet kan köras pÄ en enda instans av Tarantool, och för tester rÀcker detta.

Huvudfunktioner i Tarantool Cartridge:

  • automatiserad orkestrering av klustret;
  • utökning av applikationens funktionalitet med nya roller;
  • applikationsmall för utveckling och distribution;
  • inbyggd automatisk sharding;
  • integration med testramverket Luatest;
  • hantering av klustret med hjĂ€lp av WebUI och API;
  • verktyg för paketering och distribution.

Hello, World!

Jag ser fram emot att visa sjÀlva ramverket, sÄ vi sparar beskrivningen av arkitekturen till senare och börjar enkelt. Om vi antar att Tarantool redan Àr installerat, ÄterstÄr endast att göra

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

Dessa tvÄ kommandon installerar kommandoradsverktyg och lÄter dig skapa din första applikation frÄn en mall:

$ cartridge create --name myapp

Och hÀr Àr vad 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/

Detta Àr ett git-repo med en fÀrdig "Hello, World!"-applikation. LÄt oss omedelbart försöka köra den, efter att ha installerat beroenden (inklusive sjÀlva ramverket):

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

SÄ, vi har en nod av den framtida sharded-applikationen igÄng. En nyfiken anvÀndare kan omedelbart öppna webbingÄngen, konfigurera klustret frÄn en nod med musen och njuta av resultatet, men det Àr för tidigt att glÀdjas. Just nu kan applikationen inte göra nÄgot nyttigt, sÄ jag kommer att berÀtta om distribution senare, men nu Àr det dags att skriva kod.

Applikationsutveckling

TÀnk dig att vi designar ett projekt som ska ta emot data, spara dem och en gÄng om dagen generera en rapport.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Vi börjar skissa pÄ en schemat och placerar tre komponenter pÄ den: gateway, storage och scheduler. Vi gÄr vidare med arkitekturen. Eftersom vi anvÀnder vshard som lagring, lÀgger vi till vshard-router och vshard-storage i schemat. Varken gateway eller scheduler kommer att kontakta lagringssystemet direkt, det Àr dÀrför vi har en router, den Àr skapad just för det.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Denna schema Ă„terspeglar fortfarande inte riktigt vad vi ska skapa i projektet, eftersom komponenterna ser abstrakta ut. Vi behöver ocksĂ„ titta pĂ„ hur detta projiceras pĂ„ verklig Tarantool — vi grupperar vĂ„ra komponenter efter processer.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Att hÄlla vshard-router och gateway pÄ separata instanser ger ingen mening. Varför ska vi gÄ över nÀtverket om det redan ingÄr i routerens uppgifter? De bör köras inom samma process. Det vill sÀga att bÄde gateway och vshard.router.cfg initieras i en och samma process, sÄ att de kan interagera lokalt.

I designfasen var det praktiskt att arbeta med tre komponenter, men som utvecklare vill jag inte tĂ€nka pĂ„ att starta tre instanser av Tarantool medan jag skriver koden. Jag behöver köra tester och kontrollera att jag har skrivit gateway rĂ€tt. Eller kanske vill jag visa en funktion för kollegor. Varför ska jag plĂ„gas med att deployera tre instanser? DĂ€rför föddes konceptet med roller. En roll Ă€r en vanlig Lua-modul vars livscykel hanteras av Cartridge. I detta exempel finns det fyra — gateway, router, storage, scheduler. I ett annat projekt kan det finnas fler. Alla roller kan köras i samma process, och det rĂ€cker.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Och nÀr det kommer till deployering i staging eller produktion, kommer vi dÄ att tilldela varje Tarantool-process sin egen uppsÀttning roller beroende pÄ hÄrdvarufunktioner:

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

LÄt oss kombinera vÄra nya instanser i en replicaset

Information om var vilka roller Àr igÄng mÄste lagras nÄgonstans. Och detta "nÄgonstans" Àr den distribuerade konfigurationen som jag nÀmnde tidigare. Det viktigaste i den Àr klustrets topologi. HÀr visas 3 replikgrupper av 5 Tarantool-processer:

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Vi vill inte förlora data, sÄ vi Àr försiktiga med informationen om de aktiva processerna. Cartridge övervakar konfigurationen med hjÀlp av en tvÄfascommit. SÄ snart vi vill uppdatera konfigurationen kontrollerar den först tillgÀngligheten av alla instanser och deras beredskap att ta emot den nya konfigurationen. DÀrefter tillÀmpas konfigurationsfilen i den andra fasen. PÄ sÄ sÀtt, Àven om en instans tillfÀlligt Àr otillgÀnglig, hÀnder ingenting allvarligt. Konfigurationen tillÀmpas helt enkelt inte och du kommer att se felet i förvÀg.

I sektionen för topologi anges ocksÄ en sÄ viktig parameter som ledaren för varje replikationsgrupp. Vanligtvis Àr detta den instans dÀr skrivningar görs. De andra Àr oftast read-only, Àven om det hÀr kan finnas undantag. Ibland rÀds modiga utvecklare inte för konflikter och kan skriva data pÄ flera repliker parallellt, men det finns vissa operationer som under alla omstÀndigheter inte fÄr utföras tvÄ gÄnger. Det finns en indikator för ledaren.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Rollernas liv

För att en abstrakt roll ska kunna existera i en sÄdan arkitektur mÄste ramverket pÄ nÄgot sÀtt hantera dem. SjÀlvklart sker hanteringen utan att processerna i Tarantool startas om. Det finns 4 callbacks för att hantera roller. Cartridge kommer att anropa dem beroende pÄ vad som stÄr i dess distribuerade konfiguration, och applicera konfigurationen pÄ specifika roller.

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

Varje roll har en funktion init. Den anropas en gÄng antingen vid aktivering av rollen eller vid omstart av Tarantool. DÀr kan man enkelt initialisera box.space.create, eller sÄ kan schemalÀggaren köra en bakgrundsfiber som kommer att utföra arbete med vissa tidsintervall.

En funktion init kan vara otillrÀcklig. Cartridge gör det möjligt för roller att anvÀnda den distribuerade konfigurationen som den anvÀnder för att lagra topologin. Vi kan deklarera en ny sektion i denna konfiguration och lagra en del av affÀrskonfigurationen dÀr. I mitt exempel kan detta vara en databasstruktur eller instÀllningar för schemalÀggaren.

Klusteret anropar validate_config och apply_config vid varje förÀndring av den distribuerade konfigurationen. NÀr konfigurationen appliceras med tvÄfaskommitté, kontrollerar klusteret att varje roll Àr redo att ta emot denna nya konfiguration och meddelar vid behov anvÀndaren om ett fel. NÀr alla Àr överens om att konfigurationen Àr normal, utförs apply_config.

Rollerna har ocksÄ en metod stop, som behövs för att rensa upp resultatet av rollens livscykel. Om vi sÀger att schemalÀggaren inte lÀngre behövs pÄ denna server kan den stoppa de fiber som den startade med hjÀlp av init.

Roller kan interagera med varandra. Vi Àr vana vid att skriva funktionsanrop i Lua, men det kan hÀnda att den roll vi behöver inte Àr tillgÀnglig i processen. För att underlÀtta nÀtverksanrop anvÀnder vi hjÀlpmodulen rpc (remote procedure call), som bygger pÄ den standardiserade netbox som Àr integrerad i Tarantool. Detta kan vara anvÀndbart om till exempel din gateway vill be scheduler att utföra en uppgift omedelbart istÀllet för att vÀnta i ett dygn.

En annan viktig sak Ă€r att sĂ€kerstĂ€lla fel tolerans. För övervakning av hĂ€lsan anvĂ€nds protokollet SWIM i Cartridge. [4]. Om vi ska sammanfatta, sĂ„ utbyter processerna "rykten" med varandra över UDP – varje process berĂ€ttar för sina grannar det senaste nytt, och de svarar. Om ett svar plötsligt inte kommer, börjar Tarantool misstĂ€nka att nĂ„got Ă€r fel, och efter en tid deklarerar det dödsfall och börjar sprida den nyheten till omgivningen.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Baserat pÄ detta protokoll organiserar Cartridge automatisk hantering av fel. Varje process övervakar sin omgivning, och om ledaren plötsligt slutar svara kan en replik ta hans roll, och Cartridge konfigurerar de aktiva rollerna pÄ lÀmpligt sÀtt.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

HÀr behöver man vara försiktig, eftersom frekvent vÀxling fram och tillbaka kan leda till datakonflikter vid replikering. Att aktivera automatisk failover utan noggrant övervÀgande Àr förstÄs inte att rekommendera. Man mÄste ha en tydlig förstÄelse för vad som pÄgÄr och vara sÀkra pÄ att replikeringen inte kommer att gÄ sönder efter att ledaren ÄterhÀmtat sig och fÄtt kronan tillbaka.

Av allt som sagts kan man fÄ intrycket att rollerna liknar mikrotjÀnster. PÄ ett sÀtt Àr de verkligen det, bara som moduler inom Tarantools processer. Men det finns ocksÄ ett antal grundlÀggande skillnader. För det första mÄste alla roller i projektet leva i en gemensam kodbas. Och alla Tarantool-processer mÄste startas frÄn samma kodbas för att undvika överraskningar, som nÀr vi försöker initiera scheduler men den helt enkelt inte finns. Man bör ocksÄ undvika skillnader i kodversioner, eftersom systemets beteende i en sÄdan situation Àr mycket svÄrt att förutsÀga och felsöka.

Till skillnad frĂ„n Docker kan vi inte bara ta en "bild" av en roll, flytta den till en annan maskin och köra den dĂ€r. VĂ„ra roller Ă€r inte lika isolerade som Docker-containrar. Vi kan heller inte köra tvĂ„ identiska roller pĂ„ samma instans. Antingen finns rollen eller sĂ„ gör den inte det, pĂ„ ett sĂ€tt Ă€r det en singleton. För det tredje mĂ„ste rollerna inom hela replikationsgruppen vara identiska, för annars skulle det vara absurt — data Ă€r densamma, men konfigurationen Ă€r olika.

Deployering verktyg

Jag lovade att visa hur Cartridge hjÀlper till att deployera applikationer. För att underlÀtta livet för andra paketerar ramverket RPM-paket:

$ cartridge pack rpm myapp -- paketerar för oss .\/myapp-0.1.0-1.rpm
$ sudo yum install .\/myapp-0.1.0-1.rpm

Det installerade paketet innehÄller nÀstan allt som behövs: bÄde applikationen och dess installerade Lua-beroenden. Tarantool kommer ocksÄ att levereras till servern som en beroende av RPM-paketet, och vÄr tjÀnst Àr redo att starta. Detta görs genom systemd, men först behöver vi skriva lite konfiguration. Minst mÄste vi ange URI för varje process. Tre exempel Àr tillrÀckliga.

$ 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

HÀr finns en intressant detalj. IstÀllet för att bara ange porten för det binÀra protokollet anger vi hela den offentliga adressen för processen inklusive vÀrdnamnet. Detta Àr nödvÀndigt sÄ att klusterknutarna vet hur de ska ansluta till varandra. Det Àr en dÄlig idé att anvÀnda adressen 0.0.0.0 som advertise_uri; detta bör vara en extern IP-adress, inte bind-socketen. Utan det kommer inget att fungera, sÄ Cartridge kommer helt enkelt inte att tillÄta oss att starta en nod med en felaktig advertise_uri.

Nu nÀr konfigurationen Àr klar kan vi starta processerna. Eftersom en vanlig systemd-enhet inte tillÄter att mer Àn en process startas, installerar applikationer pÄ Cartridge sÄ kallade instansierade enheter som fungerar sÄ hÀr:

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

I konfigurationen angav vi HTTP-porten dĂ€r Cartridge tjĂ€nar webbgrĂ€nssnittet — 8080. LĂ„t oss gĂ„ dit och ta en titt:

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Vi ser att processerna har startat, men de Àr Ànnu inte konfigurerade. Kartuschen vet fortfarande inte vem som ska replikeras med vem och kan dÀrför inte fatta beslut sjÀlvstÀndigt, utan vÀntar pÄ vÄra ÄtgÀrder. VÄrt val Àr inte stort: livet för den nya klustret börjar med konfigurationen av den första noden. Sedan lÀgger vi till de andra till klustret, tilldelar dem roller, och dÄ kan vi anse att distributionen Àr framgÄngsrikt avslutad.

Vi hÀller upp en mugg av vÄr favoritdryck och kopplar av efter en lÄng arbetsvecka. Applikationen kan anvÀndas.

Tarantool Cartridge: sharding av Lua-backend pÄ tre rader

Sammanfattning

Och vad Àr resultaten? Testa, anvÀnd, lÀmna feedback, öppna Àrenden pÄ GitHub.

LĂ€nkar

[1] Tarantool » 2.2 » Referens » Rocks referens » Modul vshard

[2] Hur vi implementerade kÀrnan i investeringsverksamheten för Alfa-Bank pÄ basis av Tarantool

[3] Framstruktur för nÀsta generations fakturering: transformation med övergÄng till Tarantool

[4] SWIM — protokoll för klusteruppbyggnad

[5] GitHub — tarantool/cartridge-cli

[6] GitHub — tarantool/cartridge

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster