Balanceo de carga en Redis con Nginx

Vuelvo a la carga con un post de Arquitectura para compartir cómo usar Nginx (tanto la versión Open Source como la versión de pago – Nginx Plus) para balancear la carga entre varios servidores Redis.

Introducción a Nginx y Redis

Nginx es un servidor proxy HTTP inverso, un servidor proxy de correo y un servidor proxy TCP / UDP genérico. Productos similares serían HAProxy y Apache.

Redis es un almacén en memoria de estructuras de datos open-source, utilizado como base de datos, caché y broker de mensajes.

Solución al balanceo de carga

El balanceo de carga se refiere a la distribución del tráfico de red entre múltiples servidores backend. Estamos más acostumbrados a utilizar el módulo HTTP de Nginx utilizándolo de proxy o balancear la carga de tráfico HTTP. Desde hace algunas versiones Nginx admite el balanceo de carga de tráfico tanto TCP como UDP.

Para cambiar la configuración de Nginx debemos modificar el fichero o ficheros de configuración, en el caso de que tengamos varios que luego incluiremos con claúsulas “include“. El fichero de configuración principal por defecto de Nginx generalmente se encuentra en /etc/nginx/nginx.conf:

stream {
 server {
 listen 12345;

 #TCP traffic will be proxied to the "stream_backend" upstream group
 proxy_pass stream_backend;
 }

 server {
 listen 12346;

 #TCP traffic will be proxied a proxied server
 proxy_pass backend.example.com:12346;
 }

 server {
 listen 53 udp;

 #UDP traffic will be proxied to the "dns_servers" upstream group
 proxy_pass dns_servers;
 }
 

 upstream stream_backend {
 server 10.0.18.4:18840;
 server 10.0.18.5:18840;
 server 10.0.18.6:18840;
 }
 
 upstream dns_servers {
 server 10.0.18.4:18840;
 server 10.0.18.5:18840;
 server 10.0.18.6:18840;
 }
}
  1. Crearemos un bloque “stream {}” de primer nivel
  2. Definiremos uno o más bloques “server {}” para configurar cada servidor virtual dentro del bloque stream.
  3. Dentro de cada bloque server definiremos mediante la directiva “listen” el puerto en el que el servidor (nginx) escucha tráfico (si es tráfico udp lo especificamos).
  4. Incluiremos además dentro de “server” la directiva “proxy-pass” para definir el servidor o el grupo de upstreams en los cuales se redigirá el tráfico.
  5. Definimos uno o más bloques “upstream {}” de configuración dentro del bloque “stream” estableciendo un nombre a los mismos.
  6. Añadimos a los upstreams una directiva “server” por cada uno de los servidores Redis junto con su dirección IP y su puerto

Conclusión

Hasta aquí la configuración más básica, podemos especificar además el método de balanceo de carga (round-robin, least_conn, least_time, hash) así como parámetros específicos tanto para el número de conexiones máximo, el peso de cada servidor, etc. Al igual que el tráfico de Redis esta solución es aplicable a todo tráfico de red a través del protocolo TCP/UDP. Tienes más información aquí: tcp-load-balancing

 

 

 

Descubrimiento de Microservicios con Nginx y Consul

En una entrada previa ya traté el enfoque de Microservicios y cómo dividir en servicios de grado más fino, independientes, que exponen un API y que colaboran juntos a través de peticiones HTTP. En esta veremos una solución con Nginx y Consul para el descubrimiento dinámico de microservicios.

Una vez que tenemos unos cuantos microservicios, incluso varias instancias de cada uno para balancear la carga, nuestra atención pasa inevitablemente por conocer dónde están desplegados cada uno de ellos, ya que los mismos pueden “convivir” en el mismo servidor, en diferentes servidores de nuestra infraestructura, o en servicios cloud como el de Amazon. Necesitamos conocer si están o no en ejecución además de su IP y puerto.

La solución pasa por un mecanismo de autoregistro de los microservicios en el momento del despliegue en un catálogo de servicios en “runtime”, una forma de decir: “Estoy vivo y me puedes encontrar en esta IP y este puerto”. En el momento del “undeploy” debería desregistrarse del mismo.

Consul

Consul es un sistema distribuido open source creado por Hashicorp para el registro de servicios y la configuración de los mismos. Provee un API que permite a los servicios el registro/desregistro en su catálogo. Consul provee igualmente de un chequeo constante de la salud de los servicios, marcando aquéllos que no pasan el mismo. El cluster de Consul se compone de diferentes procesos agente que se encuentran ejecutándose en los mismos servidores en los que se localizan los servicios, de manera que cada servicio se registra en el agente Consul de su servidor. Estos procesos agente pueden ejecutarse en modo cliente o en modo Servidor. Se recomienda ejecutar entre 3 y 5 agentes en modo Servidor.

consul-arch-5d4e3623

Cualquier registro/desregistro en un nodo del cluster se propaga por el mismo. Todos los nodos del cluster conocen el catálogo de servicios (sus nombres, direcciones ip y puertos).

Una vez que tenemos un catálogo “runtime” de servicios necesitamos un proxy inverso como Apache, HAProxy o Nginx que enrute las peticiones y balancee la carga entre las distintas instancias de un servicio. Todas las peticiones llegarán a uno o más proxies y éste será el encargado del enrutamiento. El problema a resolver en este escenario es llegar a hacer dinámica la configuración de Nginx, o del proxy que se desee de los expuestos anteriormente.

La configuración de servicios se realiza en un fichero, por lo que si la misma se modifica (modificando el fichero), Nginx debería recargarse y leer la nueva. Pero, ¿quién se encarga de reescribir la configuración de Nginx cuando el catálogo de Consul recibe un alta o una baja de un servicio?

Consul-Template

Consul-Template es un proceso que se ejecuta en uno de los nodos del cluster de Consul (mismo servidor que uno de los agentes Consul) y es capaz de escribir en un fichero del File System los cambios en el catálogo de servicios a través de una plantilla. Esta plantilla la transforma en un fichero de configuración en disco y cuando finaliza puede ejecutar un comando del sistema, forzando a nginx a recargar la configuración.

$ consul-template \
  -consul my.consul.internal:6124 \
  -template "/tmp/nginx.ctmpl:/var/nginx/nginx.conf:service nginx restart"

Con el comando de arriba se ejecuta el proceso que estará ejecutándose en segundo plano consultando al agente Consul que indicamos en el parámetro -consul. El parámetro -template indica que la plantilla para el rendering del fichero se encuentra en /tmp/nginx.ctmpl y sobrescribe la configuración de nginx en /var/nginx/nginx.conf. Una vez sobreescrito el fichero se ejecuta el comando que recarga la configuración de Nginx, service nginx restart, en el ejemplo de arriba.

All together

En  el siguiente esquema observamos todos los componentes unidos y colaborando tal y cómo se ha expuesto más arriba.

registroServicios

Arquitecturas de este tipo son fáciles de implementar y distribuir incluso si nos vamos a una infraestructura basada en contenedores docker. El sistema es escalable y de alta disponibilidad con la inclusión de varios nodos con Nginx y Consul-Template.