Redis es un sistema de cacheo en memoria utilizado básicamente como almacén de sesiones y bróker de mensajería. Debido precisamente a la naturaleza del uso que se le da, es conveniente que dispongamos de una mínima infraestructura tolerante a fallos que es lo que aporta Sentinel.
Redis en su versión más básica se compone de un servidor que actúa como almacén de datos en memoria y un cliente que se conecta contra él, el cual es accedido por la aplicación o aplicaciones a través de una API de programación, almacenando en la memoria del servidor datos y recuperándolos luego a medida que sea necesario. La naturaleza de estos datos en la mayoría de las ocasiones hace que sea necesario que estén permanentemente accesibles, por lo que surge la necesidad de tener un sistema de failover y aquí es donde entra Sentinel.
Este tutorial está orientado a la configuración del cluster de Sentinel no a la propia instalación del producto ni a la automatización del arranque de los procesos.
Componentes
Redis / Sentinel tiene varios componentes que vamos a enumerar a continuación:
- Servidor Redis: Es el proceso encargado de almacenar y mantener los datos en memoria. Dado que vamos a trabajar con un cluster, hacemos una pequeña matización de cara al resto del documento. A la instancia Maestra de Redis la llamaremos M, y las instancias standby las llamaremos R (réplicas).
- Procesos Sentinel: Son los procesos encargados de monitorizar las instancias de Redis (maestras y replicas) y de iniciar el proceso de Failover en caso de que sea necesario promover una instancia replica a maestra. A partir de ahora aparecerán como S.
- Procesos Cliente: Son los procesos encargados de comunicar la aplicación con el servidor de Redis. A partir de ahora los definiremos como C.
Cada uno de los componentes pueden coexistir en la misma máquina, pero en función de las necesidades puede ser conveniente o no.
Premisas
En cualquier configuración de Sentinel cualquiera de los componentes citados anteriormente puede encontrarse en cualquier número, salvo la instancia maestra, de la que solo puede haber una instancia. El resto de componentes pueden existir en cualquier número en función de nuestra arquitectura o del comportamiento que queramos que tenga el entorno.
Hay un concepto importante relativo a los procesos de Sentinel, que es el quorum. En lo que a Sentinel se refiere, el quorum es el número de instancias que tiene que estar “de acuerdo” a la hora de marcar un nodo como caído y promocionar un Slave a Master. Es importante tener en cuenta el concepto, ya que a partir de aquí en los escenarios que veremos, siempre trabajaremos con al menos 3 instancias de Sentinel para tener al menos un quorum de 2.
Teniendo en cuenta estos conceptos básicos, pasamos a ver un primer ejemplo práctico.
Ejemplo práctico
Vamos a plantear el ejemplo como el mínimo imprescindible para tener un entorno que cumpla con varias condiciones:
- Tener 2 instancias de Redis.
- Tener 3 instancias de Sentinel.
- Tener 1 cliente.
Tenemos por tanto:
- Una instancia maestra (M1) con un sentinel (S1).
- Una segunda máquina con una réplica (R1) y un cliente de sentinel (S2).
- Una tercera máquina con un cliente (C1) y un sentinel (S3) Vamos a definir un quorum de 2.
Comenzamos con la instancia master (M1+S1)
Primero configuramos Redis. Para ello editamos el fichero redis.conf que estará en el directorio donde hayamos compilado Redis. Podemos dejar prácticamente todo el fichero por defecto, pero hemos de modificar los siguientes valores:
bind 172.31.32.83
Es la dirección del interface en el que queremos que escuche Redis. Si lo dejamos por defecto escuchara en todos los interfaces de la máquina, lo cual puede suponer un problema de seguridad, por lo que en ese caso le hacemos bind a la IP del rango interno:
port 6379
El puerto de Redis. En este caso lo dejamos por defecto:
slave-priority 1
Es el orden en el que se utilizaran las instancias que tengamos. Cuanta más baja sea la prioridad, más alta será la posibilidad de que esta instancia sea la utilizada en caso de caída. Aunque esta instancia no es slave, ponemos el parámetro, ya que en caso de caída y de que otra instancia sea promocionada a maestra, Sentinel marcará esta instancia como Slave. Al ponerle 1, nos aseguramos que en caso de una caída posterior esta instancia volverá a ser la maestra.
Arrancamos la instancia de Redis
Desde el directorio src de la instalación de Redis:
nohup ./redis-server /home/ec2-user/redis-3.2.0/redis.conf &
Una vez lo tenemos arrancado, procedemos a configurar Sentinel. Para ello editamos el fichero sentinel.conf:
bind 172.31.32.83
sentinel monitor mymaster 172.31.32.83 6379 2
sentinel down-after-milliseconds mymaster 6000
Hacemos el bind a la IP, le indicamos el nombre de la instancia de Redis que le queremos poner (en este caso mymaster, aunque podemos poner el nombre que queramos) y el quorum que queremos para nuestra configuración (2).
Una vez lo tenemos, arrancamos sentinel:
nohup ./redis-server /home/ec2-user/redis-3.2.0/sentinel.conf --sentinel &
Si consultamos el nohup.out, veremos lo siguiente:
2037:X 10 Jun 04:34:45.565 # +monitor master mymaster 172.31.32.83 6379 quorum 2
Comenzamos con la instancia replica (R1+S2)
Editamos, al igual que hemos hecho en la instancia anterior, el sentinel.conf.
Modificamos el bind con la ip interna de la máquina, dejamos el mismo puerto que teníamos y modificamos el slave-priority:
slave-priority 10
Dado que esta instancia de réplica, tenemos que añadir la siguiente línea:
slaveof 172.31.32.83 6379
De esta forma, le indicamos que es una instancia slave y quien es el master. Arrancamos Redis:
nohup ./redis-server /home/ec2-user/redis-3.2.0/redis.conf &
Y ahora si vamos al nohup.log veremos los siguiente, el slave que acabamos de arrancar, tiene que conectar con el master y replicar los datos:
2026:S 10 Jun 04:53:35.081 * The server is now ready to accept connections on port 6376
2026:S 10 Jun 04:53:35.081 * Connecting to MASTER 172.31.32.83:6379
2026:S 10 Jun 04:53:35.081 * MASTER <-> SLAVE sync started
2026:S 10 Jun 04:53:35.082 * Non blocking connect for SYNC fired the event
2026:S 10 Jun 04:53:35.082 * Master replied to PING, replication can continue.. .
2026:S 10 Jun 04:53:35.084 * Partial resynchronization not possible (no cached master)
2026:S 10 Jun 04:53:35.086 * Full resync from master:cbad7f82947d58bf5408cbd95051850427031dc:1`
2026:S 10 Jun 04:53:35.119 * MASTER <-> SLAVE sync: receiving 76 bytes from master
2026:S 10 Jun 04:53:35.119 * MASTER <-> SLAVE sync: Flushing old data
2026:S 10 Jun 04:53:35.119 * MASTER <-> SLAVE sync: Loading DB in memory
2026:S 10 Jun 04:53:35.119 * MASTER <-> SLAVE sync: Finished with success
Ahora editamos el fichero de configuración de sentinel y modificamos:
bind 172.31.20.13
sentinel monitor mymaster 172.31.20.13 6379 2
sentinel down-after-milliseconds mymaster 60000
y arrancamos:
nohup ./redis-server /home/ec2-user/redis-3.2.0/sentinel.conf --sentinel &
y veremos en el nohup.log que ha arrancado correctamente:
2032:X 10 Jun 04:57:52.068 # +monitor master mymaster 172.31.20.13 6379 quorum 2
Comenzamos con la instancia cliente (S3+C1)
En este caso, solo nos centramos en la configuración de Sentinel, ya que el cliente no es objeto de configuración en este documento.
Editamos el fichero de configuración de Sentinel y modificamos:
[root@ip-172-31-30-7 redis-3.2.0]# more sentinel.conf
bind 172.31.30.7
sentinel monitor mymaster 172.31.20.13 6379 2
sentinel down-after-milliseconds mymaster 60000
y arrancamos:
nohup ./redis-server /home/ec2-user/redis-3.2.0/sentinel.conf --sentinel &
y veremos en el nohup.log que ha arrancado correctamente:
2019:X 10 Jun 05:04:14.091 * +slave slave 172.31.30.7:6379 172.31.30.7 6379 @ mymaster 172.31.32.83 6379
2019:X 10 Jun 05:04:14.091 * +slave slave 172.31.20.13:6379 172.31.20.13 6379 @ mymaster 172.31.32.83 6379
Ya tenemos configurado y arrancado el entono.
Ahora, en cada uno de los nodos, podemos ejecutar el siguiente comando (cada máquina con su IP):
./redis-cli -h 172.31.20.13 -p 26379
Y ejecutamos el siguiente comando:
172.31.20.13:26379> info
Nos vamos hasta el fondo de la salida del comando y veremos:
master0:name=mymaster,status=ok,address=172.31.32.83:6379,slaves=2,sentinels=3
Probando el cluster
Para probar el cluster, vamos a proceder a tirar el nodo Master con un shutdown. Al hacerlo, si vamos al nohup.conf del nodo Slave, vemos lo siguiente:
2026:S 10 Jun 05:17:08.066 * Caching the disconnected master state.
2026:S 10 Jun 05:17:08.212 * Connecting to MASTER 172.31.32.83:6379
2026:S 10 Jun 05:17:08.212 * MASTER <-> SLAVE sync started`
2026:S 10 Jun 05:17:08.213 # Error condition on socket for SYNC: Connection refused
2026:S 10 Jun 05:17:09.213 * Connecting to MASTER 172.31.32.83:6379
2026:S 10 Jun 05:17:09.213 * MASTER <-> SLAVE sync started`
2032:X 10 Jun 05:18:08.079 # +sdown sentinel e5e39d16af2c4d69c3214e423b4e958d1b668987 172.31.32.83 26379 @ mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:08.181 # +sdown master mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:08.272 # +odown master mymaster 172.31.32.83 6379 #quorum 2/2
2032:X 10 Jun 05:18:08.272 # +new-epoch 7
2032:X 10 Jun 05:18:08.272 # +try-failover master mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:08.273 # +vote-for-leader da85a1715456a44f8bda6a9c90c57215fab71a8a 7
2032:X 10 Jun 05:18:08.277 # e76dfc4640e06aec9258aef8dce04fdd97c531d2 voted for da85a1715456a44f8bda6a9c90c57215fab71a8a 7
2032:X 10 Jun 05:18:08.325 # +elected-leader master mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:08.326 # +failover-state-select-slave master mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:08.378 # +selected-slave slave 172.31.20.13:6379 172.31.20.13 6379 @ mymaster 172.31.32.83 6379`
2032:X 10 Jun 05:18:08.378 * +failover-state-send-slaveof-noone slave 172.31.20.13:6379 172.31.20.13 6379 @ mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:08.440 * +failover-state-wait-promotion slave 172.31.20.13:637
172.31.20.13 6379 @ mymaster 172.31.32.83 6379`
2026:M 10 Jun 05:18:08.440 * Discarding previously cached master state.
2026:M 10 Jun 05:18:08.441 * MASTER MODE enabled (user request from 'id=11 addr=172.31.20.13:49060 fd=8 name=sentinel-da85a171-cmd age=1126 idle=0 flags=x db=0 sub=0 psub=0 multi=3 qbuf=0 qbuf-free=32768 obl=36 oll=0 omem=0 events=r cmd=exec')`
2026:M 10 Jun 05:18:08.442 # CONFIG REWRITE executed with success.
2026:M 10 Jun 05:18:08.443 * 1 changes in 900 seconds. Saving...
2026:M 10 Jun 05:18:08.443 * Background saving started by pid 2089
2089:C 10 Jun 05:18:08.446 * DB saved on disk`
2089:C 10 Jun 05:18:08.446 * RDB: 6 MB of memory used by copy-on-write
2026:M 10 Jun 05:18:08.543 * Background saving terminated with success
2032:X 10 Jun 05:18:09.296 # +promoted-slave slave 172.31.20.13:6379 172.31.20.13 6379 @ mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:09.296 # +failover-state-reconf-slaves master mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:09.377 # +failover-end master mymaster 172.31.32.83 6379
2032:X 10 Jun 05:18:09.377 # +switch-master mymaster 172.31.32.83 6379 172.31.20.13 6379
2032:X 10 Jun 05:18:09.377 * +slave slave 172.31.30.7:6379 172.31.30.7 6379 @ mymaster 172.31.20.13 6379
2032:X 10 Jun 05:18:09.377 * +slave slave 172.31.32.83:6379 172.31.32.83 6379 @ mymaster 172.31.20.13 6379
Podemos observar como al detectar la caída del nodo master, con IP 172.31.32.83, automáticamente promociona al nodo réplica, con IP 172.31.20.13, como nodo master.
Espero que os haya gustado. Para estar al día de todos los artículos, ¡síguenos en Twitter!.