La instalación de Redis para un entorno de Producción que soporte una carga de trabajo importante, tiene varios detalles a tener en cuenta, tanto de cómo hacer la instalación, como de diferentes configuraciones necesarias a realizar, tanto a nivel de Redis como del Sistema Operativo. En este Artículo te cuento los detalles más importantes, para instalar Redis en Ubuntu, para un entorno de alta carga.
Continuando con nuestra serie sobre Redis, ahora que ya hemos hablado sobre las diferentes estructuras de datos que soporta Redis (Strings, Hashes, Lists, Sets, y Sorted Sets), del problema y posibles soluciones de la búsqueda sobre claves en función de sus Atributos, las Transacciones en Redis, el mecanismo de Publicación y Suscripción en Redis, los índices geoespaciales, y la ejecución de código de servidor en Redis con Lua (similar a los Procedimientos Almacenados), vamos a hablar sobre cómo instalar y configurar Redis en Ubuntu.
Instalación de Redis en Ubuntu
Es posible instalar Redis desde el código fuente y compilarlo, aunque personalmente es algo que prefiero evitar (hay quien dice que los hombres de verdad se pican su propios drivers), hacer las cosas más sencillas habitualmente sale más rentable. En cualquier caso, si quieres aventurarte, sería algo así.
apt-get install build-essential -y
wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make
Otra opción es instalar Redis desde los repos del Sistema Operativo, en este caso de ejemplo, utilizando una VM con Ubuntu 20.04 en Azure, me instaló Redis 5.0.7, una versión no demasiado reciente, la verdad. Así que, aunque esta es la opción más sencilla, tampoco mola mucho.
sudo apt update -y && sudo apt upgrade
sudo apt install redis-server
sudo systemctl status redis-server
redis-cli ping
redis-cli info
Otra forma es instalar Redis desde los propios repos oficiales de Redis. En este caso de ejemplo, utilizando una VM con Ubuntu 20.04 en Azure, me instaló un Redis 7.0.5, algo bastante más reciente que al usar los repos del propio Sistema Operativo. Así que, esta sería para mí la opción recomendada.
sudo apt install lsb-release
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt update -y && sudo apt upgrade
sudo apt-get install redis
sudo systemctl start redis-server
redis-cli ping
redis-cli info
Una vez instalado Redis, podemos comprobar el estado del servicio, pararlo, arrancarlo, o reiniciarlo, de la forma habitual.
sudo systemctl status redis-server
sudo systemctl stop redis-server
sudo systemctl start redis-server
sudo systemctl restart redis-server
Podemos comprobar cual es el PID del servicio Redis, consultando el fichero /run/redis/redis-server.pid
(también con systemd status), lo que nos ayudará para poder ver los diferentes ficheros del sistema operativo que nos darán más información sobre dicho proceso, por debajo de /proc, como se muestra en la siguiente imagen.
Podemos ver los Logs de Redis en el fichero /var/log/redis/redis-server.log para lo que podemos ejecutar comandos como los siguientes.
head /var/log/redis/redis-server.log -n 30
tail /var/log/redis/redis-server.log -n 30
less /var/log/redis/redis-server.log
A continuación se muestra un ejemplo de consulta del log de Redis en Ubuntu con el comando head.
También podemos necesitar consultar los Logs de Redis recopilados por systemd, para lo que podemos apoyarnos en el comando journalctl, como por ejemplo.
sudo journalctl --no-pager --boot --unit redis-server.service
A continuación se muestra la salida de ejecución del anterior comando en un entorno de pruebas.
Podemos arrancar un servidor de Redis desde línea de comandos directamente, por ejemplo para hacer pruebas, especificando un fichero de configuración, o bien, pasando por parámetros las configuraciones que deseemos (de la misma forma que se indicarían en el fichero de configuración). A continuación se muestran ambos ejemplos, uno para arrancar Redis de forma similar a como lo hace systemctl usando el mismo fichero de configuración y otro para arrancar manualmente una réplica del primero.
sudo /usr/bin/redis-server /etc/redis/redis.conf
sudo /usr/bin/redis-server --port 6380 --replicaof 127.0.0.1 6379
Configuración básica de Redis
El fichero de configuración de Redis es /etc/redis/redis.conf
, es bastante extenso pero está auto-documentado con bastante nivel de detalle (bastante bien, la verdad), y organizado por secciones (ej: General, Security, etc.), lo que facilita el trabajo cuando lo tengamos que editar, aunque en alguna ocasión nos interese consultar la documentación oficial o algún blog para asegurar cómo realizar correctamente cierta configuración. Las secciones del fichero de configuración de Redis son las siguientes:
- Includes
- Modules
- Network
- TLS/SSL
- General
- Snapshotting
- Replication
- Keys Tracking
- Security
- Clients
- Memory Management
- Lazy Freeing
- Threaded I/O
- Kernel OOM Control
- Kernel transparent hugepage Control
- Append Only Mode
- Shutdown
- Non-deterministic long blocking commands
- Redis Cluster
- Cluster Docker/Nat support
- Slow Log
- Latency Monitor
- Latency Tracking
- Event Notification
- Advanced Config
- Active defragmentation
Habitualmente tendremos que editarlo para adaptar algunas configuraciones, dependiendo de nuestras necesidades. A continuación ponemos algunos ejemplos para una configuración básica.
sudo vi /etc/redis/redis.conf
bind, protected-mode y requirepass
Por defecto, Redis sólo escucha en local (127.0.0.1), por lo que si necesitamos acceder desde fuera de la máquina, deberemos ajustar la configuración de bind en la sección Network, para que Redis escuche desde la interfaz o interfaces de red desde la que vayamos a recibir el tráfico. La opción más permisiva es cualquier interfaz (0.0.0.0) que a su vez es más insegura.
bind 0.0.0.0
Por defecto, Redis no incluye ninguna contraseña y el Modo Protegido está activado (rechazando las conexiones desde fuera si no hay una contraseña establecida, por motivos de seguridad). Por lo tanto, aunque debilitemos el bind para permitir conexiones desde fuera seguiremos sin poder conectarnos excepto desde local. Para solucionarlo, deberíamos deshabilitar el Modo Protegido o bien establecer una contraseña.
- Si deseamos deshabilitar el Modo Protegido, deberíamos cambiar la configuración protected-mode de la sección Network como se muestra a continuación (opción no recomendada, por ser una vulnerabilidad).
protected-mode no
- Si deseamos establecer una contraseña, deberíamos cambiar la configuración de requirepass de la sección Security como se muestra a continuación. Esto implica, que para poder ejecutar comandos en Redis deberemos autenticarnos con el comando AUTH especificando la contraseña, o bien especificar la contraseña al conectarnos (en el caso de redis-cli mediante un parámetro – no recomendado al ser en texto claro – o una variable de entorno).
requirepass misecretpwd
A continuación se muestran un par de ejemplos de como conectar con Redis con autenticación, tanto usando el comando AUTH como especificando las password como parámetro de redis-cli.
maxclients y maxmemory
Por defecto, Redis soporta hasta un máximo de 10000 clientes, pero podemos aumentar el número máximo de conexiones con la configuración maxclients de la sección Clients, como se muestra en el siguiente ejemplo.
maxclients 50000
Por defecto, Redis no tiene configurado un límite de memoria, por lo que intentará consumir toda la memoria disponible si lo llegara a necesitar. Es importante limitar la memoria que puede usar Redis, para garantizar que dejamos memoria disponible suficiente, tanto para el propio sistema operativo como para otros procesos que corran en la misma máquina. Para ello deberemos utilizar la configuración maxmemory de la sección Memory Management.
maxmemory 4294967296
tcp-backlog
Por defecto, Redis viene con un valor de 511 para la configuración de la opción tcp-backlog de la sección Network, que deberemos aumentar si nuestro servidor Redis tiene que soportar un alto número de conexiones por segundo, para evitar problemas de lentitud en la conexión de los clientes. Un uso eficiente de las conexiones, por ejemplo mediante la utilización de Connection Pooling,
tcp-backlog 65000
Hay que tener en cuenta que al aumentar el valor de tcp-backlog, hay que asegurarse que el valor de somaxconn y tcp_max_syn_backlog a nivel de Sistema Operativo están alineados (aumentarlos si es necesario), ya que sino, aunque intentemos aumentar el valor de tcp-backlog en Redis, no valdrá para nada (se verá truncado).
cat /proc/sys/net/core/somaxconn
sudo sysctl -w net.core.somaxconn=65000
cat /proc/sys/net/core/somaxconn
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=65000
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
A continuación se muestra la salida de ejecución de los anteriores comandos.
Sin embargo, con los comandos anteriores, estos cambios no serán persistentes, por lo que deberemos añadir estas configuraciones al fichero /etc/sysctl.conf, sino queremos perder sus efectos en el próximo reinicio de nuestro servidor. Una vez hecho esto, podremos reinicar el servidor, y comprobar que estos cambios persisten.
tail /etc/sysctl.conf -n4
cat /proc/sys/net/core/somaxconn
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
A continuación se muestra la salida de ejecución de los anteriores comandos.
Opción transparent_hugepage (THP) del Kernel
Bajo altas cargas de trabajo puede haber problemas de rendimiento relacionados con la alocación de memoria, que se pueden aliviar al deshabilitar la opción transparent_hugepage (THP – Transparent Huge Pages) del kernel a nivel del Sistema Operativo, que podemos hacer con el siguiente comando.
cat /sys/kernel/mm/transparent_hugepage/enabled
sudo bash -c "echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled"
cat /sys/kernel/mm/transparent_hugepage/enabled
A continuación se muestra la salida de ejecución de los anteriores comandos.
Sin embargo, esta configuración no es persistente frente a reinicios, por lo tanto, en caso de reinicio se perderán estos cambios. Para solucionarlo, vamos a activar el servicio rc-local que ya viene en Ubuntu y que nos permitirá ejecutar comandos en el inicio del sistema operativo, y lo configuraremos para deshabilitar transparent_hugepage. Para ello, primero habilitaremos el servicio rc-local y editaremos su fichero de configuración (no debería de exitir).
sudo systemctl enable rc-local
sudo vi /etc/systemd/system/rc-local.service
Dejaremos el siguiente contenido en el fichero /etc/systemd/system/rc-local.service, y guardaremos los cambios.
[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99
[Install]
WantedBy=multi-user.target
Crearemos el fichero /etc/rc.local que contendrá el script que deseamos ejecutar cada vez que arranque nuestro servidor, le concederemos permisos de ejecución, y lo editaremos.
sudo touch /etc/rc.local
sudo chmod +x /etc/rc.local
sudo vi /etc/rc.local
Dejaremos el siguiente contenido en el fichero /etc/rc.local
#!/bin/sh -e
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
exit 0
Realizado esto, cada vez que arranquemos o reiniciemos nuestro servidor, se volverá a deshabilitar la opción transparent_hugepage, con lo que habremos conseguido que este cambio sea persistente, con la trampilla del servicio rc-local.
Opción nf_conntrack_max (conexiones simultáneas a nivel de sockets)
Es posible ajustar la configuración del número máximo de conexiones simultáneas a nivel de sockets, algo que en servidores con carga intensiva de red suele ser necesario ajustar. Podemos ver las conexiones actuales y la configuración máxima establecida actualmente, con los siguientes comandos.
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
A continuación se muestra la salida de ejecución de los anteriores comandos, en un entorno de pruebas (una VM con Ubuntu en Azure).
En este ejemplo, se pueden observar que estamos en valores muy bajos, pero en servidores de producción con alta carga de trabajo de red, no será así, y en momentos pico se podría alcanzar el máximo, lo que provocaría que acabásemos con mensajes del tipo «nf_conntrack: table full, dropping packet» en /var/log/syslog.
Para aumentar este valor, y hacerlo de forma persistente, deberemos recurrir al servicio rc-local, para usarlo como trampilla para aplicar el comando correspondiente tras cada reinicio, como hicimos con la configuración de transparent_hugepage (THP), ya que no vale con configurarlo en /etc/sysctl.conf (en las pruebas que he hecho, no me han funcionado…. rc-local o nada). Para conseguirlo, dejaremos el siguiente contenido en el fichero /etc/rc.local
(marco en negrita la línea a añadir).
#!/bin/sh -e
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
sysctl -w net.netfilter.nf_conntrack_max=100000000
exit 0
Tras esto, reiniciaremos el servidor, tras lo cual, podremos comprobar que el nuevo valor ha quedado establecido, y además con la tranquilidad de que es persistente ante reinicios.
Opción overcommit_memory
Otra opción que puede ser importante es establecer overcommit_memory a 1. Esta configuración permite tres posibles valores, y la recomendación de Redis es esta: Background saving fails with a fork() error on Linux
- 0: heuristic overcommit (this is the default)
- 1: always overcommit, never check
- 2: always check, never overcommit
Podemos comprobar su valor actual con el siguiente comando.
cat /proc/sys/vm/overcommit_memory
Para aumentar este valor, y hacerlo de forma persistente, deberemos recurrir al servicio rc-local, para usarlo como trampilla para aplicar el comando correspondiente tras cada reinicio, como hicimos con la configuración de transparent_hugepage (THP) y de nf_conntrack_max. Para conseguirlo, dejaremos el siguiente contenido en el fichero /etc/rc.local
(marco en negrita la línea a añadir).
#!/bin/sh -e
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
sysctl -w net.netfilter.nf_conntrack_max=100000000
sysctl vm.overcommit_memory=1
exit 0
Tras esto, reiniciaremos el servidor, tras lo cual, podremos comprobar que el nuevo valor ha quedado establecido, y además con la tranquilidad de que es persistente ante reinicios.
Otras opciones del Kernel a revisar
Otras opciones a revisar del Kernel del Sistema Operativo, además de las que ya hemos visto hasta ahora, serían las siguientes. En principio en Ubuntu estarían ya todas establecidas correctamente, excepto la primera y las dos últimas (las marco en negrita).
vm.swappiness=0 # turn off swapping
net.ipv4.tcp_sack=1 # enable selective acknowledgements
net.ipv4.tcp_timestamps=1 # needed for selective acknowledgements
net.ipv4.tcp_window_scaling=1 # scale the network window
net.ipv4.tcp_congestion_control=cubic # better congestion algorithm
net.ipv4.tcp_syncookies=1 # enable syn cookies
net.ipv4.tcp_tw_recycle=1 # recycle sockets quickly
net.core.rmem_max=NUMBER # receive buffer size (by default 212992, increase to 524288 or higher)
net.core.wmem_max=NUMBER # buffer size for all connections (by default 212992, increase to 524288 or higher)
Por ejemplo, para cambiar el valor de vm.swappiness y que sea persistente frente a reinicios, modificaremos el fichero /etc/sysctl.conf tal y como se muestra en la siguiente imagen.
Para cambiar el valor de net.core.rmem_max y net.core.wmem_max, también modificaremos el fichero /etc/sysctl.conf, tal y como se muestra en la siguiente imagen.
Límites máximos de ficheros abiertos y procesos
Por defecto, Ubuntu (al igual que el resto de distros) tiene una serie de límites para ciertas acciones, que podemos ver con el comando ulimit.
ulimit -a
A continuación se muestra la ejecución del comando anterior, y los límites que tenemos en nuestro caso de ejemplo, que se trata de un Ubuntu 20 arrancado en Azure.
Es recomendable revisar y aumentar si fuera necesario el límite de ficheros abiertos (open files) y de procesos (max user processes). Al tratarse de un servicio systemd, no deberíamos cambiar esta configuración de límites en /etc/security/limits.conf (esto sólo afecta a los usuarios logados con PAM, no a los servicios que han sido iniciados por otros métodos. En su lugar, deberemos especificar la configuración que deseamos en el fichero del servicio systemd, existiendo la siguiente tabla de equivalencia, donde marco en negrita las dos opciones que nos interesan.
Directive ulimit equivalent Unit
==================================================================
LimitCPU= ulimit -t Seconds
LimitFSIZE= ulimit -f Bytes
LimitDATA= ulimit -d Bytes
LimitSTACK= ulimit -s Bytes
LimitCORE= ulimit -c Bytes
LimitRSS= ulimit -m Bytes
LimitNOFILE= ulimit -n Number of File Descriptors
LimitAS= ulimit -v Bytes
LimitNPROC= ulimit -u Number of Processes
LimitMEMLOCK= ulimit -l Bytes
LimitLOCKS= ulimit -x Number of Locks
LimitSIGPENDING= ulimit -i Number of Queued Signals
LimitMSGQUEUE= ulimit -q Bytes
LimitNICE= ulimit -e Nice Level
LimitRTPRIO= ulimit -r Realtime Priority
LimitRTTIME= No equivalent
Podemos revisar, y establecer estos límites si fuera necesario, editando el fichero del servicio, en nuestro caso el fichero /lib/systemd/system/redis-server.service
tal y como se muestra en la siguiente imagen. En nuestro caso de ejemplo, la opción LimitNOFILE ya estaba establecida a 65535, por lo que sólo tuvimos que añadir la opción LimitNPROC.
Para que este cambio tome efecto, deberemos editar el ficher, actualizar la configuración de systemd, y reiniciar el servicio, que básicamente se resume en los siguientes comandos.
sudo vi /lib/systemd/system/redis-server.service
sudo systemctl daemon-reload
sudo systemctl restart redis-server
sudo systemctl status redis-server
Realizado este cambio, podemos comprobar si ha sido aplicado con éxito, comprobado cuál es el proceso de nuestro servidor Redis, para seguidamente comprobar cuáles son los límites que tiene, como se muestra en la siguiente imagen.
Afinidad de CPU
En servidores con muchas CPUs es posible configurar la Afinidad de CPU, de tal modo que podamos establer qué CPUs se encargarán de tareas como la gestión del tráfico de Red, y cuáles del servicio de Redis, y evitar situaciones en las que podamos tener CPUs sin actividad y otras sufriendo una guerra de recursos entre los procesos.
Por ejemplo, podemos habilitar RPS (Receive Packet Steering) sobre las interfaces de red que tengamos para configurar su Afinidad de CPU. En el siguiente ejemplo, se asignan las CPUs 0-1 para la interfaz eth1.
echo '3' > /sys/class/net/eth1/queues/rx-0/rps_cpus
De igual modo, podríamos establecer la Afinidad de Redis, en este caso, de una forma diferente, como se muestra en el siguiente ejemplo, donde se asignan las CPUs 2-7 a Redis.
taskset -pc 2-7 `cat /var/run/redis.pid`
Aplicar cambios en la configuración de Redis
Para aplicar los cambios que hemos realizado en el fichero de configuración (redis.conf), guardaremos los cambios en el fichero de configuración de Redis (si no lo hemos hecho antes), y reiniciaremos el servicio.
sudo systemctl restart redis-server
sudo systemctl status redis-server
Es posible también acceder a la configuración desde el propio Redis con el comando CONFIG, tanto para mostrar la configuración actual (CONFIG GET) como para cambiar la configuración al vuelo en memoria (CONFIG SET) e incluso actualizar la configuración actual en memoria sobre el fichero de configuración (CONFIG REWRITE), en el caso de que arrancásemos Redis con un fichero de configuración y no queramos perder los cambios. A continuación se muestra algún ejemplo:
config get *
config set tcp-keepalive 301
config rewrite
Despedida y Cierre
Hasta aquí llega este Post, en el que hemos hecho una introducción a la instalación de Redis (en modo standalone, es decir, sin clustering, ni réplicas, ni sentinel….), viendo que tenemos varias formas de instalarlo (compilando desde los fuente, instalando desde los repos de Ubuntu, o instalando desde los repos de Redis), así como hemos visto varias configuraciones adicionales que suele ser habitual tocar, tanto a nivel de Redis como de Sistema Operativo. Hay muchas más opciones que podemos tocar, pero para no abrumar, con estas para empezar está bastante bien para montar un servicio Redis para un entorno real de Producción.
Poco más por hoy. Como siempre, confío que la lectura resulte de interés.