Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Chez Mail.ru Group, nous avons Tarantool - il s'agit d'un serveur d'applications dans Lua, qui sert également de base de données (ou vice versa ?). C'est rapide et cool, mais les capacités d'un serveur ne sont toujours pas illimitées. La mise à l'échelle verticale n'est pas non plus une panacée, c'est pourquoi Tarantool dispose d'outils pour la mise à l'échelle horizontale - le module vshard . Il vous permet de partager des données sur plusieurs serveurs, mais vous devez les bricoler pour les configurer et attacher la logique métier.

Bonne nouvelle : nous avons rassemblé quelques gros clichés (par exemple , ) et a créé un autre cadre qui simplifiera considérablement la solution à ce problème.

Cartouche Tarantool est un nouveau cadre pour développer des 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 expliquer comment fonctionne ce framework et comment écrire des services distribués en l'utilisant.

Et quel est en fait le problème?

Nous avons une tarentule, nous avons vshard - que demander de plus ?

C'est d'abord une question de commodité. La configuration vshard est configurée via les tables Lua. Pour qu'un système distribué de plusieurs processus Tarantool fonctionne correctement, la configuration doit être la même partout. Personne ne veut faire cela manuellement. Par conséquent, toutes sortes de scripts, Ansible et systèmes de déploiement sont utilisés.

La cartouche gère elle-même la configuration vshard, elle le fait en fonction de son propre configuration distribuée. Il s'agit essentiellement d'un simple fichier YAML, dont une copie est stockée dans chaque instance de Tarantool. La simplification est que le framework lui-même surveille sa configuration et s'assure qu'elle est la même partout.

Deuxièmement, c’est encore une fois une question de commodité. La configuration vshard n'a rien à voir avec le développement de la logique métier et ne fait que distraire le programmeur de son travail. Lorsque nous discutons de l'architecture d'un projet, nous parlons le plus souvent de composants individuels et de leur interaction. Il est trop tôt pour envisager de déployer un cluster sur 3 datacenters.

Nous avons résolu ces problèmes encore et encore, et à un moment donné, nous avons réussi à développer une approche qui simplifie le travail avec l'application tout au long de son cycle de vie : création, développement, tests, CI/CD, maintenance.

Cartouche introduit le concept de rôle pour chaque processus Tarantool. Les rôles sont un concept qui permet à un développeur de se concentrer sur l'écriture de code. Tous les rôles disponibles dans le projet peuvent être exécutés sur une seule instance de Tarantool, et cela suffira pour les tests.

Principales caractéristiques de la cartouche Tarantool :

  • orchestration automatisée des clusters ;
  • étendre les fonctionnalités de l'application en utilisant de nouveaux rôles ;
  • modèle d'application pour le développement et le déploiement ;
  • partitionnement automatique intégré ;
  • intégration avec le framework de test Luatest ;
  • gestion de cluster à l'aide de WebUI et d'API ;
  • outils de packaging et de déploiement.

Bonjour le monde!

J'ai hâte de montrer le framework lui-même, nous laisserons donc l'histoire de l'architecture pour plus tard et commencerons par quelque chose de simple. Si l'on suppose que Tarantool lui-même est déjà installé, il ne reste plus qu'à faire

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

Ces deux commandes installeront les utilitaires de ligne de commande et vous permettront 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/

Il s'agit d'un référentiel git avec un « Hello, World ! » prêt à l'emploi. application. Essayons de l'exécuter tout de suite, après avoir préalablement installé les dépendances (y compris le framework lui-même) :

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

Nous avons donc un nœud en cours d’exécution pour la future application partitionnée. Un profane curieux peut immédiatement ouvrir l'interface Web, configurer un cluster d'un nœud avec la souris et profiter du résultat, mais il est trop tôt pour se réjouir. Jusqu'à présent, l'application ne peut rien faire d'utile, je vous parlerai donc du déploiement plus tard, mais il est maintenant temps d'écrire du code.

Développement d'applications

Imaginez, nous concevons un projet qui doit recevoir des données, les sauvegarder et créer un rapport une fois par jour.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Nous commençons à dessiner un diagramme et y plaçons trois composants : la passerelle, le stockage et le planificateur. Nous travaillons davantage sur l'architecture. Puisque nous utilisons vshard comme stockage, nous ajoutons vshard-router et vshard-storage au schéma. Ni la passerelle ni le planificateur n’accéderont directement au stockage ; c’est à cela que sert le routeur, c’est pour cela qu’il a été créé.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Ce diagramme ne représente toujours pas exactement ce que nous allons construire dans le projet car les composants semblent abstraits. Nous devons encore voir comment cela sera projeté sur le vrai Tarantool - regroupons nos composants par processus.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Il ne sert à rien de conserver vshard-router et gateway sur des instances distinctes. Pourquoi devons-nous surfer à nouveau sur le réseau si cela relève déjà de la responsabilité du routeur ? Ils doivent être exécutés dans le même processus. Autrement dit, la passerelle et vshard.router.cfg sont initialisés en un seul processus et leur permettent d'interagir localement.

Au stade de la conception, il était pratique de travailler avec trois composants, mais moi, en tant que développeur, lors de l'écriture du code, je ne veux pas penser au lancement de trois instances de Tarnatool. Je dois exécuter des tests et vérifier que j'ai écrit correctement la passerelle. Ou peut-être que je souhaite démontrer une fonctionnalité à mes collègues. Pourquoi devrais-je me donner la peine de déployer trois copies ? C’est ainsi qu’est né le concept de rôles. Un rôle est un module Luash standard dont le cycle de vie est géré par Cartouche. Dans cet exemple, il y en a quatre : passerelle, routeur, stockage, planificateur. Il y en aura peut-être plus dans un autre projet. Tous les rôles peuvent être exécutés en un seul processus, et cela suffira.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Et lorsqu'il s'agit de déploiement en préparation ou en production, nous attribuerons à chaque processus Tarantool son propre ensemble de rôles en fonction des capacités matérielles :

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Gestion de la topologie

Les informations sur l'endroit où les rôles sont exécutés doivent être stockées quelque part. Et ce « quelque part » est la configuration distribuée, dont j'ai déjà parlé plus haut. La chose la plus importante est la topologie du cluster. Voici 3 groupes de réplication de 5 processus Tarantool :

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Nous ne voulons pas perdre de données, c'est pourquoi nous traitons les informations sur les processus en cours avec soin. Cartouche assure le suivi de la configuration à l'aide d'une validation en deux phases. Une fois que nous souhaitons mettre à jour la configuration, il vérifie d'abord que toutes les instances sont disponibles et prêtes à accepter la nouvelle configuration. Après cela, la deuxième phase applique la configuration. Ainsi, même si un exemplaire s'avère temporairement indisponible, rien de grave ne se produira. La configuration ne sera tout simplement pas appliquée et vous verrez une erreur à l'avance.

Également dans la section topologie, un paramètre aussi important que le leader de chaque groupe de réplication est indiqué. C'est généralement la copie qui est enregistrée. Le reste est 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 parallèle, mais il existe certaines opérations qui, quoi qu'il en soit, ne doivent pas être effectuées deux fois. Pour cela, il y a un signe de leader.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

La vie des rôles

Pour qu’un rôle abstrait existe dans une telle architecture, le framework doit le gérer d’une manière ou d’une autre. Naturellement, le contrôle s'effectue sans redémarrer le processus Tarantool. Il y a 4 rappels pour gérer les rôles. La cartouche elle-même les appellera en fonction de ce qui est écrit dans sa configuration distribuée, appliquant ainsi la configuration à des 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 lorsque le rôle est activé ou lorsque Tarantool est redémarré. Il est pratique là-bas, par exemple, d'initialiser box.space.create, ou le planificateur peut lancer une fibre d'arrière-plan qui effectuera un travail à certains intervalles de temps.

Une fonction init ne suffira peut-être pas. La cartouche permet aux rôles de profiter de la configuration distribuée qu'elle utilise pour stocker la topologie. Nous pouvons déclarer une nouvelle section dans la même configuration et y stocker un fragment de la configuration métier. Dans mon exemple, il peut s'agir d'un schéma de données ou de paramètres de planification pour le rôle de planificateur.

Appels de cluster validate_config и apply_config à chaque fois que la configuration distribuée change. Lorsqu'une configuration est appliquée par un commit en deux phases, le cluster vérifie que chaque rôle est prêt à accepter cette nouvelle configuration et, si nécessaire, signale une erreur à l'utilisateur. Lorsque tout le monde est d'accord sur le fait que la configuration est normale, alors le apply_config.

Les rôles ont également une méthode stop, ce qui est nécessaire pour nettoyer la sortie du rôle. Si nous disons que le planificateur n'est plus nécessaire sur ce serveur, il peut arrêter les fibres avec lesquelles il a démarré init.

Les rôles peuvent interagir les uns avec les autres. Nous sommes habitués à écrire des appels de fonctions en Lua, mais il peut arriver qu'un processus donné n'ait pas le rôle dont nous avons besoin. Pour faciliter les appels sur le réseau, nous utilisons le module auxiliaire rpc (remote procédure call), construit sur la base de la netbox standard intégrée à Tarantool. Cela peut être utile si, par exemple, votre passerelle souhaite demander directement au planificateur d'effectuer le travail maintenant, plutôt que d'attendre une journée.

Un autre point important est de garantir la tolérance aux pannes. La cartouche utilise le protocole SWIM pour surveiller la santé . En bref, les processus échangent des « rumeurs » entre eux via UDP : chaque processus informe ses voisins des dernières nouvelles et ceux-ci répondent. Si soudainement la réponse ne vient pas, Tarantool commence à soupçonner que quelque chose ne va pas, et après un moment, il récite la mort et commence à annoncer cette nouvelle à tout le monde.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Sur la base de ce protocole, Cartouche organise le traitement automatique des pannes. Chaque processus surveille son environnement et si le leader cesse soudainement de répondre, la réplique peut reprendre son rôle et Cartouche configure les rôles en cours d'exécution en conséquence.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Vous devez être prudent ici, car des allers-retours fréquents peuvent entraîner des conflits de données lors de la réplication. Bien entendu, vous ne devez pas activer le basculement automatique au hasard. Nous devons clairement comprendre ce qui se passe et être sûrs que la réplication ne sera pas interrompue une fois le leader restauré et la couronne lui restituée.

De tout cela, vous pouvez avoir l’impression que les rôles sont similaires aux microservices. Dans un sens, ils ne sont que cela, uniquement en tant que modules au sein des processus Tarantool. Mais il existe également un certain nombre de différences fondamentales. Premièrement, tous les rôles du projet doivent résider dans la même base de code. Et tous les processus Tarantool doivent être lancés à partir de la même base de code, afin qu'il n'y ait pas de surprises comme celles lorsque nous essayons d'initialiser le planificateur, mais il n'existe tout simplement pas. De plus, vous ne devez pas autoriser de différences dans les versions de code, car le comportement du système dans une telle situation est très difficile à prédire et à déboguer.

Contrairement à Docker, nous ne pouvons pas simplement prendre un rôle « image », 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 une seule instance. Un rôle existe ou il n’existe pas ; dans un sens, c’est un singleton. Et troisièmement, les rôles doivent être les mêmes au sein de l'ensemble du groupe de réplication, car sinon ce serait absurde : les données sont les mêmes, mais la configuration est différente.

Outils de déploiement

J'ai promis de montrer comment Cartouche aide à déployer des applications. Pour faciliter la vie des autres, le framework regroupe les packages RPM :

$ cartridge pack rpm myapp -- упакует для нас ./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 installées. Tarantool arrivera également sur le serveur en tant que dépendance du package RPM, et notre service est prêt à être lancé. Cela se fait via systemd, mais vous devez d'abord écrire une petite configuration. Au minimum, spécifiez l'URI de chaque processus. Trois suffisent par 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 ici une nuance intéressante. Au lieu de spécifier uniquement le port du protocole binaire, nous spécifions l'adresse publique complète du processus, y compris le nom d'hôte. Ceci est nécessaire pour que les nœuds du cluster sachent comment se connecter les uns aux autres. C'est une mauvaise idée d'utiliser 0.0.0.0 comme adresseadvert_uri ; il doit s'agir d'une adresse IP externe, pas d'une liaison de socket. Sans cela, rien ne fonctionnera, donc Cartouche ne vous permettra tout simplement pas de lancer un nœud avec le mauvaisadvert_uri.

Maintenant que la configuration est prête, vous pouvez démarrer les processus. Étant donné qu'une unité systemd standard ne permet pas de démarrer plus d'un processus, les applications sur la cartouche sont installées par ce qu'on appelle. unités instanciées qui fonctionnent comme ceci :

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

Dans la configuration, nous avons spécifié le port HTTP sur lequel Cartouche dessert l'interface Web - 8080. Allons-y et jetons un œil :

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Nous constatons que même si les processus sont en cours d'exécution, ils ne sont pas encore configurés. La cartouche ne sait pas encore qui doit se répliquer avec qui et ne peut pas prendre de décision seule, elle attend donc nos actions. Mais nous n’avons pas vraiment le choix : la vie d’un nouveau cluster commence par la configuration du premier nœud. Ensuite, nous ajouterons les autres au cluster, leur attribuerons des rôles, et à ce stade, le déploiement pourra être considéré comme terminé avec succès.

Versons un verre de votre boisson préférée et détendons-nous après une longue semaine de travail. L'application peut être utilisée.

Cartouche Tarantool : partitionnement du backend Lua en trois lignes

Les résultats de

Quels sont les résultats ? Essayez-le, utilisez-le, laissez des commentaires, créez des tickets sur Github.

références

Tarantool » 2.2 » Référence » Référence Rocks » Module vshard

Comment nous avons mis en œuvre le cœur de l'activité d'investissement d'Alfa-Bank basé sur Tarantool

Architecture de facturation nouvelle génération : transformation avec le passage à Tarantool

SWIM - protocole de construction de cluster

GitHub - tarantool/cartouche-cli

GitHub - tarantool/cartouche

Source: habr.com

Ajouter un commentaire