Haute Disponibilité avec Redis

Redis est une base de donnée de type clef-valeur. On la range dans la grande famille plutôt hétérogène des bases NoSQL qui, pour rappel, signifie plutôt Not Only SQL que No SQL. Ceci dit, dans le cas de Redis, on est vraiment dans le No SQL at all. La base permet de stocker par clef des chaînes, des listes de chaînes, des hashtable. Elle permet de stocker des valeurs avec une date d’expiration au delà de laquelle la donnée disparaît. Idéal pour gérer des données cache et c’est d’ailleurs l’utilisation principale de Redis. On peut aussi facilement implémenter producteur / consommateur entre plusieurs clients d’une base Redis. L’ensemble des commandes supportées par Redis est parfaitement documenté ici.

La base est orientée performance et évolutivité :

  • écrite en C, facilement portable car le code n’a pas de dépendance particulière,
  • stockage des données en mémoire avec différents mécanismes optionnels pour conserver une copie sur disque,
  • réplication possible d’une base maître vers un grand nombre de bases esclaves. On peut écrire dans la base maître et seulement lire dans les bases esclaves.

Ce qui fait (aussi) le succès de Redis, c’est que le coeur est en C et qu’il existe des clients pour la plupart des langages. Pour l’instant, j’ai utilisé Jedis pour JAVA et redis-py pour Python. Enfin, un client en ligne de commande permet d’interagir avec la base sans écrire de code.

La dernière version stable est Redis 2.8.8. Quant à la version 3.0 à venir, encore en phase de bêta, elle embarque des fonctionnalités de répartition des données dans des configuration de type cluster, ce qu’on appelle en langue de Shakespeare le sharding. Dans le futur, elle embarquera aussi des fonctionnalités de Haute Disponibilité pour basculer les données lorsqu’un noeud du cluster s’écroule.

En attendant ce futur, la version actuelle apporte une solution de cluster actif/passif basée sur la réplication maître / esclave surveillée par des sentinelles chargées de promouvoir un Redis esclave en maître en cas de défaillance.

Je me suis intéressé à monter une configuration avec seulement 2 machines sous Debian qui peut fonctionner si l’une des machines tombe et automatiquement réintégrer le cluster quand elle redevient opérationnelle, sans impact pour les clients Redis. Ce n’est pas si trivial et je me suis heurté à quelques difficultés avant d’arriver à une configuration opérationnelle.

Comme dans la plupart des architectures clusterisées, un quorum (nombre minimal de votants) est nécessaire pour élire un nouveau maître. La valeur minimum possible est 1, signifiant qu’une sentinelle a besoin qu’au moins 1 autre sentinelle soit d’accord avec elle pour déclarer qu’un Redis maître est défaillant. Il faut au minimum 2 sentinelles opérationnelles quel que soit l’état du cluster donc sur chaque machine on va installer un Redis et 2 sentinelles.

Au début de mes expérimentations, j’attribuais un port différent aux sentinelles pour les exécuter sans conflit sur la même machine mais j’avais des problème d’indécision des sentinelles pour élire un nouveau noeud. Je crois que toutes les sentinelles ne communiquaient pas. La situation s’est arrangée quand j’ai configuré toutes mes sentinelles pour écouter sur le port standard 26379. Pour que ce soit possible, j’ai attaché mes sentinelles à des adresses IP différentes en déclarant une sous-interface sur chaque machine.

+–––––––––––––––+––––––––––––––+     +–––––––––––––––+––––––––––––––+
|  Sentinelle 1 |              |     |  Sentinelle 1 |              |
+–––––––––––––––+              |     +–––––––––––––––+              |
|    REDIS      | Sentinelle 2 |     |    REDIS      | Sentinelle 2 |
+––––––––––––––––––––––––––––––+     +––––––––––––––––––––––––––––––+
|     Eth0      |    Eth0:1    |     |     Eth0      |    Eth0:1    |
| 192.168.0.51  |  10.25.14.1  |     | 192.168.0.52  |  10.25.14.2  |
+–––––––––––––––+––––––––––––––+     +–––––––––––––––+––––––––––––––+
           Machine A                            Machine B

Voici la configuration réseau de la machine A (/etc/network/interfaces) :

iface eth0 inet static
    address 192.168.0.51
    netmask 255.255.255.0    

iface eth0:1 inet static
    address 10.25.14.1
    netmask 255.255.255.0

La configuration des serveurs Redis est identique sur chaque machine :

port 6379

loglevel warning
logfile "/var/log/redis.log"

maxmemory-policy noeviction

appendonly yes
appendfsync always

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-rewrite-incremental-fsync yes

Ce n’est pas le sujet de l’article mais je vous conseille de jeter un oeil à la documentation pour les paramètres liés à l’AOF et au APPEND qui vont réduire les risques de perte de données en configurant des écritures sur disque. C’est peut-être inintéressant dans le cas d’une utilisation de Redis comme cache mais ça le devient dans le cas d’une utilisation plus classique comme base de données.

Quand les deux serveurs Redis sont démarrés, on peut initier le rôle initial d’esclave de la machine B depuis le client Redis avec la commande :

slaveof 192.168.0.51 6379

Par la suite, les sentinelles se chargent de décider quel rôle est joué en fonction de la disponibilité des machines.

Voici la configuration de la sentinelle 1 sur la machine A :

port 26379    
bind 192.168.0.51

# master configuration
sentinel monitor master 192.168.0.51 6379 1
sentinel down-after-milliseconds master 3000
sentinel failover-timeout master 10000
sentinel parallel-syncs master 4

Et celle de la sentinelle 2, également sur la machine A :

port 26379
bind 10.25.14.1

# master configuration
sentinel monitor master 192.168.0.51 6379 1
sentinel down-after-milliseconds master 3000
sentinel failover-timeout master 10000
sentinel parallel-syncs master 4

La configuration des sentinelles de la machine B est identique à part les directives bind pour attacher les services aux adresses 192.168.0.52 et 10.25.14.2.

Avec cette configuration, on a suffisamment de sentinelles pour basculer le rôle d’une machine à l’autre dans cas extrème où la machine A est injoignable par le réseau ou bien mise hors tension.

Je n’ai pas détaillé le code client mais il y a une étape avant de récupérer une connexion valide à la base Redis : s’adresser au pool de sentinelles pour obtenir l’adresse du maître et ensuite ouvrir une connexion vers celui- ci. Dans le cas d’une bascule du cluster, la connexion est cassée et il faut à nouveau s’adresser aux sentinelles pour récupérer l’adresse du Redis maître.

Voici un exemple typique de code en Python :

    from redis.sentinel import Sentinel
    sentinel = Sentinel(
        [('192.168.0.51', 26379), ('192.168.0.52', 6379)], socket_timeout=0.1)
    print("Master %s %d" % sentinel.discover_master('master'))

Je suis le projet Redis depuis un bout de temps avec intérêt car il offre beaucoup d’applications possibles dans des architectures distribuées multi-langages.

Redsmin (redis gui) - 2014-04-16 09:22:22

Très bonne introduction à Redis Sentinel, nous partagerons cet article dans notre prochain RedisWeekly !

Cyril Barillet - 2017-06-26 04:04:18

Bonjour.

Aujourd’hui, je travaille sur le même cas que vous. Je me suis donc fortement inspiré de ce que vous avez fait et vous remercie pour cette page. Durant mes expérimentations, je m’aperçois, en repartant de votre premier schéma, que si la machine A héberge le Redis master et que je coupe cette machine alors les sentinels de la machine B ne parviennent pas à élire un master pour transformer le Redis slave de la machine B en master. Si je redémarre une des sentinels de la machine A, l’élection du master des sentinels s’effectue normalement et le Redis Slave de la machine B devient bien le nouveau master. En vous lisant, je ne vois pas apparaître cette limitation. Pensez-vous qu’il s’agisse d’une erreur de mon côté ou bien est-ce tout à fait normal ? Pour info, j’utilise la version suivante de Redis : Redis server v=3.2.9 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=9118b92f229c3673.

Cordialement.

Yax - 2017-06-26 08:01:22

Bonjour,

Est-ce que vous avez le quorum suffisant pour que les sentinelles prennent une décision, à savoir au moins deux sentinelles actives quand le Redis maître est stoppé ? Si c’est bien le cas, êtes vous certain de la configuration de vos sentinelles et de leur capacité à communiquer entre elles ?

/Yax

Yax - 2017-06-26 08:16:29

En complément, on peut obtenir des informations de diagnostic d’une sentinelle en se connectant dessus avec le redis-cli et même forcer une bascule. Lire la section Testing Failover de cet article

Cyril Barillet - 2017-06-26 10:37:41

Bonjour.

Merci beaucoup pour vos réponses. L’article que vous citez s’appuie sur 3 sentinels qui ne sont jamais arrêtées. Ce cas fonctionne chez moi. Pour ma part, la documentation située à l’adresse “https://redis.io/topics/sentinel" va dans le sens du comportement que j’observe : “In practical terms this means during failures Sentinel never starts a failover if the majority of Sentinel processes are unable to talk (aka no failover in the minority partition).”. Dans notre cas, nous utilisons un nombre pair de sentinels réparties sur deux machines (pas très bons comme installation à mon sens). Si je perds une machine, je n’ai pas la majorité des sentinels ce qui explique mon problème. Aussi, ma question est : avez-vous coupé la machine hébergeant le redis master dans votre cas ce qui a conduit à la perte du redis master + 2 sentinels ? Si oui, le système a-t-il bien élu un nouveau redis master ? Un autre point, d’après ce que j’ai pu comprendre de la documentation, “the quorum is only used to detect the failure” et “In order to actually perform a failover, one of the Sentinels need to be elected leader for the failover and be authorized to proceed. This only happens with the vote of the majority of the Sentinel processes.” (src : https://redis.io/topics/sentinel). Cordialement.

Yax - 2017-06-26 10:57:49

“Avez-vous coupé la machine hébergeant le redis master dans votre cas ce qui a conduit à la perte du redis master + 2 sentinels ?”

Je pense avoir effectué ce test car c’était l’objectif principal : basculer si le serveur principal tombait mais je me rappelle aussi que je ne l’ai pas productisé ainsi. Donc j’ai peut-être rencontré des problèmes dans les tests : bascule sur arrêt du REDIS mais pas bascule sur arrêt complet du serveur. Effectivement à la lecture de la doc, une sentinelle supplémentaire installée sur une autre machine semble nécessaire.

Votre commentaire
Le site Web est optionel
Le message peut être rédigé au format Markdown