Infrastructure Redis en haute disponibilité

Par Faichelbaum @faichelbaum

A l'heure où tout devient "connecté", "disponible en ligne", les échanges par l'internet se multiplient. Pour accompagner la démocratisation des liens rapides mais également pour améliorer l'expérience cliente, il est nécessaire que les fournisseurs de service en ligne optimisent leurs applications et la manière dont ils les livrent. Bien sûr, on peut toujours augmenter les ressources matériels, mais le coût n'est pas négligeable. Parfois, les développeurs peuvent travailler main dans la main avec les administrateurs. C'est le cas de l'utilisation des caches applicatifs (Memcached,Couchbase, Redis...).

Redis, dans la version stable actuelle (2.6) n'offre aucune fonctionnalité de mise en cluster comme Couchbase. Je ne parle pas non plus de l'absence totale d'organes de sécurité. Le but est clairement énoncé : la PER-FOR-MANCE.

Du coup, peu de solution pour la haute disponibilité si ce n'est mettre en place une architecture type maître/esclave avec une promotion automatique de l'esclave en tant que maître en cas de défaillance du premier. Nous allons voir cette mise en place puis tant qu'à faire, sa supervision.

On suppose pré-installés deux serveurs Debian Squeeze 64 bit. Notez que la version disponible de Redis dans les repositories officiels est une vieille 1.2.6 et que seule la branche 2.4 est disponible dans les backports. Traduction : nous allons être obligés de tout compiler à la main.

Pour cela, quelques pré-requis à installer :

aptitude install build-essential tcl8.5 pwgen

Le package tcl8.5 est là pour vous permettre d'effectuer quelques tests au besoin lors de la séance de compilation. Nous pouvons alors nous attaquer à Redis :

cd /usr/src
wget http://redis.googlecode.com/files/redis-2.6.7.tar.gz
tar -xzf redis-2.6.7.tar.gz
rm redis-2.6.7.tar.gz
cd redis-2.6.7
make
make install
useradd redis
mkdir /opt/redis
chown redis:redis /opt/redis
chmod 770 /opt/redis

Je vous met à disposition un fichier un peu préparer, ici. Cependant, quelques modifications sont à prévoir.

wget http://cdn.aichelbaum.com/files/r/redis.conf -O /etc/redis.conf

Dans le cas que je présente ici, je bind l'application sur toutes les interfaces car les serveurs Redis sont sur un VLAN précis et isolé. Au besoin, on peut simplement l'isoler sur une IP en rajoutant dans la configuration un :

bind X.X.X.X

Il s'agit également de traitement en temps réel mais donc j'ai besoin d'avoir un maximum de garanties sur la pérennité des informations stockées, je sauvegarde toutes les 60 secondes s'il y a eu au moins une modification en base.

Redis a (trop) peu de sécurité. Cependant, autant mettre au moins un mot de passe pour la réplication. Pour se faire, on va en générer un propre avec pwgen :

pwgen -s -y 16

Il est à renseigner à la ligne commençant par masterauth. On réutilise le même mot dfe passe pour le champ requirepass.

Autre point important à modifier dans la configuration : la taille de la mémoire allouée à Redis. Dans mon exemple :

maxmemory 512mb

On peut aussi la passer à 8 Go par exemple :

maxmemory 8gb

Penser également à rajouter la ligne suivante dans votre /etc/security/limits.conf :

redis - nofile 20000

Redis nécessite un rajout à sysctl :

cat < EOF >> /etc/sysctl.conf 
vm.overcommit_memory=1
net.ipv4.conf.eth0.arp_ignore = 1
net.ipv4.conf.eth1.arp_ignore = 1
net.ipv4.conf.eth0.arp_announce = 2
net.ipv4.conf.eth1.arp_announce = 2
EOF
sysctl -p

On a donc installé deux serveurs. On suppose leurs noms et IP :

  • redis01 - 192.168.1.1
  • redis02 - 192.168.1.2

On veut que redis02 soit esclave de redis01 :

sed -i "s/# slaveof <masterip> <masterport>/# slaveof <masterip> <masterport>\n#slaveof 192.168.1.1 6379/" /etc/redis.conf

Mais aussi l'inverse pour que redis01 devienne automatiquement esclave de redis01 en cas de failover :

sed -i "s/# slaveof <masterip> <masterport>/# slaveof <masterip> <masterport>\n#slaveof 192.168.1.2 6379/" /etc/redis.conf

On va entre s'appuyer sur redis-sentinel qui est une solution permettant de gérer facilement le failover entre les serveurs.

cp /usr/src/redis-2.6.7/src/redis-sentinel /usr/local/bin/redis-sentinel

Plus qu'à importer la configuration (et l'adapter avec vos IP et mots de passe) :

wget http://cdn.aichelbaum.com/files/s/sentinel.conf -O /etc/sentinel.conf

Petite précision : sentinel gère le failover, pas le fallback automatique. Il faut donc que l'ancien maître ( redis01) devienne un esclave du nouvellement promu ( redis02).

Pensez à installer un 3e sentinel sur le serveur de supervision avec les mêmes informations. Il vous permettra d'avoir un quorum de 2 sur les 3 sentinels possibles, très utile en cas de split-brain (chaque serveur est UP & Running mais ne voit plus son voisin).

On n'avait pas encore les fichiers d'init donc on va les mettre en place avec l'intelligence. J'ai préparé deux scripts d'init pour le coup

wget http://cdn.aichelbaum.com/files/r/redis-server.init -O /etc/init.d/redis-server
wget http://cdn.aichelbaum.com/files/r/redis-sentinel.init -O /etc/init.d/redis-sentinel
chmod +x /etc/init.d/redis-se*
update-rc.d redis-sentinel defaults
update-rc.d redis-server defaults
insserv redis-sentinel
insserv redis-server

On va gérer l'IP flottante permettant de n'accéder qu'au maître (pour fonctionner en actif/passif) avec un bête script. Pourquoi bête ? parce que le mode de fonctionnement ne requière pas l'installation d'une usine à gaz et qu'on a un simple cron qui peut faire le boulot. Pensez juste à éditer l'IP mise pour la VIP.

wget http://cdn.aichelbaum.com/files/r/redis-vip.sh -O /usr/local/bin/redis-vip.sh
chmod +x /usr/local/bin/redis-vip.sh
echo "* * * * * /usr/local/bin/redis-vip.sh &> /dev/null" > /etc/cron.d/redis-vip

On va s'appuyer sur un plugin existant : check_redis. Il permet aussi bien de superviser l'instance Redis en elle-même, que la réplication. Pour cela, il demande au serveur son rôle dans la réplication et dans le cas d'un esclave ( slave) son status.

Rien de particulier pour l'installation en soit. Le plugin est vraiment standard :

Usage: check_redis -H HOSTNAME -p PORT -c CRITICAL -w WARNING -t TIMEOUT (-P PASSWORD)

Par contre, il faut également superviser les sentinels. Au choix : un check_tcp sur le port 26379 ou vérifier en NRPE ou SNMP le status du service redis-sentinel.