BLOG | NGINX

Escalonando MySQL com balanceamento de carga TCP e Galera Cluster

NGINX-Parte-de-F5-horiz-preto-tipo-RGB
Miniatura de Liam Crilly
Liam Crilly
Publicado em 26 de julho de 2021

[ Editor – Originalmente publicado em 2016, este post foi atualizado para usar os recursos do NGINX que foram revisados desde então. Para obter detalhes, consulte Registro avançado com o módulo JavaScript NGINX e o painel NGINX Plus . ]

Introduzimos o balanceamento de carga TCP no NGINX Plus R5 e adicionamos continuamente recursos em versões subsequentes, bem como suporte para balanceamento de carga UDP . Neste artigo, exploramos os principais requisitos para balanceamento de carga TCP e como o NGINX Plus os aborda.

Para explorar os recursos do NGINX Plus, usaremos um ambiente de teste simples que representa os principais componentes de um aplicativo com um backend de banco de dados escalonado. Para obter instruções completas sobre como criar o ambiente de teste, consulte o Apêndice .

O ambiente de teste para balanceamento de carga de servidores MySQL coloca o NGINX Plus entre os clientes MySQL e o cluster Galera
O ambiente de teste para balanceamento de carga de nós MySQL

Neste ambiente, o NGINX Plus atua como um proxy reverso para o servidor de banco de dados, escutando na porta padrão do MySQL, 3306. Isso fornece uma interface simples para o cliente, enquanto os nós MySQL de backend podem ser dimensionados (e até mesmo colocados offline) sem afetar o cliente de forma alguma. Usamos a ferramenta de linha de comando MySQL como cliente, que representa o aplicativo frontend no ambiente de teste.

Muitos dos recursos descritos neste artigo se aplicam ao NGINX Open Source e ao NGINX Plus. Para resumir, faremos referência ao NGINX Plus ao longo do texto e destacaremos explicitamente os recursos que não estão disponíveis no NGINX Open Source.

Exploraremos os seguintes casos de uso:

Balanceamento de carga TCP

Antes de configurar o balanceamento de carga para qualquer aplicativo, é importante entender como o aplicativo se conecta ao banco de dados. Para a maioria dos nossos testes, usamos a ferramenta de linha de comando MySQL, mysql(1) , para conectar ao cluster Galera, executar uma consulta e, em seguida, fechar a conexão. No entanto, muitas estruturas de aplicativos usam um pool de conexões para minimizar a latência e fazer uso eficiente dos recursos do servidor de banco de dados.

O balanceamento de carga TCP é configurado no contexto de configuração de fluxo , então criamos nossa configuração base de balanceamento de carga do MySQL adicionando um bloco de fluxo ao arquivo nginx.conf principal.

 

Isso separa nossa configuração de balanceamento de carga TCP do arquivo de configuração principal. Em seguida, criamos stream.conf no mesmo diretório que nginx.conf . Observe que, por padrão, o diretório conf.d é reservado para o contexto de configuração http e, portanto, adicionar arquivos de configuração de fluxo a esse diretório não funciona.

 

Primeiro, definimos um grupo upstream chamado galera_cluster , contendo os três nós MySQL em nosso cluster Galera. Em nosso ambiente de teste, cada um deles é acessível no host local com um número de porta exclusivo. A diretiva de zona define uma quantidade de memória que é compartilhada entre todos os processos de trabalho do NGINX Plus para manter o estado de balanceamento de carga. O bloco server{} configura como o NGINX Plus lida com clientes. O NGINX Plus escuta na porta padrão do MySQL, 3306, e encaminha todo o tráfego para o cluster Galera definido no bloco upstream .

Para testar se essa configuração básica está funcionando, podemos usar o cliente MySQL para retornar o nome do host do nó no cluster Galera ao qual nos conectamos.

$ echo "MOSTRAR VARIÁVEIS ONDE Nome_da_variável = 'nome_do_host'" | mysql --protocol=tcp --user=nginx --password=plus -N 2> /dev/null nome_do_host node1

Para verificar se o balanceamento de carga está funcionando, podemos repetir o mesmo comando.

$ !!;!!!;!! nome do host nó2 nome do host nó3 nome do host nó1

Isso mostra que o algoritmo de balanceamento de carga round-robin padrão está operando corretamente. Entretanto, se nosso aplicativo usar um pool de conexões para acessar o banco de dados (conforme sugerido acima), abrir conexões com o cluster em um sistema round-robin provavelmente levará a um número desequilibrado de conexões em cada nó. Além disso, não podemos igualar uma conexão a uma determinada carga de trabalho, pois as conexões podem estar ociosas (aguardando uma consulta do aplicativo) ou ocupadas processando uma consulta. Um algoritmo de balanceamento de carga mais apropriado para conexões TCP de longa duração é o Least Connections , configurado com a diretiva least_conn :

 

Agora, quando um cliente abre uma nova conexão com o banco de dados, o NGINX Plus escolhe o nó do cluster com o menor número de conexões atuais.

Alta disponibilidade e verificações de saúde

A grande vantagem de compartilhar a carga de trabalho do banco de dados em um cluster é que isso também fornece alta disponibilidade . Com a configuração discutida acima, o NGINX Plus marca um servidor como “inativo” e para de enviar pacotes TCP para ele se uma nova conexão TCP não puder ser estabelecida .

Além de lidar com servidores inativos dessa maneira, o NGINX Plus também pode ser configurado para executar verificações de integridade automáticas e proativas para que servidores indisponíveis sejam detectados antes que solicitações de clientes sejam enviadas a eles (esse é um recurso exclusivo do NGINX Plus). Além disso, a disponibilidade dos servidores pode ser testada com uma verificação de integridade no nível do aplicativo, o que significa que podemos enviar uma solicitação a cada servidor e verificar se obtemos uma resposta que indique boa integridade. Isso estende nossa configuração da seguinte forma.

 

Neste exemplo, o bloco match define os dados de solicitação e resposta necessários para iniciar um handshake do protocolo MySQL versão 10 . A diretiva health_check no bloco server{} aplica esse padrão e garante que o NGINX Plus encaminhe conexões MySQL somente para servidores que são realmente capazes de aceitar novas conexões. Nesse caso, realizamos a verificação de integridade a cada 20 segundos, excluímos um servidor do pool de balanceamento de carga TCP após uma única falha e retomamos o balanceamento de carga para ele após duas verificações de integridade bem-sucedidas consecutivas.

Registro e Diagnóstico

O NGINX Plus fornece registro flexível para que todo o seu processamento TCP/UDP possa ser registrado para depuração ou análise offline. Para protocolos TCP como MySQL, o NGINX Plus grava uma entrada de log quando a conexão é fechada. A diretiva log_format define quais valores aparecem nos logs. Podemos escolher qualquer uma das variáveis disponíveis para os módulos Stream. Definimos o formato do log no contexto do fluxo , no topo do nosso arquivo stream.conf .

 

O registro é habilitado adicionando a diretiva access_log no bloco server{} , especificando o caminho para o arquivo de registro e o nome do formato de registro definido no snippet anterior.

 

Isso produz entradas de log como o exemplo abaixo.

$ 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 avançado com o módulo JavaScript NGINX

NGINX JavaScript é a linguagem de configuração programática “nativa do NGINX”. É uma implementação JavaScript exclusiva para NGINX e NGINX Plus, projetada especificamente para casos de uso do lado do servidor e processamento por solicitação.

[ Editor – O caso de uso a seguir é apenas um entre muitos para o módulo NGINX JavaScript. Para obter a lista de todos os casos de uso, consulte Casos de uso para o módulo JavaScript NGINX .

Esta postagem foi atualizada para usar o objeto de sessão(ões) refatorado( s ) para o módulo Stream introduzido no NGINX JavaScript 0.2.4 e a diretiva js_import introduzida no NGINX JavaScript 0.4.0. ]

No módulo Stream para balanceamento de carga TCP/UDP, o NGINX JavaScript fornece acesso ao conteúdo dos pacotes de solicitação e resposta. Isso significa que podemos examinar a solicitação do cliente correspondente à consulta SQL e extrair elementos úteis, como o método SQL, por exemplo SELECT ou UPDATE . O NGINX JavaScript pode então disponibilizar esses valores como variáveis NGINX regulares. Neste exemplo, colocamos nosso código JavaScript em /etc/nginx/sql_method.js .

 

A função getSqlMethod() recebe um objeto JavaScript ( s ) que representa o pacote atual. Propriedades deste objeto, como fromUpstream e buffer, nos fornecem as informações necessárias sobre o pacote e seu contexto.

Primeiro verificamos se o pacote TCP está vindo do cliente, pois não precisamos examinar pacotes vindos do servidor MySQL upstream. Aqui, estamos interessados no terceiro pacote do cliente, pois os dois primeiros pacotes contêm informações de handshake e autenticação. O terceiro pacote do cliente contém a consulta SQL. O início desta string é então comparado com um dos métodos SQL definidos na matriz de métodos . Quando encontramos uma correspondência, armazenamos o resultado na variável global $method e gravamos uma entrada no log de erros. O registro JavaScript do NGINX é gravado no log de erros nas informações de gravidade e, portanto, não aparece por padrão.

A função setSqlMethod() é chamada quando uma variável NGINX com o mesmo nome é avaliada. Quando isso acontece, a variável é preenchida pela variável global $method do NGINX JavaScript, que foi obtida de chamadas para a função getSqlMethod() .

Observe que este código JavaScript NGINX foi projetado para o cliente de linha de comando MySQL, onde uma única consulta é executada. Ele não captura com precisão consultas complexas ou múltiplas consultas em uma conexão de longa duração, embora o código possa ser adaptado para esses casos de uso. Para obter instruções sobre como instalar e habilitar o módulo JavaScript do NGINX, consulte o Guia de administração do NGINX Plus .

Para incluir o método SQL em nossos logs, incluímos a variável $sql_method na diretiva log_format .

 

Também precisamos estender nossa configuração para informar ao NGINX Plus como e quando executar o código JavaScript do NGINX.

 

Primeiro, especificamos o local do código JavaScript do NGINX com a diretiva js_import e usamos a diretiva js_set para dizer ao NGINX Plus para chamar a função setSqlMethod() quando precisar avaliar a variável $sql_method . Então, dentro do bloco server{} usamos a diretiva js_filter para especificar a função que será chamada cada vez que um pacote for processado. Opcionalmente, podemos adicionar a diretiva error_log com a opção info para habilitar o registro em log do NGINX JavaScript.

Com essa configuração adicional, nosso log de acesso agora se parece com isso.

$ 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 ATUALIZAÇÃO 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 SELECIONAR 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 ATUALIZAÇÃO

Painel NGINX Plus

[ Editor – Esta seção foi atualizada para se referir à API NGINX Plus , que substitui e desaprova o módulo Status estendido separado originalmente discutido aqui. ]

Além de registrar a atividade do MySQL em detalhes, podemos observar métricas em tempo real e a integridade dos nossos servidores MySQL upstream no painel de monitoramento de atividades ao vivo do NGINX Plus (o NGINX Open Source fornece um conjunto menor de métricas e somente por meio da API Stub Status ).

O painel do NGINX Plus foi introduzido no NGINX Plus R7 e fornece uma interface web para a API do NGINX Plus . Habilitamos isso adicionando um novo bloco server{} no contexto http em um arquivo /etc/nginx/conf.d/dashboard.conf separado:

 

Também precisamos atualizar o bloco server{} em stream.conf com a diretiva status_zone para permitir que dados de monitoramento sejam coletados para nosso serviço MySQL.

 

Com essa configuração, o painel do NGINX Plus fica disponível na porta 8080. Na captura de tela a seguir, podemos ver nossos três servidores MySQL, cada um mostrando os detalhes de diversas conexões em andamento e o status de integridade atual. Podemos ver que o nó escutando na porta 33062 teve anteriormente uma breve interrupção de 18,97 segundos (relatada na coluna DT ).

O painel de monitoramento de atividade ao vivo do NGINX Plus permite que você acompanhe a integridade do servidor ao balancear a carga dos nós MySQL
O painel de monitoramento de atividade ao vivo do NGINX Plus permite que você acompanhe a integridade dos seus servidores MySQL

Considerações para gravações simultâneas

O Galera Cluster apresenta cada nó do servidor MySQL como um banco de dados mestre que executa leituras e gravações. Para muitas aplicações, a proporção de leituras para gravações é tão grande que o risco da mesma linha da tabela ser atualizada por vários clientes ao mesmo tempo é totalmente aceitável quando comparado à flexibilidade proporcionada por um cluster de banco de dados multimestre. Em situações em que há um risco maior de ocorrência de gravações simultâneas, temos duas opções.

  1. Crie dois grupos upstream separados, um para leituras e outro para gravações, cada um escutando em uma porta diferente. Dedique um ou mais nós no cluster para gravações, com todos os nós incluídos no grupo de leituras. O código do cliente deve ser atualizado para selecionar a porta apropriada para operações de leitura e gravação. Essa abordagem é discutida em Balanceamento de carga avançado do MySQL com NGINX Plus em nosso blog e favorece um ambiente altamente escalonado com muitos nós de servidor MySQL.
  2. Mantenha um único grupo upstream e modifique o código do cliente para detectar erros de gravação. Quando um erro de gravação é detectado, o código recua exponencialmente antes de tentar novamente, após o término da simultaneidade. Essa abordagem é discutida em Alta disponibilidade do MySQL com NGINX Plus e Galera Cluster em nosso blog e favorece um cluster pequeno, onde dedicar nós de cluster para gravações comprometeria a alta disponibilidade.

Resumo

Neste artigo, exploramos vários aspectos essenciais do balanceamento de carga de um aplicativo TCP (ou UDP), como o MySQL. O NGINX Plus fornece um balanceador de carga TCP/UDP completo para ajudar você a entregar aplicativos com desempenho, confiabilidade, segurança e escala, independentemente do tipo de tráfego.

Para experimentar o NGINX Plus, comece hoje mesmo seu teste gratuito de 30 dias ou entre em contato conosco para discutir seus casos de uso.


Apêndice: Criando o ambiente de teste

O ambiente de teste é instalado em uma máquina virtual para que seja isolado e repetível. Entretanto, não há razão para que ele não possa ser instalado em um servidor físico, “bare metal”.

Instalando o NGINX Plus

Consulte o Guia de administração do NGINX Plus .

Instalando Galera Cluster para MySQL

Neste exemplo, instalamos o Galera Cluster em um único host usando contêineres Docker para cada nó. As instruções a seguir foram adaptadas de Introdução ao Galera com Docker e pressupõem que o Docker Engine e a ferramenta de linha de comando MySQL já estejam instalados.

  1. Crie um arquivo de configuração básica do MySQL ( my.cnf ) a ser copiado para cada contêiner Galera pela imagem do Docker.

     

  2. Puxe a imagem básica do Docker do Galera.

    $ sudo docker pull erkules/galera:basic
    
  3. Crie o primeiro nó Galera ( node1 ), expondo a porta padrão do MySQL como 33061.

    $ sudo docker run -p 33061:3306 --detach=true --name node1 -h node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://
    
  4. Crie o segundo nó Galera ( node2 ). A porta MySQL é exposta como 33062 e vinculada ao node1 para comunicação entre clusters.

    $ sudo docker run -p 33062:3306 --detach=true --name node2 -h node2 --link node1:node1 erkules/galera:basic --wsrep-cluster-name=local-test --wsrep-cluster-address=gcomm://node1
    
  5. Crie o terceiro e último nó Galera ( node3 ) da mesma forma que o node2 . A porta do MySQL é exposta 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. Crie uma conta de usuário chamada nginx que pode ser usada para acesso remoto ao cluster a partir do host. Isso é feito executando o comando mysql(1) de dentro do próprio contêiner Docker.

    $ sudo docker exec -ti node1 mysql -e"CONCEDER TODOS OS PRIVILÉGIOS EM *.* PARA 'nginx'@'172.17.0.1' IDENTIFICADO POR 'plus'"
    
  7. Verifique se você consegue se conectar ao cluster Galera a partir do host, usando o protocolo TCP.

    $ mysql --protocol=tcp -P 33061 --user=nginx --password=plus -e "SHOW DATABASES" mysql: [Aviso] Usar uma senha na interface de linha de comando pode ser inseguro. +--------------------+ | Banco de dados | +--------------------+ | information_schema | | mysql | | performance_schema | +--------------------+
    
  8. Por fim, execute o mesmo comando em outro nó do cluster para mostrar que a conta de usuário nginx foi replicada e o cluster está funcionando corretamente.

    $ mysql --protocol=tcp -P 33062 --user=nginx --password=plus -e "SHOW DATABASES" mysql: [Aviso] Usar uma senha na interface de linha de comando pode ser inseguro. +--------------------+ | Banco de dados | +--------------------+ | information_schema | | mysql | | performance_schema | +--------------------+
    

"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."