Cartouche Tarantool: Sharding Lua Backend en trois lignes


Dans Mail.ru Group, nous avons Tarantool, un serveur d'applications basé sur Lua et une base de données unie. C'est rapide et élégant, mais les ressources d'un seul serveur sont toujours limitées. La mise à l'échelle verticale n'est pas non plus la panacée. C'est pourquoi Tarantool a quelques outils pour la mise à l'échelle horizontale, ou le module vshard [1] . Il vous permet de répartir les données sur plusieurs serveurs, mais vous devrez les bricoler pendant un certain temps pour les configurer et renforcer la logique métier.

Bonne nouvelle: nous avons obtenu notre part de bosses (par exemple, [2] , [3] ) et avons créé un autre framework, ce qui simplifie considérablement la solution à ce problÚme.

Tarantool Cartridge est le nouveau cadre de développement de systÚmes distribués complexes. Il vous permet de vous concentrer sur l'écriture de la logique métier au lieu de résoudre les problÚmes d'infrastructure. Sous la coupe, je vais vous dire comment ce cadre fonctionne et comment il pourrait aider à écrire des services distribués.

Alors quel est exactement le problĂšme?


Nous avons Tarantool et vshard - que voulons-nous de plus?

PremiĂšrement, c'est une question de commoditĂ©. Vshard est configurĂ© dans les tables Lua. Mais pour qu'un systĂšme distribuĂ© de plusieurs processus Tarantool fonctionne correctement, la configuration doit ĂȘtre la mĂȘme partout. Personne ne voudrait le faire manuellement, donc toutes sortes de scripts, d'Ansible et de systĂšmes de dĂ©ploiement sont utilisĂ©s.

La cartouche elle-mĂȘme gĂšre la configuration vshard sur la base de sa propre configuration distribuĂ©e . En fait, il s'agit d'un simple fichier YAML, et sa copie est stockĂ©e sur chaque instance de Tarantool. En d'autres termes, le framework surveille sa configuration pour qu'il en soit de mĂȘme partout.

DeuxiÚmement, c'est encore une question de commodité. La configuration Vshard n'est pas liée au développement de la logique métier et ne distrait qu'un développeur de son travail. Lorsque nous discutons de l'architecture d'un projet, la question concerne trÚs probablement des composants séparés et leur interaction. Il est encore trop tÎt pour penser à déployer un cluster pour 3 centres de données.

Nous avons résolu ces problÚmes à maintes reprises, et à un moment donné, nous avons réussi à développer une approche afin de simplifier le travail avec l'application tout au long de son cycle de vie: création, développement, test, CI / CD, maintenance.

La cartouche introduit le concept de rĂŽles pour chaque processus Tarantool. Les rĂŽles permettent au dĂ©veloppeur de se concentrer sur l'Ă©criture de code. Tous les rĂŽles disponibles dans le projet peuvent ĂȘtre exĂ©cutĂ©s sur la seule instance de Tarantool, ce qui serait suffisant pour les tests.

Caractéristiques principales de la cartouche Tarantool:

  • orchestration automatisĂ©e des clusters;
  • fonctionnalitĂ© d'application Ă©tendue avec de nouveaux rĂŽles;
  • modĂšle d'application pour le dĂ©veloppement et le dĂ©ploiement;
  • sharding automatique intĂ©grĂ©;
  • intĂ©gration avec le framework Luatest;
  • gestion de cluster Ă  l'aide de WebUI et API;
  • outils de packaging et de dĂ©ploiement.

Bonjour tout le monde!


J'ai hĂąte de vous montrer le cadre lui-mĂȘme, alors sauvegardons l'histoire de l'architecture pour plus tard et commençons par une tĂąche facile. En supposant que Tarantool est dĂ©jĂ  installĂ©, tout ce que nous avons Ă  faire est

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

En conséquence, les utilitaires de ligne de commande sont installés, ce qui vous permet de créer votre premiÚre application à partir du modÚle:

 $ cartridge create --name myapp 

Et voici ce que nous obtenons:

 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/ 

Ceci est un rĂ©fĂ©rentiel git avec un "Hello, World!" PrĂȘt Ă  l'emploi application. Essayons de l'exĂ©cuter aprĂšs avoir installĂ© les dĂ©pendances (y compris le framework lui-mĂȘme):

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

Nous avons lancĂ© un nƓud de notre future application fragmentĂ©e. Si vous ĂȘtes curieux, vous pouvez immĂ©diatement ouvrir l'interface Web, qui fonctionne sur localhost : 8080, utiliser une souris pour configurer un cluster Ă  un nƓud et profiter du rĂ©sultat, mais ne vous excitez pas trop tĂŽt. L'application ne sait pas comment faire quoi que ce soit d'utile pour l'instant, je vais donc vous parler du dĂ©ploiement plus tard, et maintenant il est temps d'Ă©crire du code.

DĂ©velopper des applications


Imaginez que nous concevions un systÚme qui devrait recevoir des données, les enregistrer et créer un rapport une fois par jour.


Nous dessinons donc un diagramme avec trois composants: passerelle, stockage et ordonnanceur. Continuons à travailler sur l'architecture. Puisque nous utilisons vshard comme stockage, ajoutons vshard-router et vshard-storage au diagramme. Ni la passerelle ni le planificateur n'accéderont directement au stockage - un routeur est explicitement créé pour cette tùche.


Ce diagramme semble abstrait car les composants ne reflÚtent toujours pas ce que nous allons créer dans le projet. Nous devrons voir comment ce projet correspond au vrai Tarantool, nous regroupons donc nos composants par processus.


Il n'est pas trĂšs judicieux de conserver vshard-router et gateway sur des instances distinctes. Pourquoi irions-nous Ă  nouveau sur le rĂ©seau, si cela est dĂ©jĂ  la responsabilitĂ© du routeur? Ils doivent s'exĂ©cuter dans le mĂȘme processus, c'est-Ă -dire que la passerelle et vshard.router.cfg doivent ĂȘtre initialisĂ©s dans le mĂȘme processus et interagir localement.

Pendant la phase de conception, il Ă©tait pratique de travailler avec trois composants, mais en tant que dĂ©veloppeur, je ne veux pas penser Ă  lancer trois instances de Tarantool lors de l'Ă©criture de code. J'ai besoin d'exĂ©cuter les tests et de vĂ©rifier que j'ai correctement Ă©crit le code de la passerelle. Ou je veux peut-ĂȘtre montrer une nouvelle fonctionnalitĂ© Ă  mes collĂšgues. Pourquoi devrais-je prendre des problĂšmes avec le dĂ©ploiement de trois instances? Ainsi, le concept de rĂŽles est nĂ©. Un rĂŽle est un module Lua rĂ©gulier, et la cartouche gĂšre son cycle de vie. Dans cet exemple, il y en a quatre: passerelle, routeur, stockage et planificateur. Un autre projet peut avoir plus de rĂŽles. Tous les rĂŽles peuvent ĂȘtre lancĂ©s en un seul processus, et ce serait suffisant.


Et lorsque le problÚme concerne le déploiement vers la mise en production ou la production, nous attribuons un ensemble de rÎles distinct à chaque processus Tarantool en fonction des capacités matérielles sous-jacentes:


Gestion de la topologie


Nous devons également stocker des informations sur les rÎles en cours d'exécution quelque part. Et "quelque part" signifie la configuration distribuée susmentionnée. La chose la plus importante ici est la topologie de cluster. Vous pouvez voir ici 3 groupes de réplication de 5 processus Tarantool:


Nous ne voulons pas perdre les donnĂ©es, nous traitons donc les informations sur les processus en cours avec soin. La cartouche surveille la configuration Ă  l'aide d'une validation en deux phases. DĂšs que nous voulons mettre Ă  jour la configuration, il vĂ©rifie d'abord si les instances sont disponibles et prĂȘtes Ă  accepter la nouvelle configuration. AprĂšs cela, la configuration est appliquĂ©e dans la deuxiĂšme phase. Ainsi, mĂȘme si une instance est temporairement indisponible, alors rien ne peut mal tourner. La configuration ne sera tout simplement pas appliquĂ©e et vous verrez une erreur Ă  l'avance.

La section topologie a Ă©galement un paramĂštre aussi important que le leader de chaque groupe de rĂ©plication. Il s'agit gĂ©nĂ©ralement de l'instance qui accepte les Ă©critures. Les autres sont le plus souvent en lecture seule, bien qu'il puisse y avoir des exceptions. Parfois, les dĂ©veloppeurs courageux n'ont pas peur des conflits et peuvent Ă©crire des donnĂ©es sur plusieurs rĂ©pliques en mĂȘme temps. NĂ©anmoins, certaines opĂ©rations ne doivent pas ĂȘtre effectuĂ©es deux fois. C'est pourquoi nous avons un leader.


Cycle de vie des rĂŽles


Pour qu'une architecture de projet contienne des rĂŽles abstraits, le cadre doit en quelque sorte ĂȘtre capable de les gĂ©rer. Naturellement, les rĂŽles sont gĂ©rĂ©s sans redĂ©marrer le processus Tarantool. Il existe quatre rappels conçus pour la gestion des rĂŽles. La cartouche elle-mĂȘme les appelle en fonction des informations de la configuration distribuĂ©e, appliquant ainsi la configuration aux rĂŽles spĂ©cifiques.

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

Chaque rÎle a une fonction init . Il est appelé une fois: soit lorsque le rÎle est activé, soit lorsque Tarantool redémarre. Ici, il est pratique, par exemple, d'initialiser box.space.create, ou le planificateur peut exécuter une fibre d'arriÚre-plan qui terminerait la tùche à intervalles réguliers.

La fonction init seule peut ne pas suffire. La cartouche permet aux rĂŽles d'accĂ©der Ă  la configuration distribuĂ©e utilisĂ©e pour stocker la topologie. Dans la mĂȘme configuration, nous pouvons dĂ©clarer une nouvelle section et y stocker une partie de la configuration mĂ©tier. Dans mon exemple, cela pourrait ĂȘtre un schĂ©ma de donnĂ©es ou des paramĂštres de planification pour le rĂŽle de planificateur.

Le cluster appelle validate_config et apply_config chaque fois que la configuration distribuĂ©e change. Lorsqu'une configuration est appliquĂ©e dans une validation en deux phases, le cluster vĂ©rifie que chaque rĂŽle sur chaque serveur est prĂȘt Ă  accepter cette nouvelle configuration et, si nĂ©cessaire, signale une erreur Ă  l'utilisateur. Lorsque tout le monde est d'accord avec la configuration, apply_config est appelĂ©.

Les rĂŽles prennent Ă©galement en charge une mĂ©thode d' stop pour nettoyer les ordures. Si nous disons que l'ordonnanceur n'est pas nĂ©cessaire sur ce serveur, il peut arrĂȘter les fibres qu'il a commencĂ© Ă  utiliser init .

Les rĂŽles peuvent interagir les uns avec les autres. Nous sommes habituĂ©s Ă  Ă©crire des appels de fonction Lua, mais le processus peut ne pas avoir le rĂŽle nĂ©cessaire. Pour faciliter l'accĂšs au rĂ©seau, nous utilisons un module auxiliaire appelĂ© rpc (appel de procĂ©dure distante), qui est construit sur la base du module standard Tarantool net.box. Cela peut ĂȘtre utile, par exemple, si votre passerelle souhaite demander directement au planificateur d'effectuer la tĂąche maintenant, plutĂŽt qu'en une journĂ©e.

Un autre point important est d'assurer la tolérance aux pannes. La cartouche utilise le protocole SWIM [4] pour surveiller la santé. En bref, les processus échangent des «rumeurs» via UDP, c'est-à-dire que chaque processus informe ses voisins des derniÚres nouvelles, et ils répondent. S'il n'y a soudainement pas de réponse, Tarantool soupçonne que quelque chose ne va pas, et aprÚs un certain temps, il déclare la mort et envoie ce message à tout le monde.


Sur la base de ce protocole, Cartridge organise le basculement automatique. Chaque processus surveille son environnement et si le leader cesse soudainement de répondre, la réplique pourrait revendiquer son rÎle et Cartridge configurerait les rÎles en cours d'exécution en conséquence.


Vous devez ĂȘtre prudent ici car des allers-retours frĂ©quents peuvent entraĂźner des conflits de donnĂ©es lors de la rĂ©plication. Le basculement automatique ne doit certainement pas ĂȘtre activĂ© de maniĂšre alĂ©atoire. Vous devez avoir une idĂ©e claire de ce qui se passe et ĂȘtre sĂ»r que la rĂ©plication ne plantera pas lorsque le leader rĂ©cupĂšre et retrouve sa couronne.

D'aprĂšs tout ce qui a Ă©tĂ© dit, les rĂŽles peuvent sembler similaires aux microservices. Dans un sens, ils ne sont que des modules dans les processus Tarantool, et il existe plusieurs diffĂ©rences fondamentales. Tout d'abord, tous les rĂŽles de projet doivent vivre dans la mĂȘme base de code. Et tous les processus Tarantool devraient s'exĂ©cuter Ă  partir de la mĂȘme base de code, afin qu'il n'y ait pas de surprise, comme lorsque nous essayons d'initialiser le planificateur, mais qu'il n'y a tout simplement pas de planificateur. De plus, nous ne devons pas autoriser de diffĂ©rences dans les versions de code car le comportement du systĂšme est compliquĂ© Ă  prĂ©voir et Ă  dĂ©boguer dans une telle situation.

Contrairement Ă  Docker, nous ne pouvons pas simplement prendre une "image" d'un rĂŽle, le transfĂ©rer sur une autre machine et l'exĂ©cuter lĂ -bas. Nos rĂŽles ne sont pas aussi isolĂ©s que les conteneurs Docker. De plus, nous ne pouvons pas exĂ©cuter deux rĂŽles identiques sur la mĂȘme instance. Le rĂŽle est lĂ  ou non; dans un sens, c'est un singleton. Et troisiĂšmement, les rĂŽles devraient ĂȘtre les mĂȘmes au sein de l'ensemble du groupe de rĂ©plication, car sinon, cela aurait l'air ridicule: les donnĂ©es sont les mĂȘmes, mais le comportement est diffĂ©rent.

Outils de déploiement


J'ai promis de vous montrer comment la cartouche pourrait aider à déployer des applications. Pour vous faciliter la vie, le framework crée des packages RPM:

 $ cartridge pack rpm myapp # will create ./myapp-0.1.0-1.rpm $ sudo yum install ./myapp-0.1.0-1.rpm 

Le package installĂ© contient presque tout ce dont vous avez besoin: Ă  la fois l'application et les dĂ©pendances Lua installĂ©es. Tarantool arrive Ă©galement sur le serveur en tant que dĂ©pendance de package RPM, et notre service est prĂȘt Ă  ĂȘtre lancĂ©. Tout cela se fait en utilisant systemd, mais d'abord, nous devons faire une configuration, au moins spĂ©cifier l'URI de chaque processus. Trois suffiraient pour notre exemple.

 $ 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 

Il y a un aspect intĂ©ressant Ă  prendre en compte: au lieu de ne spĂ©cifier que le port du protocole binaire, nous spĂ©cifions l'adresse publique de l'ensemble du processus, y compris le nom d'hĂŽte. Nous le faisons parce que les nƓuds de cluster doivent savoir comment se connecter les uns aux autres. Ce serait une mauvaise idĂ©e d'utiliser l'adresse 0.0.0.0 comme advertise_uri, car ce devrait ĂȘtre une adresse IP externe, plutĂŽt qu'une liaison de socket. Rien ne fonctionne sans cela, donc Cartridge ne laisserait tout simplement pas le nƓud avec le mauvais advertise_uri dĂ©marrer.

Maintenant que la configuration est prĂȘte, nous pouvons dĂ©marrer les processus. Étant donnĂ© qu'une unitĂ© systemd rĂ©guliĂšre ne permet pas de dĂ©marrer plusieurs processus, les unitĂ©s dites instanciĂ©es installent les applications sur la cartouche:

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

Nous avons spĂ©cifiĂ© le port HTTP pour l'interface Web de la cartouche dans la configuration: 8080. Allons lĂ -bas et jetons un Ɠil:


Nous pouvons voir que les processus ne sont pas encore configurĂ©s, bien qu'ils soient dĂ©jĂ  en cours d'exĂ©cution. La cartouche ne sait pas encore comment la rĂ©plication doit ĂȘtre effectuĂ©e et ne peut pas dĂ©cider seule, elle attend donc nos actions. Nous n'avons pas beaucoup de choix: la vie d'un nouveau cluster commence par la configuration du premier nƓud. Ensuite, nous ajoutons d'autres nƓuds au cluster, nous leur attribuons des rĂŽles et le dĂ©ploiement peut ĂȘtre considĂ©rĂ© comme terminĂ© avec succĂšs.

Versons-nous un verre et relaxons aprĂšs une longue semaine de travail. L'application est prĂȘte Ă  l'emploi.


RĂ©sultats


Et les résultats? Veuillez tester, utiliser, laisser des commentaires et créer des tickets sur Github.

Les références


[1] Tarantool »2.2» Référence »Référence Rocks» Module vshard
[2] Comment nous avons mis en Ɠuvre le cƓur de mĂ©tier d'investissement d'Alfa-Bank basĂ© sur Tarantool
[3] Architecture de facturation de nouvelle génération: transition vers Tarantool
[4] SWIM - Cluster Building Protocol
[5] GitHub - tarantool / cartouche-cli
[6] GitHub - tarantool / cartouche

Source: https://habr.com/ru/post/fr470812/


All Articles