Il y a 3 années · 12 minutes · Back

Introduction et démystification de Zookeeper

zookeeper

Je vous propose de passer un peu de temps ensemble afin de découvrir Zookeeper que l’on retrouve régulièrement comme la pierre angulaire permettant de rendre de nombreux projets open source distribuables sur plusieurs serveurs. L’objectif est de comprendre comment fonctionne Zookeeper dans les grandes lignes et de pouvoir s’en servir en tant que développeur.

Dans les fonctionnalités offertes par Zookeeper on trouve du Naming Service, de la gestion de configuration, de la synchronisation, Leader Election… Zookeeper est en fait comme une boîte à outils que vous pouvez utiliser dès que coordonner des process déployés sur plusieurs serveurs devient un casse-tête.

Le plan d’attaque pour aborder Zookeeper sera le suivant:

  • comment fonctionne Zookeeper et un aperçu des termes à connaître,
  • installer une instance de Zookeeper en local et… s’apercevoir que c’est facile,
  • installer plusieurs instances de Zookeeper en local et… s’apercevoir que c’est facile

Comment fonctionne Zookeeper et un aperçu des termes à connaître

Zookeeper fonctionne en fournissant un espace mémoire partagé par toutes les instances d’un même ensemble de serveurs Zookeeper. Cet espace mémoire est hiérarchique, à la manière d’un système de fichier composé de répertoires et de fichiers à la différence que, dans le cas de Zookeeper, on ne parle pas de répertoires et de fichiers mais de nœuds. Chaque nœud s’appelle un ZNode.
Lorsque l’on démarre une instance Zookeeper, la hiérarchie de nœuds est vide et nous n’avons alors qu’un nœud racine qui, à l’instar des systèmes fichiers, se nomme « / » :

Zookeeper nous laisse la liberté de créer de nouveaux ZNode et de stocker des données dedans. Par exemple : ajoutons un ZNode que l’on appellera « xebia ». Nous obtenons alors l’arborescence suivante :

A l’intérieur de ce ZNode /xebia nous pouvons ajouter des données. Par exemple la valeur « Hello » :

Voilà… C’est la première étape. Maintenant pour que cela prenne tout son sens, l’idée c’est de se dire que cette hiérarchie de nœuds va se répliquer et se synchroniser sur tous les serveurs sur lesquels nous aurons installé Zookeeper.

Un serveur Zookeeper standalone a un intérêt qui se limite au test/développement en local. Zookeeper prend tout son sens lorsqu’on le déploie sur plusieurs serveurs ; on appelle cela un ensemble :

Le fonctionnement d’un ensemble Zookeeper nécessite l’élection d’un leader parmi les instances qui le composent. Lorsqu’un client écrit/modifie des données dans les ZNodes, c’est le leader qui effectue l’opération puis la transmet aux autres membres. Une fois qu’un certain nombre d’instances ont appliqué l’opération, cette dernière est considérée comme valide au sein de l’ensemble Zookeeper. Ledit nombre est appelé le quorum. Comme lors d’une élection au sein d’un groupe de personnes, le quorum représente le nombre minimum d’instances Zookeeper pour qu’une décision (typiquement, décider si la valeur affectée dans un ZNode est validée sur l’ensemble Zookeeper) soit prise par l’ensemble Zookeeper.

Par exemple si l’on a un ensemble de 3 serveurs, cela signifie que si au moins 2 serveurs dans l’ensemble Zookeeper sont d’accord sur une opération d’écriture, celle-ci est considérée valide.

Oui, mais comment connaît-on / détermine t-on la valeur du quorum ?

Le calcul du quorum s’effectue selon la formule suivante : quorum = n/2

  • avec n le nombre de serveurs présents dans l’ensemble Zookeeper
  • sachant que l’on arrondira toujours le résultat à la valeur supérieure (par exemple 3/2 = 1.5 => quorum = 2)
  • on ajoute +1 si la valeur ne représente pas une majorité stricte (par exemple 2/2 = 1 => quorum = 2)

Donc, si on crée un petit tableau rapide avec le nombre de serveurs et le quorum nécessaire voilà ce que nous obtenons:

Instance ZK Quorum
1 1
2 2
3 2
4 3
5 3
6 4
7 4
8 5

Lorsque l’ensemble est composé de 2 instances, le quorum est également de 2 car une seule instance ne représente pas une majorité stricte : non utilisable, puisque perdre un serveur revient à perdre notre service Zookeeper.
Avec 3 instances dans l’ensemble, le quorum reste à 2 (2 sur 3 représente bien une majorité stricte). Dans ce cas, si nous perdons un serveur, le service Zookeeper reste opérationnel. Un ensemble de 2 serveurs est donc le minimum pour avoir un service Zookeeper résistant aux pannes.
4 instances n’est pas intéressant dans la mesure où cela fait un quorum de 3, n’offre aucune plus-value par rapport à un ensemble de 3 : nous ne pouvons pas perdre plus d’une instance, donc nous n’avons pas amélioré notre robustesse par rapport à un ensemble de 3.
Finalement, en suivant la même logique nous pouvons constater qu’il est préférable de configurer un nombre impair d’instances dans un ensemble Zookeeper.

Pour conclure cette première partie, résumons un peu ce que nous venons de voir :

  • une instance Zookeeper offre un espace mémoire hiérarchique ;
  • plusieurs instances Zookeeper répliquées se nomment un ensemble ;
  • un leader est élu dans l’ensemble Zookeeper, c’est lui qui enregistre les opérations, les transmet aux autres membres puis considère l’opération valide lorsque le quorum est atteint ;
  • au sein d’un ensemble Zookeeper, le quorum représente le nombre minimum d’instances nécessaires pour commiter une donnée dans l’ensemble.

Installation d’une instance standalone

Nous allons installer un serveur Zookeeper en standalone. L’opération est simple, rapide et  nécessite une configuration minimaliste. Tout cela va durer 10 minutes montre en main, en comptant la durée de téléchargement…
Créez un répertoire, par exemple $ mkdir zktest.

Télécharger l’archive des binaires (http://www.apache.org/dyn/closer.cgi/zookeeper/):

wget http://wwwftp.ciril.fr/pub/apache/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz
tar -xvf zookeeper-3.4.6.tar.gz 

Tout d’abord, un peu de configuration, mais rapide, promis !
Allez dans le ./zookeeper-3.4.6/conf et créez un nouveau fichier zoo.cfg avec les éléments suivants puis sauvez :

tickTime=2000
# dataDir : attention changer avec votre path vers votre tmp qu’il faudra créer si vous le placez
# comme moi dans le répertoire d’installation de zookeeper
dataDir=/Users/jbc/Developments/zktest/zookeeper-3.4.6/tmp
clientPort=2181
  • tickTime : utilisé pour le hearbeat, le timeout de session est de deux fois le ticktime
  • dataDir : le répertoire utilisé pour stocker la base de données « in memory » et par défaut les logs de transactions. Note dans mon cas : j’ai créé le répertoire
  • clientPort : le port d’écoute des connexions des clients

Ça suffit avec la conf, démarrons le serveur !

Dans ./zookeeper-3.4.6/bin exécutez la commande :

 ./zkServer.sh start

Notre serveur standalone est démarré, nous pouvons nous y connecter et lancer des commandes dans une CLI :

bin/zkCli.sh -server 127.0.0.1:2181

Affichez l’aide avec la commande « help ». Pour afficher les nœuds présents, tapez la commande ls /.

Créer un ZNode : par exemple pour créer le ZNode xebia avec la valeur hello :

create /xebia hello

Puis afficher la valeur :

get /xebia

Étape 1 de l’installation de Zookeeper terminée. Pour le moment, vous ne voyez peut-être pas encore l’intérêt de tout cela. Mais n’oubliez pas : la vrai valeur ajoutée de Zookeeper est de répartir tout cela sur plusieurs serveurs, de vous garantir la validité des données écrites/lues et de ne pas devenir le « Single Point Of Failure » de votre architecture.

Installation en local d’un ensemble de 3 Zookeeper

Tester avec 3 instances en local ne nous prendra que 10 minutes de plus.

Nous allons créer l’ensemble Zookeeper suivant :

Voici les étapes que nous allons suivre :

  • modifier notre installation de Zookeeper standalone pour lui ajouter la configuration vers d’autres instances sur notre machine locale ;
  • copier deux fois l’installation du serveur 1 et adapter chaque copie pour la rendre unique ;
  • démarrer les serveurs et vérifier que tout fonctionne.

Modifier notre installation de Zookeeper standalone pour lui ajouter la configuration vers d’autres instances sur notre machine locale

Nous allons modifier le nom du répertoire contenant l’installation de Zookeeper. Cela nous permettra de mieux savoir à quelle instance nous avons à faire :

mv zookeeper-3.4.6 zookeeper-1

dans ./zookeeper-1/conf/zoo.cfg, copier le contenu suivant (sans oublier de modifier la propriété dataDir selon votre environnement):

tickTime=2000
# ATTENTION : changer dataDir et n’oubliez pas de créer le répertoire tmp
dataDir=/Users/jbc/Developments/zktest/zookeeper-1/tmp
clientPort=2181
server.1=localhost:2888:3888
server.2=localhost:2889:3889
server.3=localhost:2890:3890
initLimit=5
syncLimit=2

Nous avons quelques nouvelles propriétés par rapport à la version standalone :

  • initLimit : délai max pour se connecter au leader. Facteur multiplicateur du tickTime, donc ici 5 * 2000 = 10 secondes
  • syncLimit : décalage max autorisé entre la date du serveur et celle du leader. Facteur multiplicateur du tickTime, donc ici 2 * 2000 = 4 secondes
  • server.X : configuration du serveur ayant l’index X. (Nous allons voir plus bas comment fixer l’index X de chaque serveur)
  • Le format de la ligne est <serveur name>:<port communication avec le leader>:<port pour élection d’un leader>

Pour fixer le numéro de notre serveur Zookeeper, il faut créer un fichier nommé myid dans le répertoire configuré dans la clé dataDir. Dans mon cas, c’est donc dans le répertoire ./zookeeper-1/tmp que j’ai créé ce fichier, puis édité le fichier en y écrivant le numéro de notre serveur, soit 1 pour ce premier serveur.

Copier deux fois l’installation du serveur 1

Nous allons créer nos 2 autres serveurs en copiant le premier et en faisant quelques petites adaptations dans les confs

cp -R zookeeper-1 zookeeper-2
cp -R zookeeper-1 zookeeper-3

Puis dans zookeeper-2 et zookeeper-3, nous allons successivement faire les modifications adéquates :

1) éditer ./conf/zoo.cfg :

clientPort=2182 (pour zookeeper-2)
clientPort=2183 (pour zookeeper-3)

2) éditer /tmp/myid :

2 (pour zookeeper-2)
3 (pour zookeeper-3)

Démarrer les serveurs et vérifier que tout fonctionne.

Pour démarrer les serveurs :

./zookeeper-3/bin/zkServer.sh start && ./zookeeper-2/bin/zkServer.sh start && ./zookeeper-1/bin/zkServer.sh start

Se connecter à zookeeper-1 :

./zookeeper-1/bin/zkCli.sh -server 127.0.0.1:2181

Nous allons mettre un « watch » sur le ZNode /xebia de tel sorte à être averti quand celui-ci est modifié.

Tout d’abord vérifier si /xebia existe déjà:

ls /

Si vous voyez xebia dans la réponse c’est bon, sinon le créer avec la commande :

create /xebia hello

Ensuite positionner le watch sur /xebia :

get /xebia true

Dans un autre terminal se connecter sur une autre instance de notre ensemble, par exemple sur le serveur 2 :

./zookeeper-2/bin/zkCli.sh -server 127.0.0.1:2182

puis modifier la valeur affectée au ZNode xebia:

set /xebia world

Retournez dans le terminal avec la CLI sur le serveur 1 et constatez que le watcher s’est déclenché :

WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/xebia

Cela montre la synchronisation et la réplication des données dans notre ensemble Zookeeper. Maintenant nous allons tester la réelection d’un leader dans notre ensemble.

La première chose à faire est de connaître le leader en cours dans notre ensemble. Pour cela, nous utilisons la commande suivante pour chaque serveur :

echo stat | nc localhost 2181 | grep Mode && echo stat | nc localhost 2182 | grep Mode && echo stat | nc localhost 2183 | grep Mode

Cela nous donnera, dans l’ordre, l’affectation de chacun de nos serveur comme follower ou leader. Dans mon cas, par exemple, j’ai obtenu la réponse suivante :

Mode: follower
Mode: follower
Mode: leader

Nous allons stopper le serveur leader en utilisant le résultat de la commande précédente. Dans mon cas, le serveur 3 était le leader :

# Attention : ciblez bien votre serveur leader qui n'est pas forcément le 3
./zookeeper-3/bin/zkServer.sh stop

Puis, on relance sur notre ensemble, maintenant composé de 2 instances, la commande suivante :

echo stat | nc localhost 2181 | grep Mode && echo stat | nc localhost 2182 | grep Mode

Résultat :

Mode: follower
Mode: leader

Dans mon cas, je constate que le serveur 2 est devenu le leader.

Si finalement nous stoppons un serveur supplémentaire nous allons tomber à un seul élément dans notre ensemble Zookeeper initialement composé de 3 et dont le quorum dans ce cas est de 2. Par conséquent, cela devrait avoir des conséquences négatives sur notre ensemble. Vérifions cela:

./zookeeper-2/bin/zkServer.sh stop

Si nous interrogeons alors le serveur 1 :

echo stat | nc localhost 2181

La réponse est :

This ZooKeeper instance is not currently serving requests

Notre ensemble zookeeper est hors service !

Redémarrons le serveur 2 :

./zookeeper-2/bin/zkServer.sh start

Interrogeons de nouveau l’état du serveur 2 :

echo stat | nc localhost 2181

et nous pouvons voir que notre ensemble Zookeeper est de nouveau up !

Conclusion

Dans cet article, nous avons vu les termes à connaitre et le fonctionnement général de Zookeeper, puis nous avons démarré un ensemble Zookeeper pour montrer qu’il n’y avait rien de sorcier en cela. Une installation dans un environnement de production adresserait probablement d’autres problématiques, mais là n’était pas notre objectif.

Dans un prochain article à venir nous allons créer un petit service REST et à l’aide du framework Curator (une api haut niveau aidant la manipulation de Zookeeper), nous mettrons en œuvre Zookeeper comme Naming Service pour qu’un client puisse appeler notre service REST sans préalablement savoir où et sur quel port il est démarré.

5 thoughts on “Introduction et démystification de Zookeeper”

  1. Publié par PommeDouze, Il y a 3 années

    Super article d’introduction à Zookeeper qui est ma problématique du moment, ça m’a bien aidé !
    Quelques petites coquilles à mon avis :
    – Sur la modif de la version standalone de ZK, le fichier de config à modifier est « ./zookeeper-1/conf/zoo.cfg » et non « ./zookeeper-1/conf/conf.cfg ».
    – Pour tester le dysfonctionnement lorsqu’on descend en dessous du quorum, la commande devrait être « ./zookeeper-1/bin/zkServer.sh stop » et non « ./zookeeper-1/bin/zkCli.sh -server 127.0.0.1:2181 »

    Merci pour cet article !

  2. Publié par jbclaramonte, Il y a 3 années

    Bien vu !
    J’ai fait les corrections :-)
    Merci

  3. Publié par Henri, Il y a 3 années

    Et en fait, c’est plutôt ./zookeeper-2/bin/zkServer.sh stop et non 1 que vous voulez si j’ai bien suivi :-)

  4. Publié par jbclaramonte, Il y a 3 années

    oui tout à fait :-)
    c’est corrigé

  5. Publié par Pierre, Il y a 1 année

    Excellent article pour le débutant que je suis.

    Merci.

  6. Publié par Ulrich M, Il y a 5 mois

    Merci pour l’article
    Vraiment intéressante pour avoir un aperçu rapide de zookeeper
    Merci

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *