Mon blog-notes à moi que j'ai

Blog personnel d'un sysadmin, tendance hacker

À la découverte de Redis Cluster 3.0

La version 3 de Redis est en Release Candidate 6. Parmi les fonctionnalités les plus attendues figure le support d’un mode cluster. Comment fonctionne-t-il et que permet-il?

Introduction

Une fois n’est pas coutume, la documentation est très bien faite

Je donne ici quelques explications, tirées de la documentation, qui permettent de mieux appréhender Redis Cluster.

Quorum

Pour éviter un partitionnement de votre cluster, vous devriez utiliser un nombre impair de nœuds maîtres. Ceci permet au cluster de prendre des décisions à la majorité absolue.

Redondance des données

Ce n’est pas obligatoire, mais chaque maître devrait également avoir au moins un esclave pour assurer la redondance des données. Contrairement à d’autres logiciels fonctionnant en mode cluster, comme Elasticsearch, les données ne sont pas répliquées entre les nœuds maîtres.

Stockage des données

Les versions antérieures de Redis supportent un mode multi-DB, qui permet de séparer les données dans des silos différents. Cette fonctionnalités disparaît de Redis cluster.
Dans Redis cluster, les données sont donc stockées dans la même DB, mais réparties entre 16384 hash slots. La répartition est déterminée par un calcul sur la clef selon la formule

HASH_SLOT = CRC16(key) mod 16384

Les hash slots sont eux-même répartis entre les nœuds maîtres.

Par défaut, Redis prend en compte le nom de la clef en entier. Il est possible de modifier ce comportement en utilisant les accolades { et } dans le nom de la clef. Par exemple, les clefs suivantes seront stockées dans le même slot

{key::name}::value
foo::{key::name}::value

Ceci est dû au fait que les accolades permettent d’indiquer à Redis quelle partie du nom de la clef prendre en compte pour déterminer le slot de stockage.

Certaines opérations sont « slot bounded », c’est à dire qu’elle ne peuvent s’appliquer qu’au sein d’un seul et même slot. Par exemple, les opérations dites bitmaps et, notamment, la commande BITOP (qui permet de réaliser des opérations booléennes sur plusieurs clefs).
Toutes les clefs doivent se trouver dans le même slot. De fait, l’utilisation des accolades peut alors se révéler indispensable.

Cluster topology

Chaque nœud maître conserve un copie de la topologie du cluster. Pour se faire, tous les nœuds sont connectés les uns aux autres au travers d’un bus cluster. Il s’agit d’une connection sur le port TCP/16379.

Cette connection est réalisée sur le port d’écoute de Redis, par défaut TCP/6379, plus 10000. Il n’est donc pas possible de démarrer un nœud Redis cluster sur le port 55536 puisque le bus cluster n’aura pas de port disponible.

Chacun des nœuds d’un cluster Redis à n nœuds maintient donc n-1 connexions entrantes, et donc également n-1 connexions sortantes, pour maintenir la topologie du cluster. Redis cluster est donc un cluster full mesh.

Utilisation

Créer un cluster

Le fichier de configuration minimum d’un nœud Redis cluster est le suivant

port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-require-full-coverage yes
cluster-migration-barrier 1
cluster-slave-validity-factor 10
appendonly yes

Pour le démarrer, il suffit d’exécuter la commande

redis-server redis7000.conf

Redis est donc en écoute sur le port TCP/7000 pour Redis lui-même, et TCP/17000 pour le bus cluster. Par défaut, Redis n’alloue pas les slots de manière automatique. Il faut donc le faire soi-même (ne le faites pas maintenant, regardez la suite :) )

redis-cli -p 7000 CLUSTER ADDSLOTS 0
redis-cli -p 7000 CLUSTER ADDSLOTS 1
redis-cli -p 7000 CLUSTER ADDSLOTS 2
redis-cli -p 7000 CLUSTER ADDSLOTS 3
[...]
redis-cli -p 7000 CLUSTER ADDSLOTS 16383

OK, évitons d’exécuter 16384 fois la même commande. Redis fourni, avec les sources, un script Ruby qui permet de réaliser cela pour efficacement (dans mon cas, j’ai installé le script dans /usr/bin)

/usr/bin/redis-trib.rb fix 127.0.0.1:7000

Cet outil va inspecter l’état de l’allocation des slots et vous proposer de corriger ce qui doit l’être.

Naturellement, un cluster a un nœud n’est pas d’une utilité extraordinaire. Créons donc une autre instance Redis

port 7002
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-require-full-coverage yes
cluster-migration-barrier 1
cluster-slave-validity-factor 10
appendonly yes

Démarrons cette seconde instance et ajoutons-là au cluster

redis-server redis7002.conf
redis-cli -p 7002 CLUSTER MEET 127.0.0.1 7000

Vérifions à présent l’état du cluster

/usr/bin/redis-trib check 127.0.0.1:7000
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7002: OK
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 1327103b8e5b4921bf47941943c4b279a50d8ed5 127.0.0.1:7000
   slots:0-16383 (16384 slots) master
   0 additional replica(s)
M: 6a5a36527d49b1c21275feffa0f4c3b6b62b49c5 127.0.0.1:7002
   slots: (0 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Le nouveau noœud a été ajouté, mais il n’est responsable d’aucun slot. Il est nécessaire de les répartir manuellement. Dans ce cas précis, nous allons déplacer la moitié les slots

/usr/bin/redis-trib reshard 127.0.0.1:7000
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7002: OK
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 1327103b8e5b4921bf47941943c4b279a50d8ed5 127.0.0.1:7000
   slots:0-16383 (16384 slots) master
   0 additional replica(s)
M: 6a5a36527d49b1c21275feffa0f4c3b6b62b49c5 127.0.0.1:7002
   slots: (0 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 8192
What is the receiving node ID? 6a5a36527d49b1c21275feffa0f4c3b6b62b49c5
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1:1327103b8e5b4921bf47941943c4b279a50d8ed5
Source node #2:done
[...]

Une fois la répartition faite, nous pouvons vérifier à nouveau le cluster

/usr/bin/redis-trib check 127.0.0.1:7000
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7002: OK
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 1327103b8e5b4921bf47941943c4b279a50d8ed5 127.0.0.1:7000
   slots:8192-16383 (8192 slots) master
   0 additional replica(s)
M: 6a5a36527d49b1c21275feffa0f4c3b6b62b49c5 127.0.0.1:7002
   slots:0-8191 (8192 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Ajoutez à présent un troisième nœud sur le port 7004. Je vous laisse réaliser les maths nécessaires à la répartition des slots :)

/usr/bin/redis-trib.rb check 127.0.0.1:7000
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7004: OK
Connecting to node 127.0.0.1:7002: OK
>>> Performing Cluster Check (using node 127.0.0.1:7000)
M: 9b6e118f13081f6c8ded4d2419cdb21a0ad1bbc7 127.0.0.1:7000
   slots:8192-16383 (8192 slots) master
   0 additional replica(s)
M: 978ec5bc35cf57332d0c0672a7ff3f2fe3a43666 127.0.0.1:7004
   slots: (0 slots) master
   0 additional replica(s)
M: 1dfc1b008c56c8b9c415d930b7e53f4677f8b1bd 127.0.0.1:7002
   slots:0-8191 (8192 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

Munis des identifiants des nœuds, il est possible de répartir les slots de manière non interactive

/usr/bin/redis-trib.rb reshard 127.0.0.1:7000 \
                       --from 9b6e118f13081f6c8ded4d2419cdb21a0ad1bbc7 \
                       --to 978ec5bc35cf57332d0c0672a7ff3f2fe3a43666 \
                       --slots 2730 --yes
/usr/bin/redis-trib.rb reshard 127.0.0.1:7000 \
                       --from 1dfc1b008c56c8b9c415d930b7e53f4677f8b1bd \
                       --to 978ec5bc35cf57332d0c0672a7ff3f2fe3a43666 \
                       --slots 2731 --yes

Une fois la répartition effectuée, vous pouvez ajouter des esclaves à chacun de vos nœuds maîtres. Créez les nœuds Redis sur les ports 7001, 7003 et 7005 et démarrez-les. Ensuite, on peut les intégrer au cluster en tant que réplicat d’un maître

/usr/bin/redis-trib add-node --slave 127.0.0.1:7001 127.0.0.1:7000
/usr/bin/redis-trib add-node --slave 127.0.0.1:7003 127.0.0.1:7002
/usr/bin/redis-trib add-node --slave 127.0.0.1:7005 127.0.0.1:7004

Je vous laisse tester le failover. Il vous suffit pour cela de tuer un des process master

Enjoy :)