Redis est un systÚme de stockage de données en mémoire de type NoSQL orienté hautes performances. Il est souvent utilisée 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 mon 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.
sudo apt -y install redis-server sudo systemctl enable --now redis-server
VĂ©rifions maintenant que nous parvenons Ă nous connecter :
redis-cli 127.0.0.1:6379> ping PONG
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.
127.0.0.1:6379> INFO Server # Server redis_version:6.0.16 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:a3fdef44459b3ad6 redis_mode:standalone os:Linux 5.15.0-73-generic x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:atomic-builtin gcc_version:11.2.0 process_id:596 run_id:7ea0cf9f46b211a64874d7a1c0a115be78c42e98 tcp_port:6379 uptime_in_seconds:61483 uptime_in_days:0 hz:10 configured_hz:10 lru_clock:8771018 executable:/usr/bin/redis-server config_file:/etc/redis/redis.conf io_threads_active:0
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.
127.0.0.1:6379> set foo bar OK 127.0.0.1:6379> get foo "bar" 127.0.0.1:6379> del foo (integer) 1 127.0.0.1:6379> get foo (nil)
A 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 :
bind 0.0.0.0 supervised systemd protected-mode
Redémarrons le service redis et vérifions :
sudo systemctl restart redis-server sudo netstat -lataupen |grep ":6379" tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 115 39165 3267/redis-server 0
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 :
save 900 1 save 300 10 save 60 10000
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. A 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 :
appendonly yes appendfilename "appendonly.aof" appendfsync everysec
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 :
requirepass SECRET
Et nous pouvons vérifier :
# redis-cli 127.0.0.1:6379> AUTH SECRET OK
Pour assurer une meilleur 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Ă©pliquas 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 repliquas 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 :
masterauth SECRET
Et enfin, sur chaque slave, on indique depuis quel serveur notre slave doit se répliquer :
replicaof db01.morot.local 6379
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 :
127.0.0.1:6379> AUTH SECRET OK 127.0.0.1:6379> INFO Replication # Replication role:master connected_slaves:2 slave0:ip=192.168.69.83,port=6379,state=online,offset=1106,lag=0 slave1:ip=192.168.69.82,port=6379,state=online,offset=1106,lag=0 master_replid:75b55df27cafe6bfd7aec3114bfd63e77d45a8cb master_replid2:0000000000000000000000000000000000000000 master_repl_offset:1106 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:1106
A 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 indiquĂ© 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.
127.0.0.1:6379> INFO Replication # Replication role:slave master_host:db01.morot.local master_port:6379 master_link_status:up master_last_io_seconds_ago:3 master_sync_in_progress:0 slave_repl_offset:1288 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:75b55df27cafe6bfd7aec3114bfd63e77d45a8cb master_replid2:0000000000000000000000000000000000000000 master_repl_offset:1288 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:1288
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.
127.0.0.1:6379> INFO Replication # Replication role:slave master_host:db01.morot.local master_port:6379 master_link_status:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 slave_repl_offset:3472 master_link_down_since_seconds:42
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 :
127.0.0.1:6379> replicaof db02.morot.local 6379 OK
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Ă©pliqua dâaucun serveur :
127.0.0.1:6379> replicaof no one OK 127.0.0.1:6379> info replication # Replication role:master connected_slaves:1 slave0:ip=192.168.69.83,port=6379,state=online,offset=3472,lag=0
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 :
127.0.0.1:6379> replicaof db02.morot.local 6379 OK
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Ă©pliqua 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 :
sudo apt -y install redis-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 :
bind 0.0.0.0 port 26379
Ajoutons un mot de passe de connexion, il sâagit du mot de passe dâauthentification sur Sentinel :
requirepass SECRET
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.
sentinel monitor mymaster 192.168.69.81 6379 2 sentinel auth-pass mymaster SECRET
Le master est considéré comme injoignable aprÚs une minute :
sentinel down-after-milliseconds mymaster 60000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1
Une redĂ©marrage du service sentinel plus tard, vĂ©rifions lâĂ©tat de notre service en se connectant via redis-cli sur le port de Sentinel cette fois :
redis-cli -p 26379 127.0.0.1:26379> AUTH SECRET OK 127.0.0.1:26379> info sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=192.168.69.81:6379,slaves=2,sentinels=4
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 quelques 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 :
37873:X 18 Jun 2023 21:33:04.343 # +sdown master mymaster 192.168.69.81 6379
Ensuite, nous avons un odown, câest Ă dire que le serveur est vu comme injoignable par au moins le nombre de serveur 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 :
37873:X 18 Jun 2023 21:33:05.420 # +odown master mymaster 192.168.69.81 6379 #quorum 3/2
db03 a été élu comme nouveau master :
37873:X 18 Jun 2023 21:34:17.255 # +selected-slave slave 192.168.69.83:6379 192.168.69.83 6379 @ mymaster 192.168.69.81 6379 37873:X 18 Jun 2023 21:34:17.437 # +promoted-slave slave 192.168.69.83:6379 192.168.69.83 6379 @ mymaster 192.168.69.81 6379
db02 est reconfiguré comme slave de db03 :
37873:X 18 Jun 2023 21:34:17.453 * +slave-reconf-sent slave 192.168.69.82:6379 192.168.69.82 6379 @ mymaster 192.168.69.81 6379 37873:X 18 Jun 2023 21:34:17.510 * +slave-reconf-inprog slave 192.168.69.82:6379 192.168.69.82 6379 @ mymaster 192.168.69.81 6379 37873:X 18 Jun 2023 21:34:18.623 * +slave slave 192.168.69.82:6379 192.168.69.82 6379 @ mymaster 192.168.69.83 6379
Enfin, on peut le confirmer via la cli redis sur db03 :
127.0.0.1:6379> INFO REPLICATION # Replication role:master connected_slaves:1 slave0:ip=192.168.69.82,port=6379,state=online,offset=481996,lag=1FĂ©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 :127.0.0.1:26379> SENTINEL failover mymaster OK 127.0.0.1:26379> INFO sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=192.168.69.82:6379,slaves=2,sentinels=4Il 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 :
frontend ft_redis
bind 0.0.0.0:6379 name redis
default_backend bk_redisbackend bk_redis
option tcp-check
tcp-check send AUTH\ SECRET\r\n
tcp-check send PING\r\n
tcp-check expect string +PONG
tcp-check send INFO\ Replication\r\n
tcp-check expect string role:master
tcp-check send QUIT\r\n
tcp-check expect string +OK
server r1 db01:6379 check inter 10s
server r2 db02:6379 check inter 10s
server r3 db03:6379 check inter 10sConclusion
Câest en 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.