Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Nel gruppo Mail.ru abbiamo Tarantool: questo è un server applicativo in Lua, che funge anche da database (o viceversa?). È veloce e interessante, ma le capacità di un server non sono ancora illimitate. Anche il ridimensionamento verticale non è una panacea, quindi Tarantool dispone di strumenti per il ridimensionamento orizzontale: il modulo vshard ,. Ti consente di condividere i dati su più server, ma devi armeggiare con esso per configurarlo e collegare la logica aziendale.

Buone notizie: abbiamo raccolto alcuni pezzi grossi (es ,, ,) e ha creato un altro quadro che semplificherà notevolmente la soluzione a questo problema.

Cartuccia di tarantolo è un nuovo framework per lo sviluppo di sistemi distribuiti complessi. Ti consente di concentrarti sulla scrittura della logica aziendale invece che sulla risoluzione dei problemi infrastrutturali. Sotto il taglio ti dirò come funziona questo framework e come scrivere servizi distribuiti utilizzandolo.

E qual è, in effetti, il problema?

Abbiamo una tarantola, abbiamo vshard: cosa si può volere di più?

Innanzitutto è una questione di comodità. La configurazione vshard viene configurata tramite tabelle Lua. Affinché un sistema distribuito di più processi Tarantool funzioni correttamente, la configurazione deve essere la stessa ovunque. Nessuno vuole farlo manualmente. Pertanto, vengono utilizzati tutti i tipi di script, Ansible e sistemi di distribuzione.

La cartuccia stessa gestisce la configurazione vshard, lo fa in base al suo file propria configurazione distribuita. Si tratta essenzialmente di un semplice file YAML, una copia del quale viene archiviata in ciascuna istanza di Tarantool. La semplificazione è che il framework stesso monitora la sua configurazione e garantisce che sia la stessa ovunque.

In secondo luogo, è ancora una questione di comodità. La configurazione vshard non ha nulla a che fare con lo sviluppo della logica aziendale e distrae solo il programmatore dal suo lavoro. Quando discutiamo dell'architettura di un progetto, molto spesso parliamo dei singoli componenti e della loro interazione. È troppo presto per pensare di implementare un cluster su 3 data center.

Abbiamo risolto questi problemi più e più volte e ad un certo punto siamo riusciti a sviluppare un approccio che ha semplificato il lavoro con l'applicazione durante il suo intero ciclo di vita: creazione, sviluppo, test, CI/CD, manutenzione.

La cartuccia introduce il concetto di un ruolo per ciascun processo Tarantool. I ruoli sono un concetto che consente a uno sviluppatore di concentrarsi sulla scrittura del codice. Tutti i ruoli disponibili nel progetto possono essere eseguiti su un'istanza di Tarantool e questo sarà sufficiente per i test.

Caratteristiche principali della cartuccia Tarantool:

  • orchestrazione automatizzata dei cluster;
  • espandere le funzionalità dell'applicazione utilizzando nuovi ruoli;
  • modello di applicazione per lo sviluppo e la distribuzione;
  • partizionamento automatico integrato;
  • integrazione con il framework di testing Luatest;
  • gestione del cluster tramite WebUI e API;
  • strumenti di confezionamento e distribuzione.

Ciao mondo!

Non vedo l'ora di mostrare il framework stesso, quindi lasceremo la storia dell'architettura per dopo e inizieremo con qualcosa di semplice. Se assumiamo che lo stesso Tarantool sia già installato, non resta che fare

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

Questi due comandi installeranno le utilità della riga di comando e ti permetteranno di creare la tua prima applicazione dal modello:

$ cartridge create --name myapp

E questo è ciò che otteniamo:

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/

Questo è un repository git con un file "Hello, World!" già pronto. applicazione. Proviamo subito ad eseguirlo, avendo precedentemente installato le dipendenze (compreso il framework stesso):

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

Quindi, abbiamo un nodo in esecuzione per la futura applicazione frammentata. Un profano curioso può immediatamente aprire l'interfaccia web, configurare un cluster di un nodo con il mouse e godersi il risultato, ma è troppo presto per rallegrarsi. Finora l'applicazione non può fare nulla di utile, quindi ti parlerò della distribuzione più tardi, ma ora è il momento di scrivere il codice.

Sviluppo di applicazioni

Immagina, stiamo progettando un progetto che deve ricevere dati, salvarli e creare un report una volta al giorno.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Iniziamo a disegnare un diagramma e vi inseriamo tre componenti: gateway, storage e scheduler. Stiamo lavorando ulteriormente sull'architettura. Poiché utilizziamo vshard come spazio di archiviazione, aggiungiamo vshard-router e vshard-storage allo schema. Né il gateway né lo scheduler accederanno direttamente allo storage; il router è a questo, è stato creato.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Questo diagramma non rappresenta ancora esattamente ciò che costruiremo nel progetto perché i componenti sembrano astratti. Dobbiamo ancora vedere come questo verrà proiettato sul vero Tarantool: raggruppiamo i nostri componenti per processo.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Non ha molto senso mantenere vshard-router e gateway su istanze separate. Perché dobbiamo navigare nuovamente in rete se questa è già responsabilità del router? Devono essere eseguiti all'interno dello stesso processo. Cioè, sia il gateway che vshard.router.cfg vengono inizializzati in un unico processo e consentono loro di interagire localmente.

In fase di progettazione era conveniente lavorare con tre componenti, ma io, come sviluppatore, mentre scrivo il codice, non voglio pensare al lancio di tre istanze di Tarnatool. Devo eseguire dei test e verificare di aver scritto correttamente il gateway. O forse voglio dimostrare una funzionalità ai miei colleghi. Perché dovrei affrontare il fastidio di distribuire tre copie? È così che è nato il concetto di ruoli. Un ruolo è un modulo luash regolare il cui ciclo di vita è gestito da Cartuccia. In questo esempio ce ne sono quattro: gateway, router, storage, scheduler. Potrebbero essercene altri in un altro progetto. Tutti i ruoli possono essere eseguiti in un unico processo e questo sarà sufficiente.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

E quando si tratta di implementazione in fase di staging o produzione, assegneremo a ciascun processo Tarantool il proprio insieme di ruoli a seconda delle capacità hardware:

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Gestione della topologia

Le informazioni su dove vengono eseguiti i ruoli devono essere archiviate da qualche parte. E questo “da qualche parte” è la configurazione distribuita, di cui ho già parlato sopra. La cosa più importante è la topologia del cluster. Ecco 3 gruppi di replica di 5 processi Tarantool:

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Non vogliamo perdere dati, quindi trattiamo con cura le informazioni sui processi in esecuzione. La cartuccia tiene traccia della configurazione utilizzando un commit a due fasi. Una volta che vogliamo aggiornare la configurazione, controlla innanzitutto che tutte le istanze siano disponibili e pronte ad accettare la nuova configurazione. Successivamente, la seconda fase applica il file config. Pertanto, anche se una copia risulta temporaneamente non disponibile, non accadrà nulla di male. La configurazione semplicemente non verrà applicata e vedrai un errore in anticipo.

Anche nella sezione topologia viene indicato un parametro così importante come il leader di ciascun gruppo di replica. Di solito questa è la copia che viene registrata. Il resto è spesso di sola lettura, anche se potrebbero esserci delle eccezioni. A volte gli sviluppatori coraggiosi non hanno paura dei conflitti e possono scrivere dati su più repliche in parallelo, ma ci sono alcune operazioni che, qualunque cosa accada, non dovrebbero essere eseguite due volte. Per questo c'è il segno di un leader.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Vita di ruoli

Affinché esista un ruolo astratto in tale architettura, il framework deve gestirli in qualche modo. Naturalmente il controllo avviene senza riavviare il processo Tarantoolo. Sono presenti 4 callback per gestire i ruoli. La cartuccia stessa li chiamerà a seconda di quanto scritto nella sua configurazione distribuita, applicando così la configurazione a ruoli specifici.

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

Ogni ruolo ha una funzione init. Viene chiamato una volta quando il ruolo viene abilitato o quando viene riavviato Tarantool. Lì è conveniente, ad esempio, inizializzare box.space.create, oppure lo scheduler può avviare una fibra di background che eseguirà il lavoro a determinati intervalli di tempo.

Una funzione init potrebbe non essere sufficiente La cartuccia consente ai ruoli di sfruttare la configurazione distribuita utilizzata per archiviare la topologia. Possiamo dichiarare una nuova sezione nella stessa configurazione e archiviare al suo interno un frammento della configurazione aziendale. Nel mio esempio, potrebbe trattarsi di uno schema di dati o di impostazioni di pianificazione per il ruolo di pianificazione.

Chiamate cluster validate_config и apply_config ogni volta che cambia la configurazione distribuita. Quando una configurazione viene applicata tramite un commit in due fasi, il cluster verifica che ciascun ruolo sia pronto ad accettare questa nuova configurazione e, se necessario, segnala un errore all'utente. Quando tutti concordano sul fatto che la configurazione è normale, allora il file apply_config.

Anche i ruoli hanno un metodo stop, necessario per ripulire l'output del ruolo. Se diciamo che lo scheduler non è più necessario su questo server, può fermare quelle fibre con cui è partito init.

I ruoli possono interagire tra loro. Siamo abituati a scrivere chiamate di funzione in Lua, ma può succedere che un determinato processo non abbia il ruolo di cui abbiamo bisogno. Per facilitare le chiamate in rete, utilizziamo il modulo ausiliario rpc (remote procedure call), costruito sulla base della netbox standard integrata in Tarantool. Ciò può essere utile se, ad esempio, il tuo gateway vuole chiedere direttamente allo scheduler di eseguire il lavoro adesso, invece di aspettare un giorno.

Un altro punto importante è garantire la tolleranza agli errori. La cartuccia utilizza il protocollo SWIM per monitorare lo stato ,. In breve, i processi si scambiano “voci” tra loro tramite UDP: ogni processo comunica ai vicini le ultime notizie e questi rispondono. Se all'improvviso la risposta non arriva, Tarantool comincia a sospettare che qualcosa non va, e dopo un po' recita morte e comincia a raccontare a tutti intorno questa notizia.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Sulla base di questo protocollo, Cartuccia organizza l'elaborazione automatica degli errori. Ogni processo monitora il proprio ambiente e, se il leader smette improvvisamente di rispondere, la replica può assumere il suo ruolo e Cartuccia configura di conseguenza i ruoli in esecuzione.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

È necessario fare attenzione in questo caso, poiché il frequente passaggio avanti e indietro può portare a conflitti di dati durante la replica. Naturalmente, non dovresti abilitare il failover automatico in modo casuale. Dobbiamo capire chiaramente cosa sta succedendo ed essere sicuri che la replica non si interromperà dopo che il leader sarà stato ripristinato e la corona gli sarà stata restituita.

Da tutto ciò, potresti avere la sensazione che i ruoli siano simili ai microservizi. In un certo senso, sono proprio questo, solo come moduli all'interno dei processi Tarantool. Ma ci sono anche una serie di differenze fondamentali. Innanzitutto, tutti i ruoli del progetto devono vivere nella stessa codebase. E tutti i processi Tarantool dovrebbero essere lanciati dalla stessa codebase, in modo che non ci siano sorprese come quelle che proviamo quando proviamo ad inizializzare lo scheduler, ma semplicemente non esiste. Inoltre, non dovresti consentire differenze nelle versioni del codice, perché il comportamento del sistema in una situazione del genere è molto difficile da prevedere ed eseguire il debug.

A differenza di Docker, non possiamo semplicemente prendere un ruolo "immagine", portarlo su un'altra macchina ed eseguirlo lì. I nostri ruoli non sono isolati come i container Docker. Inoltre, non possiamo eseguire due ruoli identici su un'istanza. Un ruolo o esiste oppure non esiste; in un certo senso è un singleton. In terzo luogo, i ruoli devono essere gli stessi all'interno dell'intero gruppo di replica, perché altrimenti sarebbe assurdo: i dati sono gli stessi, ma la configurazione è diversa.

Strumenti di distribuzione

Ho promesso di mostrare come Cartuccia aiuta a distribuire le applicazioni. Per rendere la vita più facile agli altri, il framework raggruppa i pacchetti RPM:

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

Il pacchetto installato contiene quasi tutto ciò di cui hai bisogno: sia l'applicazione che le dipendenze installate. Tarantool arriverà anche sul server come dipendenza del pacchetto RPM e il nostro servizio è pronto per essere lanciato. Questo viene fatto tramite systemd, ma prima devi scrivere una piccola configurazione. Come minimo, specificare l'URI di ciascun processo. Tre ne bastano, ad esempio.

$ 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

C'è una sfumatura interessante qui. Invece di specificare solo la porta del protocollo binario, specifichiamo l'intero indirizzo pubblico del processo compreso il nome host. Ciò è necessario affinché i nodi del cluster sappiano come connettersi tra loro. È una cattiva idea utilizzare 0.0.0.0 come indirizzo Advertise_URI; dovrebbe essere un indirizzo IP esterno, non un collegamento socket. Senza di esso, nulla funzionerà, quindi Cartuccia semplicemente non ti consentirà di avviare un nodo con l'advert_uri errato.

Ora che la configurazione è pronta, puoi avviare i processi. Poiché una normale unità systemd non consente l'avvio di più di un processo, le applicazioni sulla cartuccia vengono installate dai cosiddetti. unità istanziate che funzionano in questo modo:

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

Nella configurazione, abbiamo specificato la porta HTTP su cui la cartuccia serve l'interfaccia web - 8080. Andiamo su di essa e diamo un'occhiata:

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Vediamo che sebbene i processi siano in esecuzione, non sono ancora configurati. La cartuccia non sa ancora chi dovrebbe replicarsi con chi e non può prendere una decisione da sola, quindi sta aspettando le nostre azioni. Ma non abbiamo molta scelta: la vita di un nuovo cluster inizia con la configurazione del primo nodo. Successivamente aggiungeremo gli altri al cluster, assegneremo loro i ruoli e a questo punto la distribuzione potrà considerarsi completata con successo.

Versiamo un bicchiere della tua bevanda preferita e rilassiamoci dopo una lunga settimana di lavoro. L'applicazione può essere utilizzata.

Cartuccia Tarantool: frammentazione di un backend Lua in tre righe

Risultati di

Quali sono i risultati? Provalo, usalo, lascia feedback, crea ticket su Github.

riferimenti

, Tarantool » 2.2 » Riferimento » Riferimento rocce » Modulo vshard

, Come abbiamo implementato il nucleo dell'attività di investimento di Alfa-Bank basata su Tarantool

, Architettura di fatturazione di nuova generazione: trasformazione con il passaggio a Tarantool

, SWIM - protocollo di costruzione del cluster

, GitHub - tarantool/cartuccia-cli

, GitHub - tarantool/cartuccia

Fonte: habr.com

Aggiungi un commento