Opérateur Tarantool Kubernetes



Kubernetes est déjà devenu une norme de facto pour l'exécution d'applications sans état, principalement parce qu'il peut réduire le délai de commercialisation des nouvelles fonctionnalités. Le lancement d'applications avec état, telles que des bases de données ou des microservices avec état, est toujours une tâche complexe, mais les entreprises doivent faire face à la concurrence et maintenir un taux de livraison élevé. Ils créent donc une demande pour de telles solutions.

Nous voulons présenter notre solution pour le lancement de clusters de cartouches Tarantool avec état: Tarantool Kubernetes Operator , plus sous la coupe.

  1. Au lieu de mille mots
  2. Ce que fait réellement l'opérateur
  3. Un peu sur les détails
  4. Comment fonctionne l'opérateur
  5. Ce que l'opérateur crée
  6. Résumé

Tarantool est un SGBD open source et un serveur d'applications tout-en-un. En tant que base de données, elle présente de nombreuses caractéristiques uniques: haute efficacité d'utilisation du matériel, schéma de données flexible, prise en charge du stockage en mémoire et sur disque, et possibilité d'extension à l'aide du langage Lua. En tant que serveur d'applications, il vous permet de déplacer le code d'application aussi près que possible des données avec un temps de réponse minimum et un débit maximum. De plus, Tarantool dispose d'un écosystème étendu fournissant des modules prêts à l'emploi pour résoudre les problèmes d'application: sharding , queue , modules pour un développement facile ( cartouche , luatest ), solutions de fonctionnement ( métriques , ansible ), pour n'en nommer que quelques-uns.

Pour tous ses avantages, les capacités d'une seule instance de Tarantool sont toujours limitées. Il faudrait créer des dizaines et des centaines d'instances pour stocker des téraoctets de données et traiter des millions de requêtes, ce qui implique déjà un système distribué avec tous ses problèmes typiques. Pour les résoudre, nous avons Tarantool Cartridge , qui est un framework conçu pour cacher toutes sortes de difficultés lors de l'écriture d'applications distribuées. Il permet aux développeurs de se concentrer sur la valeur commerciale de l'application. La cartouche fournit un ensemble robuste de composants pour l'orchestration automatique des clusters, la distribution automatique des données, l'interface utilisateur Web pour le fonctionnement et les outils de développement.

Tarantool ne concerne pas seulement les technologies, mais également une équipe d'ingénieurs travaillant sur le développement de systèmes d'entreprise clé en main, de solutions prêtes à l'emploi et de support pour les composants open-source.

Globalement, toutes nos tâches peuvent être divisées en deux domaines: le développement de nouveaux systèmes et l'amélioration des solutions existantes. Par exemple, il existe une base de données complète d'un fournisseur bien connu. Pour le mettre à l'échelle pour la lecture, un cache cohérent basé sur Tarantool est placé derrière lui. Ou vice versa: pour mettre à l'échelle l'écriture, Tarantool est installé dans la configuration chaud / froid: pendant que les données sont en train de «refroidir», elles sont transférées vers le stockage froid et en même temps dans la file d'attente d'analyse. Ou une version allégée d'un système existant est écrite (sauvegarde fonctionnelle) pour sauvegarder les données "à chaud" en utilisant la réplication des données du système principal. En savoir plus sur les rapports de la conférence T + 2019 .

Tous ces systèmes ont une chose en commun: ils sont quelque peu difficiles à utiliser. Eh bien, il y a beaucoup de choses intéressantes: créer rapidement un cluster de plus de 100 instances de sauvegarde dans 3 centres de données; pour mettre à jour l'application qui stocke les données sans interruption ni maintenance. créer une sauvegarde et une restauration afin de se préparer à un éventuel accident ou erreur humaine; pour assurer le basculement des composants cachés; organiser la gestion de la configuration ...

La cartouche Tarantool qui vient littéralement de sortir en open source simplifie considérablement le développement du système distribué: elle prend en charge le clustering de composants, la découverte de services, la gestion de la configuration, la détection des échecs d'instance et le basculement automatique, la gestion de la topologie de réplication et le partage des composants.
Ce serait formidable si nous pouvions gérer tout cela aussi rapidement que le développer. Kubernetes le permet, mais un opérateur spécialisé rendrait la vie encore plus confortable.

Aujourd'hui, nous présentons la version alpha de Tarantool Kubernetes Operator.

Au lieu de mille mots


Nous avons préparé un petit exemple basé sur la cartouche Tarantool, et nous allons travailler avec. Il s'agit d'une application simple appelée stockage de valeurs-clés distribué avec interface HTTP. Après le démarrage, nous avons les éléments suivants:



O Where

  • Les routeurs font partie du cluster chargé d'accepter et de traiter les requêtes HTTP entrantes;
  • Les stockages font partie du cluster responsable du stockage et du traitement des données; trois fragments sont installés hors de la boîte, chacun ayant un maître et une réplique.

Pour équilibrer le trafic HTTP entrant sur les routeurs, une entrée Kubernetes est utilisée. Les données sont distribuées dans le stockage au niveau de Tarantool lui-même à l'aide du composant vshard .

Nous avons besoin de Kubernetes 1.14+, mais le minikube fera l'affaire. C'est aussi agréable d'avoir du kubectl . Pour démarrer l'opérateur, créez un ServiceAccount, un rôle et un RoleBinding:

$ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/service_account.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/role.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/role_binding.yaml 

Tarantool Operator étend l'API Kubernetes avec ses définitions de ressources, alors créons-les:

 $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/crds/tarantool_v1alpha1_cluster_crd.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/crds/tarantool_v1alpha1_role_crd.yaml $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/crds/tarantool_v1alpha1_replicasettemplate_crd.yaml 

Tout est prêt pour démarrer l'opérateur, alors voilà:

 $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/deploy/operator.yaml 

Nous attendons que l'opérateur démarre, puis nous pouvons commencer le démarrage de l'application:

 $ kubectl create -f https://raw.githubusercontent.com/tarantool/tarantool-operator/0.0.1/examples/kv/deployment.yaml 

Une entrée est déclarée sur l'interface utilisateur Web dans le fichier YAML avec l'exemple; il est disponible dans cluster_ip/admin/cluster . Lorsqu'au moins un pod Ingress est prêt et en cours d'exécution, vous pouvez vous y rendre pour voir comment de nouvelles instances sont ajoutées au cluster et comment sa topologie change.
Nous attendons que le cluster soit utilisé:

 $ kubectl describe clusters.tarantool.io examples-kv-cluster 

Nous attendons le statut de cluster suivant:

 … Status: State: Ready … 

C'est tout, et l'application est prête à l'emploi!

Avez-vous besoin de plus d'espace de stockage? Ensuite, ajoutons quelques fragments:

 $ kubectl scale roles.tarantool.io storage --replicas=3 

Si les fragments ne peuvent pas gérer la charge, augmentons le nombre d'instances dans le fragment en modifiant le modèle de jeu de réplicas:

 $ kubectl edit replicasettemplates.tarantool.io storage-template 

Fixons la valeur .spec.replicas à deux afin d'augmenter le nombre d'instances dans chaque jeu de réplicas à deux.

Si un cluster n'est plus nécessaire, supprimez-le simplement avec toutes les ressources:

 $ kubectl delete clusters.tarantool.io examples-kv-cluster 

Quelque chose a mal tourné? Créez un ticket et nous travaillerons rapidement dessus.

Ce que fait réellement l'opérateur


Le démarrage et le fonctionnement du cluster Tarantool Cartridge sont une histoire de réalisation d'actions spécifiques dans un ordre spécifique à un moment spécifique.

Le cluster lui-même est géré principalement via l'API d'administration: GraphQL sur HTTP. Vous pouvez sans aucun doute descendre à un niveau inférieur et donner des commandes directement via la console, mais cela ne se produit pas très souvent.
Par exemple, voici comment le cluster démarre:

  1. Nous déployons le nombre requis d'instances Tarantool, par exemple, à l'aide de systemd.
  2. Ensuite, nous connectons les instances en tant que membres:

     mutation { probe_instance: probe_server(uri: "storage:3301") } 
  3. Ensuite, nous attribuons les rôles aux instances et spécifions les identificateurs d'instance et de jeu de réplicas. L'API GraphQL est utilisée à cet effet:

     mutation { join_server( uri:"storage:3301", instance_uuid: "cccccccc-cccc-4000-b000-000000000001", replicaset_uuid: "cccccccc-0000-4000-b000-000000000000", roles: ["storage"], timeout: 5 ) } 
  4. inally, nous bootstrap le composant responsable du sharding en utilisant l'API:

     mutation { bootstrap_vshard cluster { failover(enabled:true) } } 

Facile, non?

Tout est plus intéressant en matière d'extension de cluster. Le rôle Routeurs de l'exemple évolue facilement: créez plus d'instances, joignez-les à un cluster existant, et vous avez terminé! Le rôle Stockages est quelque peu plus délicat. Le stockage est partagé, donc lors de l'ajout / suppression d'instances, il est nécessaire de rééquilibrer les données en les déplaçant respectivement vers / depuis les instances nouvelles / supprimées. Ne pas le faire entraînerait soit des instances sous-chargées, soit des données perdues. Et s'il n'y en a pas un, mais une douzaine de clusters avec des topologies différentes?

En général, c'est tout ce que Tarantool Operator gère. L'utilisateur décrit l'état nécessaire du cluster de cartouches Tarantool, et l'opérateur le traduit en un ensemble d'actions appliquées aux ressources K8 et en certains appels à l'API administrateur du cluster Tarantool dans un ordre spécifique à un moment spécifique. Il essaie également de cacher tous les détails à l'utilisateur.

Un peu sur les détails


Lorsque vous travaillez avec l'API d'administrateur de cluster Tarantool Cartridge, l'ordre des appels et leur destination sont essentiels. Pourquoi ça?

Tarantool Cartridge contient son stockage de topologie, son composant de découverte de service et son composant de configuration. Chaque instance du cluster stocke une copie de la topologie et de la configuration dans un fichier YAML.

 servers: d8a9ce19-a880-5757-9ae0-6a0959525842: uri: storage-2-0.examples-kv-cluster:3301 replicaset_uuid: 8cf044f2-cae0-519b-8d08-00a2f1173fcb 497762e2-02a1-583e-8f51-5610375ebae9: uri: storage-0-0.examples-kv-cluster:3301 replicaset_uuid: 05e42b64-fa81-59e6-beb2-95d84c22a435 … vshard: bucket_count: 30000 ... 

Les mises à jour sont appliquées de manière cohérente à l'aide du mécanisme de validation en deux phases . Une mise à jour réussie nécessite un quorum de 100%: chaque instance doit répondre. Sinon, il revient en arrière. Qu'est-ce que cela signifie en termes de fonctionnement? En termes de fiabilité, toutes les demandes à l'API administrateur qui modifient l'état du cluster doivent être envoyées à une seule instance, ou au leader, car sinon nous risquons d'obtenir des configurations différentes sur des instances différentes. Tarantool Cartridge ne sait pas comment faire une élection de leader (pas encore), mais Tarantool Operator peut, et pour vous, c'est juste un fait amusant, car l'opérateur fait tout.

Chaque instance doit également avoir une identité fixe, c'est-à-dire un ensemble d' instance_uuid et replicaset_uuid , ainsi que advertise_uri . Si soudainement un stockage redémarre et que l'un de ces paramètres change, vous courez le risque de briser le quorum, et l'opérateur en est responsable.

Comment fonctionne l'opérateur


Le but de l'opérateur est d'amener le système dans l'état défini par l'utilisateur et de maintenir le système dans cet état jusqu'à ce que de nouvelles directions soient données. Pour que l'opérateur puisse travailler, il lui faut:

  1. La description de l'état du système.
  2. Le code qui mettrait le système dans cet état.
  3. Un mécanisme pour intégrer ce code dans les k8 (par exemple, pour recevoir des notifications de changement d'état).

Le cluster de cartouches Tarantool est décrit en termes de k8 utilisant une définition de ressource personnalisée (CRD) . L'opérateur aurait besoin de trois ressources personnalisées réunies sous le groupe tarantool.io/v1alpha:

  • Le cluster est une ressource de niveau supérieur qui correspond à un seul cluster de cartouches Tarantool.
  • Le rôle est un rôle d'utilisateur en termes de cartouche Tarantool.
  • Le modèle Replicaset est un modèle pour créer des StatefulSets (je vous dirai un peu plus tard pourquoi ils sont avec état; à ne pas confondre avec K8s ReplicaSet).

Toutes ces ressources reflètent directement le modèle de description du cluster de cartouches Tarantool. Le fait d'avoir un dictionnaire commun facilite la communication avec les développeurs et la compréhension de ce qu'ils aimeraient voir en production.

Le code qui amène le système à l'état donné est le contrôleur en termes de K8. Dans le cas de Tarantool Operator, il existe plusieurs contrôleurs:

  • Le Cluster Controller est responsable de l'interaction avec le cluster Tarantool Cartridge; il connecte les instances au cluster et déconnecte les instances du cluster.
  • Le contrôleur de rôle est le contrôleur de rôle utilisateur chargé de créer des StatefulSets à partir du modèle et d'en conserver le nombre prédéfini.

À quoi ressemble un contrôleur? C'est un ensemble de codes qui met progressivement le monde autour de lui. Un Cluster Controller ressemblerait schématiquement à:



Un point d'entrée est un test pour voir si une ressource de cluster correspondante existe pour un événement. Existe-t-il? «Non» signifie quitter. «Oui» signifie passer au bloc suivant et s'approprier les rôles d'utilisateur. Lorsque la propriété d'un rôle est prise, elle se termine et recommence la deuxième fois. Il continue indéfiniment jusqu'à ce qu'il prenne la propriété de tous les rôles. Lorsque la propriété est prise, il est temps de passer au prochain bloc d'opérations. Et le processus se poursuit jusqu'au dernier bloc. Après cela, nous pouvons supposer que le système contrôlé est dans l'état défini.

En général, tout est assez simple. Cependant, il est important de déterminer les critères de réussite pour réussir chaque étape. Par exemple, l'opération de jointure de cluster n'est pas considérée comme réussie lorsqu'elle renvoie hypothetic success = true, mais lorsqu'elle renvoie une erreur comme «déjà joint».

Et la dernière partie de ce mécanisme est l'intégration du contrôleur avec les K8. D'un point de vue d'oiseau, l'ensemble du K8 se compose d'un ensemble de contrôleurs qui génèrent des événements et y répondent. Ces événements sont organisés en files d'attente auxquelles nous pouvons nous abonner. Cela ressemblerait schématiquement à:



L'utilisateur appelle kubectl create -f tarantool_cluster.yaml et la ressource de cluster correspondante est créée. Le contrôleur de cluster est informé de la création de la ressource de cluster. Et la première chose qu'il essaie de faire est de trouver toutes les ressources de rôle qui devraient faire partie de ce cluster. Si tel est le cas, il attribue le cluster en tant que propriétaire du rôle et met à jour la ressource de rôle. Le contrôleur de rôle reçoit une notification de mise à jour de rôle, comprend que la ressource a son propriétaire et commence à créer des StatefulSets. Voici comment cela fonctionne: le premier événement déclenche le second, le deuxième événement déclenche le troisième, et ainsi de suite jusqu'à ce que l'un d'eux s'arrête. Vous pouvez également définir un déclencheur temporel, par exemple, toutes les 5 secondes.

Voici comment est organisé l'opérateur: nous créons une ressource personnalisée et écrivons le code qui répond aux événements liés aux ressources.

Ce que l'opérateur crée


Les actions de l'opérateur aboutissent finalement à la création de pods et conteneurs K8s. Dans le cluster Tarantool Cartridge déployé sur les K8, tous les pods sont connectés aux StatefulSets.

Pourquoi StatefulSet? Comme je l'ai mentionné précédemment, chaque instance de Tarantool Cluster conserve une copie de la topologie et de la configuration du cluster. Et de temps en temps, un serveur d'applications dispose d'un espace dédié, par exemple, pour les files d'attente ou les données de référence, et c'est déjà un état complet. StatefulSet garantit également que les identités de pod sont préservées, ce qui est important lors du clustering des instances: les instances doivent avoir des identités fixes, sinon nous risquons de perdre le quorum au redémarrage.

Lorsque toutes les ressources du cluster sont prêtes et dans l'état souhaité, elles reflètent la hiérarchie suivante:



Les flèches indiquent la relation propriétaire-dépendante entre les ressources. Il est nécessaire, par exemple, que le ramasse-miettes nettoie après la suppression du cluster.

En plus des StatefulSets, Tarantool Operator crée un service sans tête pour l'élection du leader, et les instances communiquent entre elles via ce service.

Tarantool Operator est basé sur Operator Framework , et le code opérateur est écrit en Golang, il n'y a donc rien de spécial ici.

Résumé


C'est à peu près tout ce qu'il y a à faire. Nous attendons vos commentaires et vos billets. Nous ne pouvons pas nous en passer - c'est la version alpha après tout. Quelle est la prochaine étape? La prochaine étape est beaucoup de polissage:

  • Unité, tests E2E;
  • Tests de Chaos Monkey;
  • tests de résistance;
  • sauvegarde / restauration;
  • fournisseur de topologie externe.

Chacun de ces sujets est vaste en soi et mérite un article séparé, alors attendez les mises à jour!

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


All Articles