BLOG | NGINX

Escalado de MySQL con equilibrio de carga TCP y clúster Galera

NGINX - Parte de F5 - horizontal, negro, tipo RGB
Miniatura de Liam Crilly
Liam Crilly
Publicado el 26 de julio de 2021

[ Editor : Publicada originalmente en 2016, esta publicación se ha actualizado para utilizar las características de NGINX que se han revisado desde entonces. Para obtener más detalles, consulte Registro avanzado con el módulo JavaScript de NGINX y el panel de control de NGINX Plus . ]

Introdujimos el equilibrio de carga TCP en NGINX Plus R5 y continuamente agregamos características en versiones posteriores, así como también soporte para el equilibrio de carga UDP . En este artículo exploramos los requisitos clave para el equilibrio de carga TCP y cómo NGINX Plus los aborda.

Para explorar las características de NGINX Plus, utilizaremos un entorno de prueba simple que representa los componentes clave de una aplicação con un backend de base de datos escalable. Para obtener instrucciones completas sobre cómo crear el entorno de prueba, consulte el Apéndice .

El entorno de prueba para equilibrar la carga de los servidores MySQL coloca NGINX Plus entre los clientes MySQL y el clúster Galera
El entorno de prueba para equilibrar la carga de los nodos MySQL

En este entorno, NGINX Plus actúa como un proxy inverso para el servidor de base de datos, escuchando en el puerto MySQL predeterminado de 3306. Esto proporciona una interfaz sencilla para el cliente, mientras que los nodos MySQL backend se pueden escalar horizontalmente (e incluso desconectarse) sin afectar al cliente de ninguna manera. Utilizamos la herramienta de línea de comandos MySQL como cliente, que representa la aplicação frontend en el entorno de prueba.

Muchas de las características descritas en este artículo se aplican tanto a NGINX Open Source como a NGINX Plus. Para abreviar, nos referiremos a NGINX Plus en todo momento y mencionaremos explícitamente las características que no están disponibles en NGINX Open Source.

Exploraremos los siguientes casos de uso:

Equilibrio de carga TCP

Antes de configurar el equilibrio de carga para cualquier aplicação , es importante comprender cómo se conecta la aplicação a la base de datos. Para la mayoría de nuestras pruebas, utilizamos la herramienta de línea de comandos MySQL, mysql(1) , para conectarnos al clúster Galera, ejecutar una consulta y luego cerrar la conexión. Sin embargo, muchos marcos de aplicação utilizan un grupo de conexiones para minimizar la latencia y hacer un uso eficiente de los recursos del servidor de base de datos.

El equilibrio de carga TCP se configura en el contexto de configuración de transmisión , por lo que creamos nuestra configuración base de equilibrio de carga MySQL agregando un bloque de transmisión al archivo principal nginx.conf .

 

Esto separa nuestra configuración de equilibrio de carga TCP del archivo de configuración principal. Luego creamos stream.conf en el mismo directorio que nginx.conf . Tenga en cuenta que, de forma predeterminada, el directorio conf.d está reservado para el contexto de configuración http , por lo que agregar archivos de configuración de transmisión a ese directorio no funciona.

 

Primero definimos un grupo ascendente llamado galera_cluster , que contiene los tres nodos MySQL en nuestro clúster Galera. En nuestro entorno de prueba, cada uno de ellos es accesible en localhost con un número de puerto único. La directiva de zona define una cantidad de memoria que se comparte entre todos los procesos de trabajo de NGINX Plus para mantener el estado de equilibrio de carga. El bloque server{} configura cómo NGINX Plus trata con los clientes. NGINX Plus escucha en el puerto MySQL predeterminado, 3306, y reenvía todo el tráfico al clúster Galera definido en el bloque ascendente .

Para probar que esta configuración básica funciona, podemos usar el cliente MySQL para devolver el nombre de host del nodo en el clúster Galera al que nos conectamos.

$ echo "MOSTRAR VARIABLES DONDE Nombre_de_variable = 'nombre_de_host'" | mysql --protocol=tcp --user=nginx --password=plus -N 2> /dev/null nombre_de_host nodo1

Para comprobar que el equilibrio de carga funciona, podemos repetir el mismo comando.

$ !!;!!;!! nombre de host nodo2 nombre de host nodo3 nombre de host nodo1

Esto demuestra que el algoritmo de equilibrio de carga round-robin predeterminado está funcionando correctamente. Sin embargo, si nuestra aplicação utiliza un grupo de conexiones para acceder a la base de datos (como se sugirió anteriormente), entonces abrir conexiones al clúster en forma rotatoria probablemente conduzca a un número desequilibrado de conexiones en cada nodo. Además, no podemos equiparar una conexión con una carga de trabajo determinada, ya que las conexiones pueden estar inactivas (esperando una consulta de la aplicação) u ocupadas procesando una consulta. Un algoritmo de equilibrio de carga más apropiado para conexiones TCP de larga duración es Least Connections , configurado con la directiva least_conn :

 

Ahora, cuando un cliente abre una nueva conexión a la base de datos, NGINX Plus elige el nodo del clúster con la menor cantidad de conexiones actuales.

Alta disponibilidad y comprobaciones de estado

La gran ventaja de compartir la carga de trabajo de la base de datos en un clúster es que también proporciona alta disponibilidad . Con la configuración comentada anteriormente, NGINX Plus marca un servidor como “caído” y deja de enviarle paquetes TCP si no se puede establecer una nueva conexión TCP.

Además de gestionar servidores caídos de esta manera, NGINX Plus también puede configurarse para realizar controles de estado automáticos y proactivos para detectar servidores no disponibles antes de que se les envíen solicitudes de clientes (esta es una función exclusiva de NGINX Plus). Además, la disponibilidad de los servidores se puede probar con una comprobación del estado de la aplicação, lo que significa que podemos enviar una solicitud a cada servidor y comprobar que recibimos una respuesta que indica un buen estado. Esto extiende nuestra configuración de la siguiente manera.

 

En este ejemplo, el bloque de coincidencia define los datos de solicitud y respuesta necesarios para iniciar un protocolo de enlace MySQL versión 10 . La directiva health_check en el bloque server{} aplica este patrón y garantiza que NGINX Plus reenvíe conexiones MySQL solo a servidores que realmente sean capaces de aceptar nuevas conexiones. En este caso, realizamos la comprobación de estado cada 20 segundos, excluimos un servidor del grupo de equilibrio de carga TCP después de una sola falla y reanudamos el equilibrio de carga en él después de 2 comprobaciones de estado exitosas consecutivas.

Registro y diagnóstico

NGINX Plus proporciona un registro flexible para que todo su procesamiento TCP/UDP pueda registrarse para depuración o análisis fuera de línea. Para protocolos TCP como MySQL, NGINX Plus escribe una entrada de registro cuando se cierra la conexión. La directiva log_format define qué valores aparecen en los registros. Podremos elegir entre cualquiera de las variables disponibles para los módulos Stream. Definimos el formato del registro en el contexto del flujo , en la parte superior de nuestro archivo stream.conf .

 

El registro se habilita agregando la directiva access_log en el bloque server{} , especificando la ruta al archivo de registro y el nombre del formato de registro definido en el fragmento anterior.

 

Esto produce entradas de registro como el ejemplo a continuación.

$ tail -3 /var/log/nginx/galera_access.log 192.168.91.1 [23/jul/2021:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614 192.168.91.1 [23/jul/2021:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181 192.168.91.1 [23/jul/2021:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460

Registro avanzado con el módulo JavaScript de NGINX

NGINX JavaScript es el lenguaje de configuración programática “nativo de NGINX”. Es una implementación de JavaScript única para NGINX y NGINX Plus, diseñada específicamente para casos de uso del lado del servidor y procesamiento por solicitud.

[ Editor : el siguiente caso de uso es solo uno de muchos para el módulo JavaScript de NGINX. Para obtener la lista de todos los casos de uso, consulte Casos de uso para el módulo JavaScript de NGINX .

Esta publicación se ha actualizado para utilizar el objeto de sesión ( s ) refactorizado para el módulo Stream introducido en NGINX JavaScript 0.2.4 y la directiva js_import introducida en NGINX JavaScript 0.4.0 . ]

Dentro del módulo Stream para el equilibrio de carga TCP/UDP, NGINX JavaScript proporciona acceso al contenido de los paquetes de solicitud y respuesta. Esto significa que podemos examinar la solicitud del cliente correspondiente a la consulta SQL y extraer elementos útiles como el método SQL, por ejemplo SELECT o UPDATE . Luego, JavaScript de NGINX puede hacer que dichos valores estén disponibles como variables NGINX normales. En este ejemplo colocamos nuestro código JavaScript en /etc/nginx/sql_method.js .

 

A la función getSqlMethod() se le pasa un objeto ( s ) de JavaScript que representa el paquete actual. Las propiedades de este objeto, como fromUpstream y buffer, nos proporcionan la información que necesitamos sobre el paquete y su contexto.

Primero verificamos que el paquete TCP proviene del cliente, ya que no necesitamos examinar los paquetes que vienen del servidor MySQL ascendente. Aquí, nos interesa el tercer paquete de cliente, ya que los dos primeros paquetes contienen información de autenticación y protocolo de enlace. El tercer paquete de cliente contiene la consulta SQL. Luego, el comienzo de esta cadena se compara con uno de los métodos SQL definidos en la matriz de métodos . Cuando encontramos una coincidencia, almacenamos el resultado en la variable global $method y escribimos una entrada en el registro de errores. El registro de JavaScript de NGINX se escribe en el registro de errores en la información de gravedad y, por lo tanto, no aparece de forma predeterminada.

La función setSqlMethod() se llama cuando se evalúa una variable NGINX del mismo nombre. Cuando esto sucede, la variable se completa con la variable global $method de JavaScript NGINX que se obtuvo a partir de las llamadas a la función getSqlMethod() .

Tenga en cuenta que este código JavaScript NGINX está diseñado para el cliente de línea de comandos MySQL, donde se ejecuta una sola consulta. No captura con precisión consultas complejas ni consultas múltiples en una conexión de larga duración, aunque el código podría adaptarse para esos casos de uso. Para obtener instrucciones sobre cómo instalar y habilitar el módulo JavaScript de NGINX, consulte la Guía de administración de NGINX Plus .

Para incluir el método SQL en nuestros registros, incluimos la variable $sql_method en la directiva log_format .

 

También necesitamos ampliar nuestra configuración para indicarle a NGINX Plus cómo y cuándo ejecutar el código JavaScript de NGINX.

 

Primero especificamos la ubicación del código JavaScript de NGINX con la directiva js_import y usamos la directiva js_set para indicarle a NGINX Plus que llame a la función setSqlMethod() cuando necesite evaluar la variable $sql_method . Luego, dentro del bloque server{} usamos la directiva js_filter para especificar la función que se llamará cada vez que se procese un paquete. Opcionalmente podemos agregar la directiva error_log con la opción info para habilitar el registro de JavaScript de NGINX.

Con esta configuración adicional, nuestro registro de acceso ahora se ve así.

$ tail -3 /var/log/nginx/galera_access.log 192.168.91.1 [23/jul/2021:17:42:18 +0100] TCP 200 369 1611 127.0.0.1:33063 0.000 0.003 12.614 12.614 ACTUALIZACIÓN 192.168.91.1 [23/jul/2021:17:42:18 +0100] TCP 200 369 8337 127.0.0.1:33061 0.001 0.001 11.181 11.181 SELECCIONAR 192.168.91.1 [23/07/2021:17:42:19 +0100] TCP 200 369 1611 127.0.0.1:33062 0.001 0.001 10.460 10.460 ACTUALIZACIÓN

Panel de control de NGINX Plus

[ Editor : esta sección se ha actualizado para hacer referencia a la API NGINX Plus , que reemplaza y deja obsoleto el módulo de Estado extendido independiente que se analizó originalmente aquí. ]

Además de registrar la actividad de MySQL en detalle, podemos observar métricas en tiempo real y el estado de nuestros servidores MySQL ascendentes en el panel de monitoreo de actividad en vivo NGINX Plus (NGINX Open Source brinda un conjunto más pequeño de métricas y solo a través de la API Stub Status ).

El panel de control de NGINX Plus se introdujo en NGINX Plus R7 y proporciona una interfaz web para la API de NGINX Plus . Esto se habilita añadiendo un nuevo bloque server{} en el contexto http , en un archivo /etc/nginx/conf.d/dashboard.conf aparte:

 

También debemos actualizar el bloque server{} en stream.conf con la directiva status_zone para permitir que se recopilen datos de monitoreo para nuestro servicio MySQL.

 

Con esta configuración establecida, el panel de NGINX Plus está disponible en el puerto 8080. En la siguiente captura de pantalla podemos ver nuestros tres servidores MySQL, cada uno mostrando los detalles de numerosas conexiones en curso y el estado de salud actual. Podemos ver que el nodo que escucha en el puerto 33062 anteriormente tuvo una breve interrupción de 18,97 segundos (reportada en la columna DT ).

El panel de monitoreo de actividad en vivo de NGINX Plus le permite realizar un seguimiento del estado del servidor al equilibrar la carga de los nodos MySQL
El panel de control de actividad en vivo de NGINX Plus le permite monitorear el estado de sus servidores MySQL.

Consideraciones para escrituras concurrentes

Galera Cluster presenta cada nodo del servidor MySQL como una base de datos maestra que realiza lecturas y escrituras. Para muchas aplicações , la relación entre lecturas y escrituras es tan grande que el riesgo de que varios clientes actualicen la misma fila de la tabla al mismo tiempo es totalmente aceptable en comparación con la flexibilidad que ofrece un clúster de base de datos multimaestro. En situaciones donde existe un mayor riesgo de que se produzcan escrituras simultáneas, tenemos dos opciones.

  1. Cree dos grupos ascendentes separados, uno para lecturas y otro para escrituras, cada uno escuchando en un puerto diferente. Dedique uno o más nodos del clúster a las escrituras, con todos los nodos incluidos en el grupo de lecturas. El código del cliente debe actualizarse para seleccionar el puerto apropiado para las operaciones de lectura y escritura. Este enfoque se analiza en Equilibrio de carga avanzado de MySQL con NGINX Plus en nuestro blog y favorece un entorno altamente escalable con muchos nodos de servidor MySQL.
  2. Mantenga un único grupo ascendente y modifique el código del cliente para detectar errores de escritura. Cuando se detecta un error de escritura, el código retrocede exponencialmente antes de volver a intentarlo, una vez finalizada la concurrencia. Este enfoque se analiza en MySQL High Availability con NGINX Plus y Galera Cluster en nuestro blog y favorece un clúster pequeño, donde dedicar nodos del clúster a escrituras comprometería la alta disponibilidad.

resumen

En este artículo hemos explorado varios de los aspectos esenciales del equilibrio de carga de una aplicação TCP (o UDP) como MySQL. NGINX Plus proporciona un balanceador de carga TCP/UDP con todas las funciones para ayudarlo a entregar aplicações con rendimiento, confiabilidad, seguridad y escala, independientemente del tipo de tráfico.

Para probar NGINX Plus, comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso.


Apéndice: Creación del entorno de prueba

El entorno de prueba se instala en una máquina virtual para que esté aislado y sea repetible. Sin embargo, no hay ninguna razón por la cual no se pueda instalar en un servidor físico.

Instalación de NGINX Plus

Consulte la Guía de administración de NGINX Plus .

Instalación de Galera Cluster para MySQL

En este ejemplo instalamos Galera Cluster en un solo host usando contenedores Docker para cada nodo. Las siguientes instrucciones están adaptadas de Primeros pasos con Galera con Docker y suponen que tanto Docker Engine como la herramienta de línea de comandos MySQL ya están instalados.

  1. Cree un archivo de configuración básica de MySQL ( my.cnf ) que la imagen de Docker copiará en cada contenedor de Galera.

     

  2. Extraiga la imagen básica de Docker de Galera.

    $ sudo docker pull erkules/galera:basic
    
  3. Cree el primer nodo Galera ( nodo1 ), exponiendo el puerto MySQL predeterminado como 33061.

    $ sudo docker run -p 33061:3306 --detach=true --name nodo1 -h nodo1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://
    
  4. Crea el segundo nodo Galera ( nodo2 ). El puerto MySQL está expuesto como 33062 y vinculado al nodo1 para la comunicación entre clústeres.

    $ sudo docker run -p 33062:3306 --detach=true --name nodo2 -h nodo2 --link nodo1:nodo1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://nodo1
    
  5. Crea el tercer y último nodo Galera ( nodo3 ) de la misma manera que el nodo2 . El puerto MySQL está expuesto como 33063.

    $ sudo docker run -p 33063:3306 --detach=true --name node3 -h node3 --link node1:node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://node1
    
  6. Cree una cuenta de usuario llamada nginx que pueda usarse para acceso remoto al clúster desde el host. Esto se realiza ejecutando el comando mysql(1) desde el propio contenedor Docker.

    $ sudo docker exec -ti node1 mysql -e"CONCEDER TODOS LOS PRIVILEGIOS EN *.* A 'nginx'@'172.17.0.1' IDENTIFICADO POR 'plus'"
    
  7. Verifique que pueda conectarse al clúster Galera desde el host, utilizando el protocolo TCP.

    $ mysql --protocol=tcp -P 33061 --user=nginx --password=plus -e "SHOW DATABASES" mysql: [Advertencia] El uso de una contraseña en la interfaz de línea de comandos puede ser inseguro. +--------------------+ | Base de datos | +--------------------+ | esquema_de_información | | mysql | | esquema_de_rendimiento | +--------------------+
    
  8. Por último, ejecute el mismo comando contra otro de los nodos del clúster para demostrar que la cuenta de usuario nginx se ha replicado y que el clúster está funcionando correctamente.

    mysql --protocol=tcp -P 33062 --user=nginx --password=plus -e "SHOW DATABASES" mysql: [Advertencia] El uso de una contraseña en la interfaz de línea de comandos puede ser inseguro. +--------------------+ | Base de datos | +--------------------+ | esquema_de_información | | mysql | | esquema_de_rendimiento | +--------------------+
    

"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.