Howto Redis
- Documentation: https://redis.io/documentation
- Rôle Ansible : https://gitea.evolix.org/evolix/ansible-roles/src/branch/stable/redis
Redis est un serveur noSQL clé-valeur très stable (écrit en 2009). Une valeur peut être une chaîne de caractères, un tableau, une liste, etc. Redis tourne en mémoire, et si besoin sauvegarde de temps en temps ses données sur le disque.
Installation
Les versions suivantes sont disponibles selon la version de Debian :
- Debian 12 (Bookworm) : Redis 7.0.15
- Debian 11 (Bullseye) : Redis 6.0.16
- Debian 10 (Buster) : Redis 5.0.14
- Debian 9 (Stretch) : Redis 3.2.6
# apt install redis-server redis-tools
$ /usr/bin/redis-server -v
Redis server v=7.0.15 sha=00000000:0 malloc=jemalloc-5.3.0 bits=64 build=8fef3e995a542118
# systemctl status redis
● redis-server.service - Advanced key-value store
Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; preset: enabled)
Active: active (running) since Thu 2024-02-15 09:55:26 CET; 6 days ago
Docs: http://redis.io/documentation,
man:redis-server(1)
Main PID: 192625 (redis-server)
Status: "Ready to accept connections"
Tasks: 5 (limit: 9514)
Memory: 8.2M
CPU: 12min 56.115s
CGroup: /system.slice/redis-server.service
└─192625 "/usr/bin/redis-server 127.0.0.1:6379"
Configuration
La configuration principale se fait dans
/etc/redis/redis.conf
dont voici quelques options :
daemonize yes
pidfile /var/run/redis.pid
unixsocket /var/run/redis/redis.sock
bind 127.0.0.1
port 6379
timeout 300
loglevel notice
logfile /var/log/redis/redis-server.log
databases 16
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir /var/lib/redis
#requirepass <password>
maxclients 4096
maxmemory 0
Note importante: la valeur de maxmemory
indique la quantité maximale de mémoire utilisable, au delà de laquelle
Redis applique une politique
spécifique. Par défaut il s’agit de noeviction
. Voir
plus bas pour plus de détails.
Pour choisir la bonne valeur de maxmemory
, on peut
s’aider de la commande Redis INFO MEMORY
(Redis < 4) ou
MEMORY STATS
(Redis >= 4).
Configuration à chaud
La plupart des options sont reconfigurables à chaud en mode CLI via
la commande CONFIG SET
. La liste des arguments peut être
listée :
# redis-cli
redis> CONFIG GET *
1) "dir"
2) "/var/lib/redis"
3) "dbfilename"
4) "dump.rdb"
5) "requirepass"
6) (nil)
7) "masterauth"
8) (nil)
9) "maxmemory"
10) "0"
Par exemple, pour passer le paramètre maxmemory
à 1G
:
# redis-cli
redis> AUTH <PASSWORD>
OK
redis> CONFIG SET maxmemory 1024000000
OK
Attention : Si on veut que la configuration soit persistante, il faut
également mettre à jour le paramètre dans
/etc/redis/redis.conf
.
bind
Pour écouter sur plusieurs interfaces :
bind 192.0.32.42 127.0.0.1
Attention : si on fait un bind
sur
plusieurs IP, il faut que la première de la liste soit l’IP publique du
serveur. Par exemple : bind 192.0.32.42 127.0.0.1
Réglage du noyau
Il est recommandé, surtout pour les grosses instances redis d’activer l’overcommit au niveau du Kernel Linux. Si ce n’est pas le cas, ça peut empêcher Redis de faire ses sauvegardes.
# cat /etc/sysctl.d/evolinux-redis.conf
vm.overcommit_memory=1
# sysctl vm.overcommit_memory=1
Politique sur la mémoire
Il est possible de démarrer Redis sans limite de mémoire. Il va donc occuper autant de RAM que nécessaire. Si les données stockées croissent suffisamment, ça peut conduire à une saturation de la RAM disponible et une intervention du noyau qui pourra tuer des processus (Out Of Memory Kill).
On peut aussi définir une quantité maximale de mémoire utilisable par
Redis (paramètre maxmemory
, en octets, fixé à 100 Mo par
défaut dans nos installations). Il faut indiquer à Redis quoi faire
lorsque celle-ci est épuisée (paramètre maxmemory-policy
).
Il y a plusieurs options, et le choix dépend du type de données que l’on
stocke dans Redis et de la manière on les utilise. le mieux est de se
référer à la documentation
officielle, mais voici les politiques d’éviction disponibles :
noeviction
: Ne sauvegarde pas les nouvelles valeurs lorsque la limite est atteinte. En cas de réplication, ça s’applique au primaire.allkeys-lru
: Conserve les clés les plus récemment utilisées et supprime les moins récemment utilisées (LRU = « least recently used »).allkeys-lfu
: Conserve les clés les plus fréquemment utilisées et supprime les moins fréquemment utilisées (LFU = « least frequently used »).volatile-lru
: Supprime les clés ayant une expiration définie les moins récemment utilisées.volatile-lfu
: Supprime les clés ayant une expiration définie les moins fréquemment utilisées.allkeys-random
: Supprime au hasard des clés.volatile-random
: Supprime au hasard des clés ayant une expiration définie.volatile-ttl
: Supprime des clés ayant une expiration définie, par ordre du plus petit TTL restant (TTL = « Time To Live »).
Lorsque Redis est utilisé exclusivement pour du cache, il est
conseillé de choisir allkeys-lfu
(ou
allkeys-lru
si la version de Redis ne supporte pas encore
allkeys-lfu
). Lorsque les données stockées dans Redis ne
doivent surtout pas risquer de disparaître, il faut choisir
noeviction
et gérer applicativement les éventuelles erreurs
d’écriture.
Utilisation
En mode CLI :
$ redis-cli
redis 127.0.0.1:6379>
redis> set foo 3
OK
redis> get blabla
(nil)
redis> get foo
3
redis> keys *
1) "foo"
redis> mset un 1 deux 2 trois 3 quatre 4
OK
redis> keys *
1) "un"
2) "foo"
3) "deux"
4) "trois"
5) "quatre"
redis> *keys *r*
1) "four"
2) "three"
redis> get four
"4"
redis> del "trois" "quatre"
(integer) 2
On peut se connecter avec la socket si elle existe :
$ redis-cli -s /path/to/redis.sock
Mais aussi en réseau (sans authentification, attention) :
$ telnet 127.0.0.1 6379
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get foo
$1
3
quit
Connection closed by foreign host.
En PHP, il existe différentes bibliothèques. Nous utilisons principalement http://github.com/nicolasff/phpredis et nous utilisons un fork pour avoir un packaging Debian propre :
$ git clone <https://github.com/gcolpart/phpredis>
$ cd phpredis
$ tar --exclude debian --exclude .git -cvzf ../php5-redis_0.1~git20120519.orig.tar.gz .
$ git-buildpackage -us -uc
Sur Debian Stretch, on peut aussi installer le paquet php-redis.
Sessions PHP avec Redis
Redis peut notamment être utilisé pour stocker les sessions PHP. Par exemple :
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=<password>"
Instances Redis
Il est possible de faire fonctionner plusieurs instances de Redis sur un serveur ; chacune avec ses propres données, sa propre configuration et son propre utilisateur.
Template systemd
Créer le template d’unité systemd suivant
dans /etc/systemd/system/redis-server@.service
:
[Unit]
Description=Advanced key-value store
After=network.target
[Service]
Type=forking
ExecStart=/usr/bin/redis-server /etc/redis-%i/redis.conf
PIDFile=/var/run/redis-%i/redis-server.pid
TimeoutStopSec=0
Restart=always
User=redis-%i
Group=redis-%i
RuntimeDirectory=redis-%i
ExecStartPre=-/bin/run-parts --verbose /etc/redis-%i/redis-server.pre-up.d
ExecStartPost=-/bin/run-parts --verbose /etc/redis-%i/redis-server.post-up.d
ExecStop=-/bin/run-parts --verbose /etc/redis-%i/redis-server.pre-down.d
ExecStop=/bin/kill -s TERM $MAINPID
ExecStopPost=-/bin/run-parts --verbose /etc/redis-%i/redis-server.post-down.d
UMask=007
PrivateTmp=yes
LimitNOFILE=65535
PrivateDevices=yes
ProtectHome=yes
ReadOnlyDirectories=/
ReadWriteDirectories=-/var/lib/redis-%i
ReadWriteDirectories=-/var/log/redis-%i
ReadWriteDirectories=-/var/run/redis-%i
CapabilityBoundingSet=~CAP_SYS_PTRACE
# redis-server writes its own config file when in cluster mode so we allow
# writing there (NB. ProtectSystem=true over ProtectSystem=full)
ProtectSystem=true
ReadWriteDirectories=-/etc/redis-%i
[Install]
WantedBy=multi-user.target
Recharger la configuration systemd :
systemctl daemon-reload
Installation
Créer un utilisateur système pour l’instance :
useradd --system --home-dir /var/lib/redis/instance1 redis-instance1
Créer ensuite le datadir et le dossier de log :
mkdir -m 0750 -p /var/lib/redis/instance1 /var/log/redis/instance1
chown redis-instance1: /var/lib/redis/instance1 /var/log/redis/instance1
Créer ensuite le fichier de configuration suivant dans
/etc/redis/redis-instance1.conf
:
daemonize yes
port 0 # Listen only on unix socket defined by systemd unit
unixsocketperm 770 # Unix socket only accessible by user and group of instance1
timeout 300
loglevel notice
logfile /var/log/redis/instance1/redis-server.log
databases 16
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir /var/lib/redis/instance1
#requirepass <password>
maxclients 128
maxmemory 104857600
Administration
L’instance Redis « instance1 » est maintenant administrable via :
systemctl start redis-server@instance1
systemctl stop redis-server@instance1
systemctl restart redis-server@instance1
systemctl status redis-server@instance1
systemctl enable redis-server@instance1
systemctl disable redis-server@instance1
Pour s’y connecter en CLI :
redis-cli -p <INSTANCE_PORT>
redis-cli -s <INSTANCE_SOCKET>
Sauvegardes
http://redis.io/topics/persistence
Par défaut, Redis sauvegarde de temps en temps ses données sur le disque (mode RDB persistance). Il suffit donc de copier le fichier /var/lib/redis/dump.rdb pour sauvegarder une base Redis à chaud !
Pour restaurer les données, il suffira d’éteindre Redis, remettre en place le fichier RDB, et relancer Redis.
Réplication interne
http://redis.io/topics/replication
Note: le terme « slave » a été remplacé par « replica » dans la plupart des commandes et des messages, mais il peut tout de même apparaître à certains endroits. À priori les commandes basées sur ce terme sont toujours utilisables (pour rétro-compatibilité).
Côté « replica » il suffit de mettre en place la configuration :
replicaof <IP DU MASTER> 6379
Une fois le démon relancé on peut vérifier l’état de la réplication de la façon suivante :
# redis-cli -h X.X.X.X -p 6379 info replication
On peut arrêter la réplication et passer un replica en master
# redis-cli -h X.X.X.X -p 6379 replicaof no one
# redis-cli -h X.X.X.X -p 6379 info replication
# Replication
role:master
connected_slaves:1
master_repl_offset:631177410
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:630128835
repl_backlog_histlen:1048576
Tests
On teste une écriture sur le master, est-ce répliqué sur le replica ?
redis [master] 127.0.0.1:6379> set key value
redis [replica] 127.0.0.1:6379> get key
On devrait avoir « value ». Par ailleurs on pourra lire dans les logs
du replica (/var/log/redis/redis-server.log
) :
[26287] 06 Sep 15:04:04 * MASTER <-> REPLICA sync: receiving 34 bytes from master
Depuis redis 2.6 (2.4 en Wheezy), par défaut, le replica est en read-only, on peut le passer en read-write, en mettant ceci dans la configuration du replica :
replica-read-only off
Réplication avec Sentinel
Sentinel permet de surveiller un ensemble d’instances avec réplication. Il repose sur plusieurs processus qui communiquent entre-eux afin d’aboutir à un consensus sur l’état des réplications et l’élection d’un master. C’est un processus séparé de Redis lui-même.
Par exemple, une instance master “redis1” et un replica “redis2”. En cas de panne de “redis1”, les processus Sentinel vont s’accorder sur le fait que “redis2” devient le master. À son retour en ligne, “redis1” sera promu replica de “redis2”.
Un seul ensemble de processus Sentinel peut surveiller un ou plusieurs ensemble de réplications Redis.
Unité systemd
Depuis Debian 9, Le paquet redis-sentinel
fournit une
unité systemd.
Pour des versions précédentes, on peut créer l’unité systemd suivante
dans /etc/systemd/system/redis-sentinel.service
:
[Unit]
Description=Advanced key-value store (monitoring)
After=network.target
[Service]
Type=simple
PIDFile=/run/redis/sentinel.pid
ExecStart=/usr/bin/redis-sentinel /etc/redis/sentinel.conf --unixsocket /run/redis/sentinel.sock --pidfile /run/redis/sentinel.pid
ExecStop=/usr/bin/redis-cli -s /run/redis/sentinel.sock shutdown
Restart=always
User=redis
Group=redis
[Install]
WantedBy=multi-user.target
Configuration
Créer ensuite le fichier de configuration suivant dans
/etc/redis/sentinel.conf
:
port 16379
dir "/tmp"
sentinel monitor replication1 127.0.0.1 6379 1
sentinel down-after-milliseconds replication1 1000
sentinel config-epoch replication1 7
sentinel leader-epoch replication1 8
sentinel known-replica replication1 127.0.0.1 6380
sentinel current-epoch 8
Attention : si on fait un bind
sur
plusieurs IP, il faut que la première de la liste soit l’IP publique du
serveur. Par exemple : bind 192.168.2.1 127.0.0.1
Sentinel doit pouvoir ecrire dans son fichier de configuration :
chown redis: /etc/redis/sentinel.conf
Utilisation
Utiliser la commande redis-cli
pour se connecter.
Quelques commandes Sentinel utiles :
sentinel> info
=> infos complètes
sentinel> sentinel masters
=> liste des groupes Redis connus
sentinel> sentinel master <groupe>
=> infos spécifique à un groupe
sentinel> sentinel get-master-addr-by-name <groupe>
=> info du master d'un groupe
sentinel> sentinel replicas <groupe>
=> liste des réplica d'un groupe
sentinel> sentinel failover <groupe>
=> force le failover d'un groupe
Load-balancing avec HAProxy
Il est possible d’avoir un master et plusieurs replica derrière un
proxy/load-balancer HAProxy (sur 172.19.3.100
dans
l’exemple).
frontend fe_redis
mode tcp
option tcplog
bind 127.0.0.1:6379
default_backend be_redis
backend be_redis
mode tcp
balance first
default-server maxconn 1024 check agent-check agent-port 16379 agent-inter 1s
server srv1 172.20.3.200:6379
server srv2 172.20.3.201:6379
Sur les serveurs avec Redis (172.19.3.200
et
172.19.3.201
dans notre exemple), il faut installer le
paquet xinetd
puis créer une configuration
/etc/xinetd.d/haproxy-agent-redis-role
:
service haproxy-agent-redis-role
{
socket_type = stream
bind = 172.19.3.200
port = 16379
protocol = tcp
wait = no
type = UNLISTED
user = root
server = /usr/share/scripts/haproxy-agent-redis-role.sh
log_on_failure += USERID
disable = no
only_from = 172.19.3.100
}
On active ce service dans /etc/services
:
haproxy-agent-redis-role 16379/tcp
Puis on redémarre xinetd
:
systemctl restart xinetd
Enfin, on installe le script qui sert d’agent
/usr/share/scripts/haproxy-agent-redis-role.sh
:
#!/bin/sh
set -u
config_var() {
variable=$1
file=$2
test -f "${file}" && grep -E "^${variable}\s+.+$" "${file}" | awk '{ print $2 }' | sed -e "s/^[\"']//" -e "s/[\"']$//"
}
get_role() {
host=$(config_var "bind" "${conf_file}")
port=$(config_var "port" "${conf_file}")
pass=$(config_var "requirepass" "${conf_file}")
if [ -n "${pass}" ]; then
export REDISCLI_AUTH="${pass}"
fi
cmd="${redis_cli_bin} -h ${host} -p ${port} INFO REPLICATION"
${cmd} 2>/dev/null | grep -Eo "role:\w+" | cut -d ':' -f2
}
get_status() {
systemctl is-active "${1}"
}
redis_cli_bin=$(command -v redis-cli)
if [ -z "${redis_cli_bin}" ]; then
printf "%s\n" "down # missing redis"
exit 1
fi
if [ $# -lt 1 ]; then
service="redis-server.service"
conf_file="/etc/redis/redis.conf"
else
service="redis-server@${1}.service"
conf_file="/etc/redis-${1}/redis.conf"
fi
if [ ! -r "${conf_file}" ]; then
printf "%s\n" "down # missing config"
exit 3
fi
status=$(get_status "${service}")
case "${status}" in
active)
printf "%s " "up"
;;
inactive)
printf "%s\n" "stopped # systemd:${status}"
exit 1
;;
failed)
printf "%s\n" "fail # systemd:${status}"
exit 1
;;
*)
printf "%s\n" "down # systemd:${status}"
exit 1
;;
esac
role=$(get_role "${conf_file}")
case "${role}" in
master)
printf "%s" "ready # role:master"
;;
slave)
printf "%s" "maint # role:slave"
;;
*)
printf "%s" "maint # role:unknown"
;;
esac
printf "\n"
exit 0
Redis multi-instances
Ce script est compatible avec la présence de multiples instances de Redis.
Si on l’exécute avec un argument foo
il cherchera une
configuration /etc/redis-foo/redis.conf
et un service
systemd redis-server@foo
.
Il faudra alors déclarer autant de services xinetd que d’instances à surveiller :
haproxy-agent-redis-role-foo 16380/tcp
haproxy-agent-redis-role-bar 16381/tcp
Et enfin, modifier les services pour ajouter le paramètre
server_args
:
service haproxy-agent-redis-role-foo
{
socket_type = stream
bind = 172.19.3.200
port = 16380
protocol = tcp
wait = no
type = UNLISTED
user = root
server = /usr/share/scripts/haproxy-agent-redis-role.sh
server_args = foo
log_on_failure += USERID
disable = no
only_from = 172.19.3.100
}
Benchmarks
http://redis.io/topics/benchmarks
redis-benchmark -n 100000
=##### PING (inline)=
100000 requests completed in 1.88 seconds
50 parallel clients
3 bytes payload
keep alive: 1
97.82% <= 1 milliseconds
99.80% <= 2 milliseconds
99.89% <= 3 milliseconds
99.93% <= 4 milliseconds
99.93% <= 5 milliseconds
100.00% <= 6 milliseconds
100.00% <= 6 milliseconds
53276.50 requests per second
=##### PING=
100000 requests completed in 1.97 seconds
50 parallel clients
3 bytes payload
keep alive: 1
93.58% <= 1 milliseconds
99.36% <= 2 milliseconds
99.61% <= 3 milliseconds
99.71% <= 4 milliseconds
99.81% <= 5 milliseconds
99.87% <= 6 milliseconds
99.88% <= 7 milliseconds
99.88% <= 8 milliseconds
99.92% <= 10 milliseconds
99.93% <= 11 milliseconds
99.93% <= 12 milliseconds
99.95% <= 13 milliseconds
100.00% <= 13 milliseconds
50761.42 requests per second
=##### MSET (10 keys)=
100000 requests completed in 3.12 seconds
50 parallel clients
3 bytes payload
keep alive: 1
19.71% <= 1 milliseconds
91.36% <= 2 milliseconds
98.93% <= 3 milliseconds
99.66% <= 4 milliseconds
99.75% <= 5 milliseconds
99.76% <= 8 milliseconds
99.77% <= 9 milliseconds
99.77% <= 10 milliseconds
99.80% <= 11 milliseconds
99.80% <= 14 milliseconds
99.85% <= 15 milliseconds
99.89% <= 22 milliseconds
99.89% <= 23 milliseconds
99.94% <= 24 milliseconds
99.95% <= 160 milliseconds
99.95% <= 208 milliseconds
99.95% <= 245 milliseconds
99.95% <= 246 milliseconds
100.00% <= 246 milliseconds
32020.49 requests per second
=##### SET=
100000 requests completed in 1.86 seconds
50 parallel clients
3 bytes payload
keep alive: 1
97.80% <= 1 milliseconds
99.78% <= 2 milliseconds
99.84% <= 3 milliseconds
99.87% <= 4 milliseconds
99.93% <= 5 milliseconds
99.95% <= 10 milliseconds
99.96% <= 11 milliseconds
100.00% <= 12 milliseconds
100.00% <= 12 milliseconds
53850.30 requests per second
=##### GET=
100000 requests completed in 2.02 seconds
50 parallel clients
3 bytes payload
keep alive: 1
91.26% <= 1 milliseconds
99.65% <= 2 milliseconds
99.86% <= 3 milliseconds
99.89% <= 4 milliseconds
99.92% <= 5 milliseconds
99.97% <= 7 milliseconds
100.00% <= 7 milliseconds
49407.12 requests per second
[etc.]
Monitoring
En live
Voir des stats toutes les secondes :
$ redis-cli --stat
------- data ------ --------------------- load -------------------- - child -
keys mem clients blocked requests connections
227947 1.25G 21 0 15259436851 (+0) 252707982
227948 1.25G 22 0 15259437470 (+619) 252708002
227951 1.25G 20 0 15259438412 (+942) 252708021
227951 1.25G 18 0 15259438787 (+375) 252708025
[…]
Voir les requêtes que redis reçoit :
$ redis-cli monitor
Nagios
Depuis Debian Stretch, un check redis a été ajouté dans le
paquet nagios-plugins-contrib
, permettant d’avoir un check
plus sophistiqué qu’un simple check TCP. Il est capable de surveiller la
réplication, la consommation de mémoire… tout en retournant diverses
informations sur le status du serveur. Il faut penser à installer la lib
redis pour perl, absente des dépendances.
# apt install libredis-perl nagios-plugins-contrib
$ /usr/lib/nagios/plugins/check_redis -H 127.0.0.1
OK: REDIS 3.2.6 on 127.0.0.1:6379 has 42 databases (foo00,foo01,foo01...) with 179927 keys, up 17 hours 56 minutes
Sur Debian Jessie, il n’y a pas de check redis embarqué. A place, on peut faire un check Nagios « basique » consistant à initier une connexion TCP sur le socket unix du serveur Redis, et s’assurer qu’il répond bien :
$ /usr/lib/nagios/plugins/check_tcp -H /var/run/redis.pid
Il est toujours possible de récupérer le check avancé, pour l’utiliser sur Jessie :
- https://exchange.nagios.org/directory/Plugins/Databases/check_redis-2Epl/details
- https://github.com/willixix/WL-NagiosPlugins/blob/master/check_redis.pl
Munin
Un plugin Munin existe pour grapher divers paramètres comme la mémoire utilisée par Redis, le nombre de clés utilisées, le nombre de requêtes par seconde, et le nombre de clients connectés.
TODO: section obsolète, lien mort
Installer le paquet suivant si ne n’est pas déjà fait :
apt install libswitch-perl
Puis suivre la procédure :
# mkdir -p /usr/local/share/munin/plugins/
# cd /usr/local/share/munin/plugins/
# wget "https://raw.githubusercontent.com/munin-monitoring/contrib/master/plugins/redis/redis_"
# chmod -R 755 /usr/local/share/munin
# cd /etc/munin/plugins/
# for module in connected_clients key_ratio keys_per_sec per_sec used_keys used_memory; do ln -s /usr/local/share/munin/plugins/redis_ redis_${module}; done
Si vous avez une authentification pour votre serveur Redis :
# cat /etc/munin/plugin-conf.d/munin-node
[redis_*]
env.password PASSWORD