Elasticsearch : bienvenue dans la nouvelle ère de la coordination de cluster

Elasticsearch est une solution dont la popularité ne cesse de croître, et pour cause : la facilité avec laquelle elle permet de faire évoluer les clusters est unique. Au cœur de cette solution réside le sous-système de coordination de cluster. La version 7 d’Elasticsearch intègre un nouveau sous-système de coordination de cluster qui offre de nombreux avantages par rapport aux versions précédentes. Dans cet article, nous nous pencherons sur les améliorations apportées à ce sous-système dans la version 7. Nous verrons notamment comment utiliser le nouveau sous-système, comment les modifications apportées impactent les mises à niveau à partir de la version 6, et comment ces améliorations vous évitent de mettre involontairement en péril vos données. Pour conclure, nous évoquerons brièvement la théorie décrivant le fonctionnement du nouveau sous-système.

La coordination de cluster, qu’est-ce que c’est ?

Un cluster Elasticsearch peut réaliser différentes tâches qui impliquent l’exécution simultanée d’un certain nombre de nœuds. Dans le cas d’une recherche, par exemple, celle-ci doit être acheminée vers l’ensemble des partitions appropriées pour pouvoir générer des résultats exacts. Autre exemple : lorsque vous indexez ou que vous supprimez des documents, chaque copie doit être mise à jour. De même, chaque requête client doit être transférée du nœud qui la réceptionne vers les nœuds capables de la prendre en charge. Les nœuds disposent chacun de leur propre aperçu du cluster, ce qui leur permet d’effectuer des recherches, de procéder à une indexation ou de réaliser d’autres activités coordonnées. On désigne cet aperçu par état du cluster. L’état du cluster détermine différents aspects, tels que les mappages et les paramètres de chaque index, les partitions attribuées à chaque nœud et les copies de partition synchronisées. Ces informations doivent impérativement rester cohérentes au sein du cluster. En effet, de cette cohérence dépend l’exécution correcte de nombreuses fonctionnalités récentes, telles que la réplication basée sur numéros de séquence et la réplication inter-clusters.

Le sous-système choisit tout d’abord un nœud qui sera le nœud maître du cluster. Ce nœud maître veille à ce que tous les nœuds du cluster reçoivent les mises à jour concernant l’état du cluster. Dit ainsi, cela semble simple. Mais la réalité est bien plus complexe, car les systèmes distribués comme Elasticsearch doivent être parés pour faire face à des situations inhabituelles. Les nœuds s’exécutent parfois lentement, s’interrompent pour récupérer de la mémoire, ou perdent soudainement de la puissance. Les réseaux peuvent connaître des problèmes de partitions, de pertes de paquets ou de latence élevée, ou encore, de messages qu’ils délivrent dans un ordre différent de celui de l’envoi. Ces problèmes peuvent se produire simultanément, et de façon intermittente. Malgré tout, le sous-système de coordination de cluster doit être capable de conserver la cohérence de la vue sur l’état du cluster pour chaque nœud.

Plus important, Elasticsearch doit pouvoir faire preuve de résilience en cas d’échec d’un nœud. Pour y parvenir, Elasticsearch considère que les mises à jour sur l’état du cluster sont réussies lorsqu’un quorum de nœuds les a acceptées. Un quorum est un sous-ensemble de nœuds choisis avec soin dans un cluster qui peuvent être potentiellement élus en tant que maîtres. Le fait de n’utiliser qu’un sous-ensemble de nœuds pour réagir présente un avantage : cela signifie que certains nœuds peuvent échouer sans que la disponibilité du cluster ne soit remise en cause. Un soin tout particulier doit être apporté au choix des nœuds du quorum : en effet, il ne faut pas que le cluster puisse élire deux nœuds maîtres indépendants prenant des décisions incohérentes. Vous risqueriez alors de perdre des données.

Ce que nous recommandons généralement, c’est de choisir trois nœuds éligibles par cluster, de sorte que si l’un des nœuds échoue, les deux autres puissent former malgré tout un quorum en toute sécurité et continuer à progresser. Si un cluster a moins de trois nœuds éligibles, il ne saurait tolérer la perte de l’un d’eux. À l’inverse, s’il y a plus de trois nœuds éligibles sur un cluster, les élections et les mises à jour de l’état du cluster risquent de prendre plus de temps.

Évolution ou révolution ?

Dans les versions 6.x et précédentes, Elasticsearch utilise un sous-système de coordination de cluster appelé Zen Discovery. Ce sous-système a évolué et gagné en maturité au fil des ans, et soutient des clusters de toutes tailles. Néanmoins, nous souhaitions y apporter des améliorations. Et ces améliorations ont eu un impact fondamental sur sa façon de fonctionner.

Avec Zen Discovery, l’utilisateur peut déterminer le nombre nécessaire de nœuds potentiellement éligibles en tant que maître pour former un quorum à l’aide du paramètre discovery.zen.minimum_master_nodes. Il est essentiel de configurer correctement ce paramètre sur chaque nœud et, plus important, de le mettre à jour au fur et à mesure de la montée en charge dynamique du cluster, car le système n’a pas la possibilité de déterminer si ce paramètre a été bien configuré ou non. Et on le sait bien : en pratique, on oublie facilement d’ajuster ce paramètre après l’ajout ou la suppression de nœuds. Pour essayer de pallier ce problème, Zen Discovery patiente quelques secondes à chaque élection de nœud maître, et fait généralement preuve de prudence dans d’autres cas également. Plus concrètement, cela signifie que, si le nœud maître élu échoue, le cluster sera indisponible pendant quelques secondes avant qu’un remplaçant ne soit élu à son tour. Il peut arriver également que le cluster n’élise pas de nœud maître. En général, il est très difficile de savoir pourquoi.

Pour Elasticsearch 7.0, nous avons repensé et reconçu le sous-système de coordination de cluster :

  • Le paramètre minimum_master_nodes est supprimé, pour laisser la possibilité à Elasticsearch de choisir les nœuds qui formeront le quorum. 
  • Les élections de maître se font en moins d’une seconde. 
  • L’expansion et le rétrécissement des clusters sont plus simples à réaliser, et plus sécurisés. Le risque de perte de données en raison d’une mauvaise configuration du système a été atténué. 
  • Les nœuds indiquent leur statut avec bien plus de clarté. Résultat : il est bien plus simple de déterminer pourquoi ils ne peuvent pas rejoindre un cluster ou pourquoi aucun maître ne peut être élu.

Au fur et à mesure que des nœuds sont ajoutés ou supprimés, Elasticsearch assure automatiquement un niveau optimal de tolérance aux défaillances en mettant à jour la configuration de vote du cluster. La configuration de vote est un ensemble de nœuds éligibles en tant que maître dont les votes sont comptabilisés lors de la prise d’une décision. En général, elle contient tous les nœuds pouvant être élus en tant que nœud maître dans le cluster. Un quorum représente tout simplement la majorité de la configuration de vote. De ce fait, pour qu’une mise à jour de l’état du cluster soit possible, il faut qu’elle soit décidée par plus de la moitié des nœuds faisant partie de la configuration de vote. Étant donné que c’est le système qui gère la configuration de vote, et donc les quorums, il peut éviter qu’une mauvaise configuration entraîne une perte de données, même en cas d’ajout ou de suppression des nœuds.

Si un nœud ne parvient pas à identifier un nœud maître et qu’il ne peut pas gagner une élection par lui-même, alors Elasticsearch version 7 consignera périodiquement un message d’avertissement décrivant son statut actuel avec suffisamment de détails pour aider à résoudre les problèmes courants.

Jusque-là, Zen Discovery disposait d’un mode de défaillance extrêmement rare, affiché sur la page relative au statut de résilience d’Elasticsearch par "Repeated network partitions can cause cluster state updates to be lost". Il n’est désormais plus proposé. Cet élément est marqué comme résolu.

Comment utiliser ce sous-système ?

Si vous utilisez des nœuds Elasticsearch venant d’être installés avec une configuration par défaut, ceux-ci rechercheront automatiquement d’autres nœuds s’exécutant sur le même hôte et s’associeront pour former un cluster au bout de quelques secondes. Si d’autres nœuds sont démarrés sur le même hôte, par défaut, ils découvriront le cluster et le rejoindront également. La mise en place d’un cluster à plusieurs nœuds est aussi simple avec la version 7.0 d’Elasticsearch qu’avec les versions précédentes.

Ce mécanisme entièrement automatique de formation de cluster fonctionne bien sur un hôte unique, mais il n’est pas suffisamment robuste pour être utilisé en production ou dans d’autres environnements distribués. Dans ces autres cas, le risque est que les nœuds ne se découvrent pas à temps et qu’ils forment plusieurs clusters indépendants à la place. À partir de la version 7.0, si vous souhaitez lancer un nouveau cluster avec des nœuds sur plusieurs hôtes, vous devez déterminer l’ensemble de base de nœuds éligibles en tant que maître que le cluster doit utiliser en tant que configuration de vote lors de la première élection. On parle alors de bootstrapping de cluster. Cette procédure n’est nécessaire qu’à la formation initiale du cluster. Les nœuds ayant déjà rejoint un cluster stockent la configuration de vote dans leurs dossiers de données et la réutilisent après un redémarrage. Les nœuds qui viennent d’être lancés et qui rejoignent un cluster existant peuvent recevoir ces informations par le maître élu du cluster.

Pour bootstrapper un cluster, définissez le paramètre cluster.initial_master_nodes setting sur les noms ou les adresses IP des nœuds pouvant être élus en tant que maître. Vous pouvez définir ce paramètre sur la ligne de commande ou dans le fichier elasticsearch.yml d’un ou de plusieurs nœuds éligibles. Vous devrez également configurer le sous-système de découverte afin que les nœuds puissent savoir comment se repérer les uns les autres.

Si le paramètre initial_master_nodes n’est pas défini, les tout nouveaux nœuds démarreront en s’attendant à découvrir un cluster existant. Or, si un nœud ne parvient pas à trouver de cluster auquel se joindre, il enverra périodiquement un message d’avertissement indiquant :

master not discovered yet, this node has not previously joined a bootstrapped (v7+) cluster,
and [cluster.initial_master_nodes] is empty on this node

Désormais, il n’y a plus de procédure particulière pour ajouter de nouveaux nœuds éligibles à un cluster. Configurez tout simplement les nouveaux nœuds pour qu’ils découvrent le cluster existant, démarrez-les, et le cluster adaptera sa configuration de vote de façon automatique et sécurisée lorsque les nouveaux nœuds le rejoindront. Vous pouvez également arrêter des nœuds en toute sécurité pour les supprimer, tant que vous ne stoppez pas la moitié (ou plus) des nœuds éligibles en même temps. Si vous devez stopper au moins la moitié des nœuds éligibles, ou si vous avez des besoins de montée en charge et d’orchestration plus complexes, il existe une procédure de montée en charge spécifique qui se sert d’une API pour ajuster la configuration de vote directement.

Comment faire la mise à niveau ?

Pour mettre à niveau un cluster Elasticsearch de la version 6 vers la version 7, deux options s’offrent à vous : la mise à niveau propagée ou le redémarrage intégral du cluster. Nous vous conseillons d’opter pour la mise à niveau propagée : celle-ci vous permet d’intervenir nœud par nœud tout en préservant la disponibilité du cluster. Vous devez au préalable faire évoluer votre cluster de la version 6 vers la version 6.7 avant d’effectuer une mise à niveau propagée vers la version 7. Dans le cadre d’un redémarrage intégral, vous pouvez effectuer la mise à niveau vers la version 7 à partir de n’importe quelle version 6.x, mais vous devrez stopper l’ensemble du cluster, pour ensuite le redémarrer. Quelle que soit l’option que vous choisissez, sachez que les changements apportés à Elasticsearch entre les versions 6 et la version 7 ne s’arrêtent pas aux améliorations de la coordination de cluster décrites ici. Il y en a bien d’autres. Pour que la mise à niveau se déroule de façon fluide, respectez scrupuleusement les instructions de mise à niveau détaillées.

Si vous effectuez une mise à niveau propagée, le bootstrapping du cluster interviendra automatiquement, en fonction du nombre de nœuds du cluster et du paramètre minimum_master_nodes existant. Il est donc crucial que ce paramètre soit correctement défini avant la mise à niveau. Dans le cas présent, il est inutile de définir le paramètre initial_master_nodes étant donné que le bootstrapping du cluster s’exécute automatiquement avec cette mise à niveau propagée. Les nœuds éligibles de la version 7 préféreront voter pour des nœuds de la version 6.7 lors de l’élection d’un maître. Vous pouvez donc vous attendre à ce qu’un nœud de la version 6.7 soit élu maître lors de la mise à niveau, et ce jusqu’à ce que vous ayez mis à niveau chacun des nœuds pouvant être élus en tant que maître.

Si vous optez pour un redémarrage intégral du cluster, vous devez alors procéder au bootstrapping du cluster comme indiqué ci-dessus : avant de démarrer le cluster venant d’être mis à niveau, vous devez tout d’abord définir le paramètre initial_master_nodes sur les noms ou les adresses IP des nœuds éligibles.

Dans les versions 6 et les versions précédentes, d’autres paramètres vous permettent de configurer le comportement de Zen Discovery dans l’espace de nom discovery.zen.*. Certains de ces paramètres n’ont plus d’utilité et ont donc été supprimés. Les autres ont été renommés. De ce fait, leur ancien nom n’est plus accepté dans la version 7, et vous devez donc ajuster votre configuration afin d’utiliser les nouveaux noms :

| Ancien nom | Nouveau nom | | --- | --- | | discovery.zen.ping.unicast.hosts | discovery.seed_hosts | | discovery.zen.hosts_provider | discovery.seed_providers | | discovery.zen.no_master_block | cluster.no_master_block |

Le nouveau sous-système de coordination de cluster intègre un nouveau mécanisme de détection des défaillances. Cela signifie donc que les paramètres de détection des défaillances de Zen Discovery de l’espace de nom discovery.zen.fd.* n’ont plus d’utilité. Il est conseillé d’utiliser la configuration de détection des défaillances par défaut des versions 7 et supérieures. Néanmoins, si vous avez besoin d’apporter des modifications, utilisez les paramètres cluster.fault_detection.*.

La sécurité avant tout

Les versions précédentes d’Elasticsearch présentaient un risque, et pas des moindres : celui de réaliser par inadvertance une série d’étapes mettant en péril la cohérence du cluster. À partir de la version 7, les choses changent : vous êtes averti que vous êtes sur le point de réaliser une action risquée et vous êtes invité à confirmer que vous souhaitez poursuivre.

Par exemple, un cluster Elasticsearch 7.0 ne sera pas automatiquement restauré si au moins la moitié des nœuds éligibles sont perdus de façon permanente. Une pratique courante consiste à avoir trois nœuds éligibles dans un cluster, ce qui permet à Elasticsearch de tolérer la perte de l’un d’eux sans qu’il y ait de période d’indisponibilité. En revanche, si deux d’entre eux sont définitivement perdus, le nœud restant ne peut plus progresser de façon sécurisée.

Dans les versions précédentes d’Elasticsearch, il était possible de restaurer un cluster après une telle situation, en démarrant de nouveaux nœuds vides et éligibles afin de remplacer ceux ayant été perdus. Toutefois, la restauration automatisée d’au moins la moitié des nœuds éligibles est une procédure non sécurisée, car aucun des nœuds restants n’est sûr de disposer d’une copie de l’état le plus récent du cluster. Cela peut entraîner une perte de données. Par exemple, une copie de partition peut avoir été supprimée de l’ensemble synchronisé. Si aucun des nœuds restants n’en a connaissance, c’est l’ancienne copie de partition qui sera allouée en tant que copie principale. Le gros problème, c’est que les utilisateurs ne se rendaient absolument pas compte que cette série d’étapes mettait leur cluster en péril. Il pouvait s’écouler plusieurs semaines ou mois avant qu’un utilisateur ne remarque une incohérence.

À partir d’Elasticsearch 7.0, ce risque est atténué dans une large mesure. Les clusters préfèrent être indisponibles plutôt que de prendre ce genre de risque. Dans le cas extrêmement rare où il n’y aurait aucune sauvegarde, il est tout de même possible de réaliser ce type d’opération non sécurisée, si cela s’avère absolument nécessaire. Il y aura quelques étapes supplémentaires pour confirmer que vous êtes conscient des risques encourus et éviter que cette opération non sécurisée soit réalisée par inadvertance.

Si vous avez perdu au moins la moitié des nœuds éligibles, essayez tout d’abord de ramener les nœuds éligibles en ligne. Si les répertoires de données des nœuds sont intacts, utilisez-les pour démarrer de nouveaux nœuds. Si cela est possible, le cluster se reformera en toute sécurité à l’aide de l’état le plus récent.

Deuxième action à tenter : restaurer le cluster à partir d’un instantané récent. Le cluster sera rétabli dans un état fonctionnel, mais les données écrites après la capture de l’instantané seront perdues. Vous pouvez toutefois indexer à nouveau les données manquantes, étant donné que vous connaissez la période de perte concernée. Les instantanés sont incrémentiels : vous pouvez donc en réaliser à fréquence régulière. Une pratique courante consiste à capturer un instantané toutes les 30 minutes pour limiter le volume de données perdues lors d’une telle restauration.

Enfin, si aucune de ces procédures de restauration ne fonctionne, votre dernier recours est d’employer l’outil de restauration non sécurisé elasticsearch-node. Il s’agit d’un outil de ligne de commande pouvant être utilisé par un administrateur pour réaliser des actions non sécurisées, comme l’élection d’un ancien maître à partir d’une minorité. En explicitant les étapes pouvant nuire à la cohérence, Elasticsearch 7.0 élimine le risque de perte involontaire de données suite à une série d’opérations non sécurisées.

Comment ça fonctionne ?

Si vous connaissez la théorie des systèmes distribués, vous serez d’accord pour dire que la coordination de cluster est un exemple de problème pouvant être résolu à l’aide d’un consensus distribué.  La notion de "consensus distribué" n’était pas encore bien connue au moment où le développement d’Elasticsearch a démarré. Les technologies de pointe ont cependant considérablement progressé ces dernières années.

Zen Discovery a repris de nombreuses idées des algorithmes de consensus distribué, mais l’a fait de façon organique plutôt que de respecter strictement le modèle prescrit par la théorie. Ce sous-système est également très prudent par rapport aux délais d’expiration, ce qui fait que sa restauration peut être très lente après un échec. L’introduction d’un nouveau sous-système de coordination de cluster dans la version 7.0 nous donne la possibilité de suivre le modèle théorique de façon plus étroite.

La coordination distribuée est en effet un problème connu pour être difficile à résoudre. Nous nous appuyons sur des méthodes formelles pour valider nos conceptions en amont, avec des outils automatisés qui nous donnent des garanties solides quant à l’exactitude et à la sécurité. Vous trouverez les caractéristiques formelles du nouvel algorithme de coordination de cluster d’Elasticsearch dans notre référentiel public de modèles formels d’Elasticsearch. Le module de sécurité principal de l’algorithme est simple et concis. Il existe une correspondance "un à un" directe entre le modèle formel et le code de production dans le référentiel d’Elasticsearch.

Si vous avez l’habitude de voir des algorithmes de consensus distribué, comme Paxos, Raft, Zab et Viewstamped Replication (VR), alors le module de sécurité principal vous semblera familier. Il modélise un registre réinscriptible unique et s’appuie sur une notion de terme maître mettant en parallèle les bulletins de vote Paxos, les termes de Raft et les vues de VR. Le module de sécurité principal et son modèle formel prennent également en charge le bootstrapping du cluster, la persistance sur les redémarrages de nœud et la reconfiguration dynamique. Toutes ces fonctionnalités sont importantes pour veiller à ce que le système se comporte correctement quelles que soient les circonstances.

Nous mettons aussi en place une couche garantissant la vivacité, le but étant qu’après un échec, un maître soit élu et capable de publier les mises à jour sur l’état du cluster une fois le réseau restauré et le nombre de nœuds suffisant mis en ligne. Cette couche de vivacité s’appuie sur un certain nombre de techniques de pointe pour éviter la plupart des problèmes fréquemment rencontrés. Le planificateur d’élections est adaptable, ce qui signifie qu’il modifie son comportement en fonction des conditions du réseau pour éviter qu’un nombre excessif d’élections soient contestées. Un tour avant vote de style Raft élimine les élections qui ne peuvent être remportées avant qu’elles ne commencent, ce qui évite les interruptions par des nœuds hostiles. La détection de la latence empêche que les nœuds n’interrompent le cluster s’ils échouent trop loin du maître. La détection bidirectionnelle active des défaillances veille à ce que les nœuds du cluster puissent toujours communiquer les uns avec les autres. La plupart des mises à jour sur l’état du cluster sont publiées avec efficacité en tant que petits diffs, ce qui évite d’avoir à copier l’intégralité de l’état du cluster nœud par nœud. Les leaders qui sont interrompus abdiqueront explicitement en faveur d’un successeur de leur choix, ce qui réduit l’indisponibilité lors d’un basculement volontaire étant donné qu’une élection complète devient inutile. Nous avons élaboré une infrastructure de test pour simuler efficacement les effets des interruptions pathologiques pouvant durer plusieurs secondes, minutes ou heures. Nous avons pu vérifier ainsi que le cluster se restaurait toujours rapidement une fois l’interruption résolue.

Et pourquoi pas choisir Raft ?

Une question que l’on nous pose souvent est de savoir pourquoi nous ne nous contentons tout simplement pas d’utiliser un algorithme de consensus distribué standard tel que Raft. Il existe un certain nombre d’algorithmes bien connus, offrant chacun différents compromis. Nous avons procédé à des évaluations minutieuses et nous nous sommes inspirés des documents que nous avons pu trouver. L’une de nos premières preuves de faisabilité se servait d’un protocole extrêmement proche de Raft. Nous avons appris de cette expérience que les changements nécessaires pour que l’intégration à Elasticsearch soit totale s’avéraient assez conséquents. La plupart des algorithmes standard recommandent également des décisions de conception qui ne seraient pas optimales pour Elasticsearch. Par exemple :

  • Leur structure est souvent basée sur un log d’opérations, alors que la coordination de cluster d’Elasticsearch se base plus naturellement sur l’état du cluster. Il est ainsi possible d’effectuer des optimisations cruciales, comme le batching (qui combine des opérations connexes en une diffusion unique), bien plus facilement que si la coordination était basée sur les opérations.
  • Ces algorithmes ont souvent une capacité plutôt limitée pour agrandir ou restreindre un cluster. Ils doivent passer par un certain nombre d’étapes pour réaliser des tâches de maintenance. A contrario, la coordination de cluster d’Elasticsearch peut exécuter des reconfigurations arbitraires en une seule étape de façon sécurisée. Le système environnant s’en trouve simplifié car il n’y a pas d’états intermédiaires problématiques.
  • Bien souvent, ils mettent massivement l’accent sur la sécurité, laissent en suspens la façon dont ils assurent la vivacité et ne décrivent pas la réaction que devrait avoir le cluster s’il identifie un nœud malveillant. Les contrôles d’intégrité d’Elasticsearch sont complexes. Ils ont été utilisés et affinés sur le terrain pendant de nombreuses années, et il était important pour nous qu’Elasticsearch garde le même comportement. En réalité, ça a été bien plus facile de mettre en place les propriétés de sécurité du système que d’en assurer la vivacité. De ce fait, lors de la mise en œuvre, nos efforts se sont concentrés sur les propriétés de vivacité du système.
  • L’un des objectifs du projet était de réaliser une mise à niveau propagée avec un temps d’indisponibilité nul, depuis un cluster 6.7 exécutant Zen Discovery, vers un cluster version 7 exécutant le nouveau sous-système de coordination. Il ne semblait pas réalisable d’adapter l’un de ces algorithmes standard en un algorithme permettant ce type de mise à niveau.

La mise en œuvre industrielle totale d’un algorithme de consensus distribué implique un gros travail de développement, et doit aller au-delà des documents universitaires. Dans la pratique, des personnalisations s’avèreront nécessaires, c’est inévitable. Néanmoins, les protocoles de coordination sont complexes et ces personnalisations risquent de créer des erreurs. Du point de vue de l’ingénierie, traiter ces personnalisations comme si l’on développait un nouveau protocole était une question de bon sens.

Récapitulons

Elasticsearch 7.0 intègre un nouveau sous-système de coordination de cluster plus rapide, plus sécurisé et plus simple d’utilisation. Il prend en charge des mises à niveau propagée préservant la disponibilité du cluster à partir de la version 6.7 et offre une base à la réplication de données résiliente. Pour tester ce nouveau sous-système de coordination de cluster, téléchargez la dernière version bêta 7.0, consultez les docs, jouez avec et faites-nous part de vos commentaires.