Redis est un système de stockage de données en mémoire de type NoSQL orienté hautes performances. Il est souvent utilisé comme base de données, système de cache ou de messages. Nous verrons dans cet article comment déployer un service hautement disponible grâce à Redis Replication.
1. Notre premier serveur Redis
Pour démarrer, commençons avec un serveur Redis. L’installation sera présentée pour Ubuntu 22.04 et devrait fonctionner sans trop d’adaptations pour Debian ou RHEL-like.
1.1 Installation
Redis est disponible dans les dépôts classiques de la distribution. Une configuration minimale peut être faite en installant simplement les paquets et en activant le service systemd.
Vérifions maintenant que nous parvenons à nous connecter :
La CLI permet également de récupérer avec la commande INFO un certain nombre d’éléments sur le fonctionnement du serveur. Celle-ci s’appelle seule ou avec un argument pour obtenir les informations relatives à une catégorie particulière.
Redis gère plusieurs types de données. Il est possible d’utiliser des chaînes de caractères, des ensembles, des hashs, des listes ainsi que d’autres types de données. Pour procéder à une première vérification de bon fonctionnement, nous allons ainsi écrire une donnée de type string, récupérer la valeur en indiquant la clé puis effacer cette donnée.
À ce stade, notre serveur est fonctionnel, toutefois sa configuration est très basique. Redis n’écoute que sur l’interface localhost, ce qui est incompatible avec la notion de réplication que nous mettrons en place et il est préférable que systemd prenne en charge le service de manière explicite. Nous allons entreprendre donc nos premières modifications du fichier de configuration /etc/redis/redis.conf et remplacer les paramètres bind et supervised par les valeurs suivantes :
Redémarrons le service redis et vérifions :
En pratique, la plupart des options de Redis peuvent être définies avec la commande CONFIG SET. C’est peut-être moins classique ou confortable qu’un fichier de configuration, mais c’est ce qui permet à Redis d’être entièrement reparamétré sans qu’il ne soit nécessaire de relancer le service et donc sans aucune interruption de service.
1.2 Gérer la persistance
Redis est une base de données en mémoire. Par conséquent si le service est arrêté, quelle qu’en soit la raison, les données sont perdues. Pour assurer la persistance des données, Redis permet d’utiliser deux mécanismes. Dans le premier, appelé RDB, Redis va générer des snapshots à intervalles réguliers de la base de données. Cela se configure avec save suivi de deux indicateurs, le premier consiste en une sauvegarde après n secondes si au moins un certain nombre d’écritures ont eu lieu. Ce paramètre est en outre multivalué. La politique par défaut est celle-ci :
Généralement, on souhaite avoir au moins un snapshot récent même s’il y a peu d’écritures, d’où celui réalisé après 15 minutes à partir du moment où il y a eu une écriture. À l’inverse, en cas de forte charge, on souhaite également avoir des snapshots fréquents pour minimiser la perte de données, d’où le snapshot toutes les minutes dès 10000 clés modifiées. Ces seuils sont naturellement à adapter en fonction de l’activité de la base et afin de minimiser la perte de données admissible.
Dans le second mécanisme, AOF pour Append Only File, la persistance est gérée via l’utilisation d’un fichier de logs qui va historiser chaque écriture. Pour un maximum de fiabilité, les deux modes peuvent être utilisés conjointement. Naturellement, cela a un impact sur la consommation mémoire et les I/O disques, car pour chaque écriture en base, une seconde est faite pour le journal AOB.
Pour utiliser l’AOF, ajoutons ces deux lignes dans le fichier redis.conf :
1.3 Un minimum de sécurité
Par défaut, Redis n’est pas joignable via le réseau. Pour que la mise en œuvre de la réplication à suivre soit possible, le service écoute désormais sur l’ensemble des interfaces réseau. Bien qu’il soit possible de restreindre à une ou plusieurs interfaces, l’accès au service à des fins de test s’est réalisé sans authentification. En définissant un mot de passe sur le paramètre requirepass, toute opération sur Redis nécessitera au préalable de s’authentifier avec la commande AUTH suivie du mot de passe. Dans la configuration redis.conf, nous ajoutons donc :
Et nous pouvons vérifier :
Pour assurer une meilleure sécurité, du TLS devrait être mis en œuvre et les ACL Redis 6 devraient être utilisées.
2. Assurer la haute disponibilité
2.1 Les mécanismes
Redis Replication est un système maître/esclave permettant de répliquer de manière asynchrone les données du master sur les slaves. En cas de perte de connectivité avec le master, les slaves vont tenter de resynchroniser l’écart de données dès que la connexion redevient disponible. Si cela n’est pas possible, une resynchronisation complète est réalisée. Un slave peut servir de source de réplication à un autre slave et les réplicas peuvent servir de serveur en lecture pour répartir la charge et donc augmenter la performance.
En cas de perte du master, une procédure de failover doit être mise en œuvre pour promouvoir un slave comme master. Tant que cela n’est pas fait, le fonctionnement est en mode dégradé si ce n’est totalement interrompu. Redis Sentinel utilisé en complément permet de superviser le fonctionnement de Redis et en cas de panne du master, de promouvoir un nouveau master et de reconfigurer les replicas pour qu’ils se répliquent depuis ce nouveau master. Dans ce type d’architecture, il est recommandé de disposer d’au moins 3 serveurs pour gérer le quorum.
Avec Redis Replication, il est fortement recommandé d’activer la persistance. En effet, sans persistance et en cas de redémarrage du master, il se retrouvera vide de toute donnée. Dans un second temps, les slaves vont s’empresser de répliquer cela les vidant de leurs données.
Pour assurer la montée en charge horizontale, Redis Cluster offre des possibilités complémentaires. Dans ce mode, il s’agit toujours d’avoir une architecture master/slave, mais avec gestion et failover automatique. L’espace de clés est divisé de sorte que chaque groupe de serveur ne gère qu’une partition de la base de données, cela s’appelle du sharding. La taille de la base de données peut ainsi être plus importante et la charge peut être répartie sur plus de nœuds. Un minimum de 6 nœuds est toutefois requis pour Redis Cluster ce qui prédispose ce mode à des infrastructures relativement conséquentes.
De fait, dans cet article, j’aborderai uniquement Redis Replication.
2.2 Configuration
La configuration mise en œuvre sur le premier serveur que nous appellerons db01 devra être reprise à l’identique sur nos deux slaves. Le serveur DB01 sera le master par défaut. Pour que la suite soit aisément compréhensible, voici les serveurs et adresses IP que je vais utiliser :
- db01 : 192.168.69.81
- db02 : 192.168.69.82
- db03 : 192.168.69.83
Sur chacun de nos serveurs, nous allons définir la valeur de masterauth. En effet, nous avons exigé précédemment un mot de passe via requirepass, il faut donc s’authentifier pour avoir les droits pour répliquer :
Et enfin, sur chaque slave, on indique depuis quel serveur notre slave doit se répliquer :
Il suffit de redémarrer nos serveurs, il n’y a rien de plus à faire.
2.3 Vérification de la réplication
Dans un premier temps, envoyons la commande INFO Replication sur notre master. Si la configuration est correcte, alors on doit pouvoir avoir un rôle master, visualiser les deux slaves comme connectés ainsi que leurs adresses IP et enfin en fonctionnement nominal un statut online :
À l’inverse, sur un slave, le rôle doit correspondre et le serveur doit être connecté au slave (master_link_status). Si master_sync_in_progress est à 0 alors la réplication est terminée. La section master_replid indique le dataset de ReplicationID qui doit être identique avec le master lorsque les masters et slaves sont synchronisés. Si une synchronisation était en cours, nous aurions des champs master_sync supplémentaires pour suivre la volumétrie à répliquer et les performances de réplication.
2.4 Failover
Voyons désormais ce qu’il se passe lors d’une panne du master. J’ai simulé un crash du master en coupant violemment la VM qui porte le service. Sur chacun de mes slaves, la réplication indique que le lien avec le master est « down » avec le délai depuis lequel le lien est rompu.
S’il s’agit d’un simple redémarrage du serveur master, la réplication devrait remonter dès que le serveur sera up. En cas de panne majeure, le service rendu est interrompu. Il faut manuellement reconfigurer les slaves pour rétablir le service.
Sur db03, nous allons donc indiquer que sa source de réplication est db02 :
Pour autant, cela ne fait pas de db02 un master par magie. D’autant que Redis supporte la réplication d’un slave depuis un autre slave. Il faut donc indiquer que db02 est un master, ce qui se configure en indiquant qu’il est le réplica d’aucun serveur :
2.5 Le retour du master
Se pose ensuite la question de la remise en service du serveur db01 lorsqu’il sera réparé. Avec le failover précédent, il va se retrouver master sans slave et il sera aussi désynchronisé des autres serveurs Redis. Il est donc indispensable de le remettre en synchronisation avec le master qui a été promu. Cela se fait de la même façon que lors du failover précédent :
Et c’est tout. Si toutefois vous tenez à remettre le serveur redis master « nominal » en tant que master, il faut alors :
- le resynchroniser sur le master et s’assurer que la réplication est terminée ;
- définir les slaves en état nominal (db02 et db03) comme replicaof db01 ;
- indiquer que db01 n’est plus le réplica d’aucun serveur.
Cela reste toutefois une opération à réaliser en période de maintenance, car une courte rupture du service lors de la bascule est à prévoir.
3. Failover automatique avec Sentinel
3.1 Configuration
Nous venons de le voir, la réplication est simple à mettre en œuvre ou à reconfigurer. Elle a toutefois un énorme inconvénient en cas de perte du master, elle est manuelle. Nous allons donc mettre en œuvre un système complémentaire appelé Redis Sentinel qui viendra gérer automatiquement la promotion d’un nouveau master et la reconfiguration des slaves.
Commençons par installer sentinel :
Et configurer le service pour qu’il communique sur le réseau et non plus uniquement sur localhost. Au niveau firewall, le port 26379 devra autoriser les flux. Cela se fait via le fichier dédié /etc/redis/sentinel.conf :
Ajoutons un mot de passe de connexion, il s’agit du mot de passe d’authentification sur Sentinel :
Enfin, démarrons la configuration des règles de bascule. Nous monitorons l’adresse IP du master, 192.168.69. Un quorum de 2 serveurs est exigé, c’est-à-dire que si au moins deux slaves s’accordent sur le fait que le master est injoignable alors une bascule est enclenchée.
Le master est considéré comme injoignable après une minute :
Une redémarrage du service sentinel plus tard, vérifions l’état de notre service en nous connectant via redis-cli sur le port de Sentinel cette fois :
Nous retrouvons bien le bon nombre de slaves. Si on souhaite des informations détaillées sur les slaves, il est possible d’envoyer la commande sentinel slaves mymaster et pour les instances sentinel : sentinel sentinels mymaster. La sortie étant peu lisible, il s’agit de la configuration en mode clé valeur dans la base Redis, je ne vais pas l’inclure ici.
3.2 Test de bascule
Sur notre master, simulons une panne en arrêtant le service avec systemctl stop redis-server. En parallèle, regardons les logs de Sentinel dans /var/log/redis/redis-sentinel.log. Par lisibilité, je vais filtrer quelque peu les logs.
En premier lieu, nous avons un event sdown, cela signifie que sentinel a détecté que le master n’était plus joignable :
Ensuite, nous avons un odown, c’est-à-dire que le serveur est vu comme injoignable par au moins le nombre de serveurs du quorum, comme nous avons indiqué 2, il faut au moins les deux serveurs pour qu’une promotion d’un nouveau soit réalisée :
db03 a été élu comme nouveau master :
db02 est reconfiguré comme slave de db03 :
Enfin, on peut le confirmer via la CLI redis sur db03 :
Félicitations, Redis s’est automatiquement reconfiguré sans intervention.
Que se passe-t-il désormais pour notre ancien master ? Au redémarrage de redis, il est devenu un slave. Cela se voit d’ailleurs dans les logs sentinel des autres serveurs. Il est possible de forcer via la CLI sentinel un failover vers un autre serveur :
Il n’est toutefois pas possible d’indiquer expressément quel serveur sera promu. Une manière de le contrôler est d’utiliser la directive slave-priority pour favoriser un serveur avec une priorité plus faible.
4. HAProxy
Dans un monde idéal, votre client Redis aura un support de Sentinel. Il saura alors contacter sentinel pour connaître le master et s’y connecter. En récupérant la liste des serveurs, il sera en mesure de distribuer les requêtes en lecture sur les slaves pour répartir la charge. En cas d’anomalie du master, il saura enfin gérer le failover.
En pratique, on ne peut pas toujours connaître le niveau de support des clients notamment lorsque l’on fournit la plateforme à des développeurs tiers. Dans ce cas précis, je recommande d’utiliser le classique HAProxy qui sait tout faire en matière de répartition de charge, que je vous laisse installer via les packages de la distribution.
Le cas particulier qu’il faut néanmoins gérer c’est la détection du master. De manière simple, si le port de destination répond, on considère souvent que le service est fourni par le backend. Dans notre cas, il va falloir identifier le serveur dont le rôle est le master, souvenez-vous nous avions l’information avec INFO Replication avec la CLI Redis. Il faut donc définir un TCP Check qui réalisera une authentification, enverra cette même commande est aura comme backend active celui dont le rôle est master :
Conclusion
C’est terminé pour ce tour d’horizon de la haute disponibilité d’un service Redis Replication avec Sentinel. J’invite le lecteur curieux à compléter ce parcours pour utiliser les ACL et ajouter ce niveau de sécurité. J’ai volontairement écarté ce point pour légèrement simplifier la configuration et rester ciblé sur le sujet.