ElasticSearch – Réindexation sans interruption de service grâce aux alias (zero downtime)

Lorsqu’un cluster ElasticSearch contient plusieurs nœuds, indexes, shards, replicas et surtout des millions, voire des milliards, de documents, il peut parfois être très long de réindexer tout le contenu.

En effet, il peut parfois arriver que l’on ait besoin de changer d’infrastructure technique (machine, virtualisation,…) ou que l’on veuille changer l’architecture du cluster ElasticSearch. Dans ce cas, comment maintenir notre service de recherche opérationnel 24h/24 et 7j/7 (SLA) tout en réalisant les opérations de maintenance désirées (zero downtime) ?

L’utilisation des alias ElasticSearch

Une fois encore les alias vont nous permettre de résoudre notre problématique. Un alias ElasticSearch peut être configuré de manière à pointer vers un ou plusieurs indexes d’un cluster tout en spécifiant des filtres ou des clés de routage.

Le point le plus important réside dans le fait que l’on peut changer la configuration d’un alias en une seule requête vers notre service d’indexation, et ainsi faire pointer un alias vers un ou plusieurs nouveaux indexes que l’on vient de reconstruire ou dont l’architecture vient de changer.

Ainsi, en utilisant systématiquement (bonne pratique) des alias (nom logique) à la place des noms d’indexes (nom physique) dans le code de nos applications clientes des services d’indexation, on pourra très facilement changer le ou les indexes cibles de nos recherches.

Exemple :

L’alias « produits » pointe vers les indexes « produits catégorie A » et « produits catégorie B » depuis que l’instance ElasticSearch a été démarrée. En une seule requête vers le cluster, on peut instantanément faire pointer notre alias « produits » vers les indexes « nouveaux produits catégorie A », « nouveaux produits catégorie B1 » et « nouveaux produits catégorie B2 » et sans interruption de services. On vient de changer par la même occasion notre découpage des indexes.

curl -XPOST localhost:9200/_aliases -d '
{
    "actions": [
        { "add": {
            "alias": "produits",
            "index": "produits_catégorie_A"
        }},
        { "add": {
            "alias": "produits",
            "index": "produits_catégorie_B"
        }}
    ]
}
'

puis

curl -XPOST localhost:9200/_aliases -d '
{
    "actions": [
        { "remove": {
            "alias": "produits",
            "index": "produits_catégorie_A"
        }},
        { "remove": {
            "alias": "produits",
            "index": "produits_catégorie_B"
        }},
        { "add": {
            "alias": "produits",
            "index": "nouveaux_produits_catégorie_A"
        }},
        { "add": {
            "alias": "produits",
            "index": "nouveaux_produits_catégorie_B1"
        }},
        { "add": {
            "alias": "produits",
            "index": "nouveaux_produits_catégorie_B2"
        }}
    ]
}
'

L’alias est la solution à privilégier pour garantir la haute disponibilité d’une application. Les alias permettent également d’organiser logiquement les indexes.

Voici un article sur le blog d’elasticsearch expliquant plus en détail ces problématiques et solutions : http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/

ElasticSearch – The Split Brain problem

Lorsqu’on met en place un cluster ElasticSearch, un problème très embêtant peut survenir si on ne configure par correctement le nombre de nœuds « master » dans ce dernier.

Split Brain

Le problème nommé « split brain » se produira plus aisément sur de petits clusters pour lesquels le nombre de nœuds « master » n’a pas correctement été calculé/configuré. Pour notre exemple, prenons un cluster avec deux nœuds répartis sur des sites géographiques différents et contenant chacun un index ElasticSearch constitué d’un shard et d’un replica.

split brainOn peut rencontrer le problème du « split brain » par exemple si une coupure réseau intervient pendant un temps suffisamment long pour que chacun de nos nœuds reçoive une exception ConnectTransportException.

  1. Pour fonctionner correctement, un cluster Elasticsearch a besoin d’un nœud maître qui détermine où et comment sont répartis les différents shards et réplicas. Le nœud 1 joue ce rôle. Il héberge la partition primaire (shard 0). Le nœud 2 qui est esclave, contient le réplica de la partition du nœud 1. Le réplica est une copie de la partition primaire et peut jouer le rôle de backup en cas de problème sur le nœud 1.
  2. Une coupure réseau survient entre les 2 nœuds.  Lorsqu’il tente de communiquer avec son nœud maître, le nœud 2 reçoit une ConnectTransportException. A plusieurs reprises, toutes les n secondes, le nœud 2 essaie de se reconnecter au cluster.  Mais en vain… Possédant un réplica complet de l’index, sans nœud voisin, le nœud 2 va alors s’autoproclamer maître. Deux « sous-clusters » existent alors sur le réseau, chacun étant capable de répondre à des requêtes de recherche et d’indexation de manière totalement indépendante.
  3. Une fois le réseau correctement rétabli, les 2 nœuds ne se réunissent pas en un seul et même cluster comme on pourrait le prévoir. Deux clusters indépendants coexistent parfaitement. C’est ce que l’on appelle le Split Brain. Cette situation entraîne une divergence progressive des données présentes sur chacun des sous-clusters, parfois les requêtes d’indexation arrivant sur le nœud 1, parfois sur le nœud 2.

La solution à ce problème est parfaitement connue. Il faut fixer le paramètre discovery.zen.minimum_master_node à N/2 + 1 (avec N le nombre de nœuds), soit 2 dans notre cas. Ce paramètre permet de spécifier que l’élection d’un nouveau nœud maître nécessite la majorité absolue. Coupé en deux, un cluster de 2 nœuds comme le notre n’aurait pas pu élire de nouveau maître.  Le Nœud 1 se serait mis en attente du rétablissement du réseau. Le cluster aurait perdu sa haute disponibilité : aucun des nœuds n’aurait pu répondre aux requêtes de recherche.

Avec 3 nœuds, l’isolement d’un nœud par rapport aux deux autres aurait permis de conserver un cluster parfaitement fonctionnel grâce à une bonne répartition des shards et réplicas.

Il faut également noter que le troisième nœud peut être configuré pour ne pas stocker de données (paramètre node.data à false). Il jouera alors simplement un rôle d’arbitre.

En paramétrant un nœud avec les configurations suivantes, on peut lui faire jouer un rôle de Load Balancer pour nos requêtes :

node.data : false
node.master : false

Une configuration intéressante pourrait être la suivante :

  1. Un nœud non master et sans data permettant de router les requêtes de recherche vers tous les nœuds contenant des données.
  2. Un nœud non master et sans data permettant d’indexer des données vers les nœuds contenant des données.
  3. Une configuration correcte du nombre minimale de nœud master.

Le but est ici de ne pas indexer et rechercher des données en passant par le même nœud. On augmente ainsi sensiblement les performances en répartissant ces actions sur différents nœuds. On profite également de la fonctionnalité de réplication de données pour les obtenir quelques secondes plus tard dans nos requêtes de recherches.

En utilisant une solution de type RabbitMQ on augmentera encore les performances applicatives en indexant les données de manière asynchrone.

Pour finir…

Pensez à l’utilisation du cluster Elasticsearch avec vagrant disponible sur github, pour vos tests ou vos POC : https://github.com/ypereirareis/vagrant-elasticsearch-cluster

Sources

Elasticsearch vagrant cluster, the easy way

En tant que fanatique d’elasticsearch, j’utilise régulièrement l’outil pour réaliser des POC, tester des applicatifs comme Logstash et Kibana, ou plus généralement, pour découvrir les extraordinaires fonctionnalités offertes par ElasticSearch (percolator, aggregations, sharding, scalability,…).

Cependant, il est souvent compliqué et chronophage de mettre en place un cluster ElasticSearch (vagrant cluster) complet simplement pour un POC ou des tests.

A l’inverse, utiliser la configuration minimale et par défaut d’ES (très rapide à mettre en place et qui fonctionne très bien) a ses limites lorsque l’on souhaite travailler en prenant en compte les problématiques suivantes :

  • Sharding sur plusieurs nœuds physiques (routing, filtres, rack, …).
  • Configuration réseau d’un cluster (unicast, multicast, ip:port, …)
  • Configuration de snapshot/recovery
  • Configuration spcécifiques (load balancer, data nodes et master nodes)
  • Simulation de coupure réseaux et autres incidents

J’ai donc décidé de créer un projet permettant de lancer un cluster elasticsearch de machines virtuelles (virtualbox/vagrant) en quelques minutes et en une seule commande bash :

vagrant up

Vous devriez alors obtenir un shell ressemblant à ceci :

$ vagrant up
Cluster size: 5
Cluster IP: 10.0.0.0
Bringing machine 'vm1' up with 'virtualbox' provider...
Bringing machine 'vm2' up with 'virtualbox' provider...
...

ElasticSearch Vagrant cluster

Voici le lien vers le dépôt github du projet : https://github.com/ypereirareis/vagrant-elasticsearch-cluster

La gestion du cluster se fait ensuite en utilisant vagrant et elasticsearch de manière classique. Tout est expliqué sur le dépôt (en anglais) :

  1. Pré-requis
  2. Installation
  3. Configuration
  4. Plugins

J’illustrerai, prochainement et de façon concrète, différentes configurations, problématiques ou bonnes pratiques à prendre en compte ou mettre en œuvre lorsque l’on travaille avec elasticsearch.

Le projet de cluster ES avec Vagrant vous permettra de rapidement reproduire les exemples et de modifier les configurations comme vous le souhaitez.

ElasticSearch – In production

Voici un très bon article en Anglais, concernant tous les aspects de l’utilisation d’ElasticSearch en production :

https://www.found.no/foundation/elasticsearch-in-production/

Memory
Search engines are designed to deliver answers fast. Really fast. To do this, most of the data structures they use must reside in memory. To a large extent, they assume you provide them with enough memory to do so. This can lead to problems when that is not the case – not just with performance, but also with your cluster’s reliability.
Security
Elasticsearch does not consider authentication or authorization to be its job (which is perfectly fine!), so it has no features for it. Thus, there are several things developers must be aware of, to avoid disclosing data that should be private, being denied service due to prohibitively expensive queries, or letting users run arbitrary code with access to anything Elasticsearch has access to.
Networking
Elasticsearch works brilliantly on a single machine, and easily lets you scale out to multiple machines when your data size requires it. It is impressively easy to use for a distributed system, but distributed systems are complicated – and can fail in many ways.
Client-side considerations
Assuming you have a reliable cluster, there are still some things you need to get right in your clients/applications to be reliable and performant.

https://www.found.no/foundation/elasticsearch-in-production/