Cloister → gestion simple des clusters OTP

Presque toutes les applications commerciales réussies entrent tôt ou tard dans une phase où une mise à l’échelle horizontale est requise. Dans de nombreux cas, vous pouvez simplement démarrer une nouvelle instance et réduire la charge moyenne. Mais il existe également des cas moins triviaux où nous devons nous assurer que les différents nœuds se connaissent et répartissent soigneusement la charge de travail.

Cloister → gestion simple des clusters OTP

Il s'est avéré si chanceux que Erlang, que nous avons choisi pour sa syntaxe agréable et le battage médiatique qui l'entoure, a un prise en charge des systèmes distribués. En théorie, cela semble complètement trivial :

Le transfert de messages entre processus sur différents nœuds, ainsi qu'entre liens et moniteurs, est transparent […]

En pratique, tout est un peu plus compliqué. Distribué Erlang a été développé lorsque « conteneur » désignait une grande boîte en fer pour l'expédition, et « docker » était simplement synonyme de débardeur. DANS IP4 il y avait de nombreuses adresses inoccupées, les coupures de réseau étaient généralement causées par des rats rongeant le câble et la durée de disponibilité moyenne du système de production se mesurait en décennies.

Maintenant, nous sommes tous incroyablement autonomes, packagés et fonctionnant de manière distribuée. Erlang dans un environnement où les adresses IP dynamiques sont distribuées selon le principe d'un grand hasard, et où les nœuds peuvent apparaître et disparaître au gré du talon gauche de l'ordonnanceur. Pour éviter les piles de code passe-partout dans chaque projet exécutant un système distribué Erlang, pour lutter contre l’environnement hostile, il faut de l’aide.

Noter: je suis conscient qu'il y a libcluster. C'est vraiment cool, il a plus de mille étoiles, l'auteur est célèbre dans la communauté, et tout ça. Si les méthodes proposées par ce package pour créer et maintenir un cluster vous suffisent, je suis content pour vous. Malheureusement, j'ai besoin de beaucoup plus. Je souhaite contrôler la configuration en détail et ne pas être un spectateur extérieur sur le théâtre de la réorganisation des clusters.

Exigences

Ce dont j'avais personnellement besoin, c'était d'une bibliothèque qui prendrait en charge la gestion du cluster et aurait les propriétés suivantes :

  • travail transparent avec à la fois une liste de nœuds codée en dur et une découverte dynamique via les services Erlang;
  • rappel entièrement fonctionnel pour chaque changement de topologie (nœud là-bas, nœud ici, instabilité du réseau, scissions) ;
  • interface transparente pour lancer un cluster avec des noms longs et courts, comme avec :nonode@nohost;
  • Prise en charge Docker prête à l'emploi, sans avoir à écrire de code d'infrastructure.

Ce dernier signifie qu'après avoir testé l'application localement dans :nonode@nohost, ou dans un environnement artificiellement distribué en utilisant test_cluster_task, je veux juste courir docker-compose up --scale my_app=3 et voyez comment il exécute trois instances dans Docker sans aucune modification de code. Je veux aussi des applications dépendantes comme mnesia - lorsque la topologie change, en coulisses, ils reconstruisent le cluster en direct sans aucun coup de pouce supplémentaire de l'application.

Cloître n'était pas destiné à être une bibliothèque capable de tout, de la prise en charge d'un cluster à la préparation du café. Il ne s’agit pas d’une solution miracle visant à couvrir tous les cas possibles, ni d’une solution académiquement complète au sens où les théoriciens du CS mettre dans ce terme. Cette bibliothèque est conçue pour remplir un objectif très clair, mais remplit parfaitement son travail pas trop volumineux. Cet objectif sera d'assurer une transparence totale entre l'environnement de développement local et un environnement élastique distribué rempli de conteneurs hostiles.

Approche choisie

Cloître est destiné à être exécuté en tant qu'application, bien que les utilisateurs avancés puissent travailler manuellement sur l'assemblage et la maintenance du cluster en exécutant directement Cloister.Manager dans l'arborescence du superviseur de l'application cible.

Lorsqu'elle est exécutée en tant qu'application, la bibliothèque s'appuie sur config, à partir duquel il lit les valeurs de base suivantes :

config :cloister,
  otp_app: :my_app,
  sentry: :"cloister.local", # or ~w|n1@foo n2@bar|a
  consensus: 3,              # number of nodes to consider
                             #    the cluster is up
  listener: MyApp.Listener   # listener to be called when
                             #    the ring has changed

Les paramètres ci-dessus signifient littéralement ce qui suit : Cloître utilisé pour l'application OTP :my_apputilise découverte du service erlang pour connecter des nœuds, au moins trois, et MyApp.Listener module (mise en œuvre @behaviour Cloister.Listener) est configuré pour recevoir des notifications sur les modifications de topologie. Une description détaillée de la configuration complète peut être trouvée dans documentation.

Avec cette configuration, l'application Cloître volonté lancement par étapes, retardant le processus de démarrage de l'application principale jusqu'à ce qu'un consensus soit atteint (trois nœuds sont connectés et connectés, comme dans l'exemple ci-dessus.) Cela donne à l'application principale la possibilité de supposer qu'au démarrage, le cluster est déjà disponible. Chaque fois que la topologie change (il y en aura beaucoup, car les nœuds ne démarrent pas de manière complètement synchrone), le gestionnaire sera appelé MyApp.Listener.on_state_change/2. La plupart du temps, nous effectuons une action lorsque nous recevons un message d'état %Cloister.Monitor{status: :up}, ce qui signifie : « Bonjour, le cluster est assemblé. »

Dans la plupart des cas, l'installation consensus: 3 est optimal car même si nous nous attendons à ce que davantage de nœuds se connectent, le rappel passera par status: :rehashingstatus: :up sur tout nœud nouvellement ajouté ou supprimé.

Au démarrage en mode développement, il vous suffit de définir consensus: 1 и Cloître il évitera volontiers l'attente de l'assemblage du cluster lorsqu'il verra :nonode@nohostOu :node@hostOu :node@host.domain - selon la configuration du nœud (:none | :shortnames | :longnames).

Gestion des applications distribuées

Les applications distribuées qui ne fonctionnent pas en vase clos incluent généralement des dépendances distribuées, telles que mnesia. Il nous est facile de gérer leur reconfiguration à partir du même rappel on_state_change/2. Voici, par exemple, une description détaillée de la façon de reconfigurer mnesia à la volée documentation Cloître.

Le principal avantage de l'utilisation Cloître est qu'il effectue toutes les opérations nécessaires pour reconstruire le cluster après un changement de topologie sous la capuche. L'application s'exécute simplement dans un environnement distribué déjà préparé, avec tous les nœuds connectés, que nous connaissions à l'avance les adresses IP et donc les noms des nœuds, ou qu'ils aient été attribués/modifiés dynamiquement. Cela ne nécessite absolument aucun paramètre de configuration Docker spécial et du point de vue d'un développeur d'applications, il n'y a aucune différence entre une exécution dans un environnement distribué ou une exécution dans un environnement local. :nonode@nohost. Vous pouvez en savoir plus à ce sujet dans documentation.

Bien qu'une gestion complexe des changements de topologie soit possible grâce à une implémentation personnalisée MyApp.Listener, il peut toujours y avoir des cas extrêmes où ces limitations de bibliothèque et ces biais de configuration s'avèrent être les pierres angulaires de la mise en œuvre. C'est bon, prends juste ce qui précède libcluster, qui est plus généraliste, ou même gérer vous-même le cluster de bas niveau. Le but de cette bibliothèque de codes n'est pas de couvrir tous les scénarios possibles, mais d'utiliser le scénario le plus courant sans douleur inutile ni copier-coller fastidieux.

Note: à ce stade, dans l'original, il y avait l'expression « Joyeux clustering ! » et Yandex, avec lequel je traduis (je n'ai pas besoin de parcourir les dictionnaires moi-même), m'a proposé l'option « Joyeux clustering ! » Il est peut-être impossible d’imaginer une meilleure traduction, surtout à la lumière de la situation géopolitique actuelle.

Source: habr.com