Desvendando o Nginx - Parte 3

No artigo anterior mostrei como montar um webserver com Nginx. Neste artigo vamos aprender a montar um web proxy reverso, e um proxy balanceador de carga.

Nginx como proxy reverso

Um servidor proxy reverso atua como um serviço intermediário entre o cliente e o servidor. Usando proxy reverso, podemos obter múltiplas vantagens como melhor desempenho, balanceamento de carga, melhoria da segurança e muito mais.


Cenário 1

Digamos que estamos executando dois servidores. São eles: server-01, e server-02. O server-01 está executando Debian com Nginx proxy reverso e um endereço IP que corresponde a 10.1.1.251. Por sua vez, o server-02 executa um site básico usando Apache2 no endereço IP 10.1.1.252. Vamos assumir, então, que o server-02 está instalado e funcionando para servir a um conteúdo na porta 80.

Nota: Neste cenário, nosso foco principal será dedicado ao server-01, bem como a configuração de proxy reverso que irá agir como um intermediário entre o cliente e o server-02.

Dentro do server-01 vamos criar um arquivo chamado proxy dentro da pasta /etc/nginx/sites-available e com o seguinte conteúdo:

server {  
        listen 80;
        location / {
             proxy_pass http://10.1.1.252;
        }
}

A principal característica acima é a diretiva proxy_pass que como expliquei no artigo anterior, instrui o nginx a ouvir a porta 80 no IP 10.1.1.251 (máquina que estamos configurando), ao socket remoto 10.1.1.252. Para se certificar que a configuração acima está ok, basta digita o comando a baixo:

nginx -t  

Nota: O parâmetro -t serve para testar e checar os arquivos de configuração do nginx sem a necessidade de restartar o serviço.

Em caso de sucesso, basta agora dar um reload no nginx com o seguinte comando:

systemctl reload nginx.service  

Usando o exemplo acima, com o seu ip local e direcionando o proxy_pass para um domínio ou ip qualquer, faça o teste. Por exemplo:

server {  
        listen 80;
        location / {
             proxy_pass http://rocket.ti.lemaf.ufla.br;
        }
}

Agora entre com IP local em seu navegador padrão e veja o resultado. Seu ip local irá redirecionar para o IP desejado na porta 80, no caso, o endereço do rocket do Lemaf.


Cenário 2

Proxy balanceador de carga

O balanceamento de carga é uma técnica comumente usada para otimizar a utilização de recursos, maximizando a produtividade, reduzindo a latência, e assegurando configurações tolerantes a falhas. O Nginx pode ser usado em diferentes cenários como um balanceador de carga HTTP muito eficiente.

Em primeiro lugar, você precisa definir o grupo com a diretiva upstream. A diretiva é colocada no contexto http. Grupo de servidores são configurados usando a diretiva server. A seguinte configuração define um grupo chamado server e consiste em três configurações de servidor (o que pode resolver em mais de três servidores reais):

http {  
    upstream backend {
        server backend1.example.com weight=5;
        server backend2.example.com;
        server 192.0.0.1 backup;
    }
}

Para passar solicitações para um grupo de servidores, o nome do grupo é especificado na diretiva proxy_pass (ou fastcgi_pass, memcached_pass, uwsgi_pass, scgi_pass dependendo do protocolo). No próximo exemplo, um servidor virtual em execução no NGINX passa todas as solicitações para o grupo de servidores backend definido no exemplo anterior:

server {  
    location / {
        proxy_pass http://backend;
    }
}

O exemplo a seguir resume os dois exemplos acima e mostra os pedidos de proxy para o grupo de servidores de backend, onde o grupo de servidores é composto por três servidores, dois deles executam duas instâncias do mesmo aplicativo enquanto um é servidor de backup. O Nginx aplica o balanceamento de carga HTTP para distribuir as solicitações:

http {  
    upstream backend {
        server backend1.example.com;
        server backend2.example.com;
        server 192.0.0.1 backup;
    }
    server {
        location / {
            proxy_pass http://backend;
        }
    }
}

Basicamente o Nginx suporta quatro métodos de balanceamento de carga. Além disso, há também um quinto método adicional. São eles:

  • round-robin: os pedidos são distribuídos uniformemente entre os servidores. Este método é usado por padrão portanto, não existe uma diretiva para ativar este método:
upstream backend {  
   server backend1.example.com;
   server backend2.example.com;
}
  • least_conn: neste método, a solicitação é enviada para o servidor com o menor número de conexões ativas:
upstream backend {  
    least_conn;

    server backend1.example.com;
    server backend2.example.com;
}
  • ip_hash: o servidor ao qual é enviado um pedido é determinado a partir do endereço IP do cliente. Neste caso, os três primeiros octetos do endereço IPv4 ou todo o endereço IPv6 são utilizados para calcular o valor do hash.
upstream backend {  
    ip_hash;

    server backend1.example.com;
    server backend2.example.com;
}

Se um dos servidores tem de ser temporariamente removido, pode ser marcado com o parâmetro down afim de preservar a corrente de hashing de endereços IP do cliente. Os pedidos que estavam a ser processados ​​por este servidor são enviados automaticamente para o próximo servidor no grupo:

upstream backend {  
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
}
  • generic hash: o servidor ao qual é enviado um pedido é determinado a partir de uma chave definida pelo utilizador que pode ser um texto, variável, ou a sua combinação. Por exemplo, a chave pode ser um IP de origem e a porta, ou URI:
upstream backend {  
    hash $request_uri consistent;

    server backend1.example.com;
    server backend2.example.com;
}

O parâmetro consistent é opcional e permite o balanceamento de carga consistente. As solicitações serão distribuídas uniformemente em todos os servidores upstream baseados no valor da chave hashed definido pelo usuário. Se um servidor upstream é adicionado ou removido de um grupo upstream, apenas poucas chaves vão ser remapeadas o que minimiza erros de cache em caso de balanceamento de servidores de cache e outras aplicações.

  • least_time (método Extra): para cada solicitação, o Nginx seleciona o servidor com a menor latência média e o menor número de conexões ativas, onde a menor latência média é calculada com base em qual dos seguintes parâmetros:

header - Tempo para receber o primeiro byte do servidor.

last_byte - Tempo para receber a resposta completa do servidor.

upstream backend {  
    least_time header;

    server backend1.example.com;
    server backend2.example.com;
}

Nota: Ao configurar qualquer método diferente do round-robin, é importante declarar a diretiva correspondente (least_conn, ip_hash, hash, least_time) acima da lista de diretivas de servidor no bloco upstream.


Extras (nginx plus)

O Ngnix Plus inclui recursos adicionais, como balanceamento de carga avançado e acesso a um amplo conjunto de métricas para monitoramento de desempenho com o nginx plus (pago). No entanto, as configurações abaixo são possíveis na versão free e open-source.

Por padrão, o Nginx distribui as solicitações entre os servidores do grupo de acordo com os seus pesos, utilizando o método round-robin. O parâmetro weight da diretiva server define o peso de um servidor que, por padrão, é 1:

upstream backend {  
    server backend1.example.com weight=5;
    server backend2.example.com;
    server 192.0.0.1 backup;
}

No exemplo, backend1.exemplo.com tem peso 5; os outros dois servidores têm o peso padrão (1), mas o único com endereço IP 192.0.0.1 que está marcado como um servidor de backup, não recebe solicitações a menos que ambos os outros servidores estejam indisponíveis. Com esta configuração de pesos, em cada seis pedidos, cinco são enviados backend1.exemplo.com e um para backend2.exemplo.com.

Existe também uma função de start lento que impede que um servidor inicie sem levar em consideração o tráfego pesado da rede. Desta maneira, evitamos falhas relacionadas ao tráfego intenso da rede. Para tal, basta usar a diretiva slow_start:

upstream backend {  
    server backend1.example.com slow_start=30s;
    server backend2.example.com;
    server 192.0.0.1 backup;
} 

Nota: se houver apenas um único grupo de servidores com os parâmetros max_fails, e fail_timeout, o parâmetro slow_start será ignorado pois, estes parâmetros.

O Nginx também suporta métodos de persistência. Os métodos são definidos com a diretiva sticky ou melhor, o método sticky cookie. Com este método, o Nginx adiciona um cookie de sessão para a primeira resposta do grupo upstream e identifica o servidor que enviou a mensagem de resposta:

upstream backend {  
    server backend1.example.com;
    server backend2.example.com;

    sticky cookie srv_id expires=1h domain=.example.com path=/;
}

No exemplo acima, o parâmetro srv_id define o nome do cookie que será definido ou inspecionado. O parâmetro expires limita o tempo em que o parâmetro irá manter o cookie. O parâmetro domain é opcional e define um domínio para o qual o cookie será definido. O parâmetro path define o caminho do cookie. Este é o método mais simples de persistência.

O método sticky route irá atribuir uma rota para o cliente quando ele receber o primeiro pedido. Todos os pedidos subsequentes serão comparados com o parâmetro route da diretiva server e identificar o servidor onde os pedidos serão filtrados. As informações rota são feitas a partir de qualquer cookie ou URI:

upstream backend {  
    server backend1.example.com route=a;
    server backend2.example.com route=b;

    sticky route $route_cookie $route_uri;
}

O método cookie learn encontra identificadores de sessão inspecionando solicitações e respostas. Então o Nginx 'aprende' quais servidores upstream corresponde, e qual é o identificador de sessão:

upstream backend {  
   server backend1.example.com;
   server backend2.example.com;

   sticky learn 
       create=$upstream_cookie_examplecookie
       lookup=$cookie_examplecookie
       zone=client_sessions:1m
       timeout=1h;
} 

No exemplo acima, um dos servidores upstream cria uma sessão, definindo o cookie exameplecookie na resposta. O parâmetro obrigatório create especifica uma variável que indica como uma nova sessão é criada. No nosso exemplo, novas sessões são criados a partir do cookie 'examplecookie' enviada pelo servidor upstream.

Outro parâmetro obrigatório lookupespecifica como procurar sessões existentes. No nosso exemplo, sessões existentes são pesquisadas ​​no cookie 'examplecookie' enviada pelo cliente. O parâmetro zone, por sua vez, especifica uma zona de memória compartilhada, onde todas as informações sobre sessões sticky será mantido. No nosso exemplo, o zone é denominado client_sessions e tem o tamanho de 1 megabyte.

Este é um método sessão de persistência mais sofisticado, pois não exige que se mantenha os cookies no lado do cliente. Isto é, toda a informação será mantida do lado no servidor e na zona de memória partilhada. É possível também manter o número desejado de conexões definindo um limite de conexões com o parâmetro max_conns. Se o limite max_conns tiver sido atingido, o pedido pode ser colocado na fila de espera para o ser processado mais a diante, desde que a diretiva queue seja declarada. A diretiva define o número máximo de solicitações que podem ser simultaneamente na queue:

upstream backend {  
    server backend1.example.com  max_conns=3;
    server backend2.example.com;

    queue 100 timeout=70;
}

Se a queue é preenchida com pedidos ou o servidor upstream não pode ser selecionado durante o tempo limite especificado no parâmetro de tempo limite opcional, o cliente receberá um erro. Note que o limite max_conns será ignorado se houver conexões keepalive ociosas abertas em outros processos de trabalho. Como resultado, o número total de conexões para o servidor pode exceder o valor max_conns numa configuração em que a memória é partilhada com vários processos de trabalho.

No próximo artigo, vou explorar um pouco mais sobre balanceamento de carga utilizando métodos de monitoramento ativo, e passivo, compartilhamento de dados com múltiplos processos, balanceamento de carga usando DNS, configuração On-the-Fly e On-the-fly persistente, isto é, permitindo visualizar todos os servidores, ou um servidor específico em um grupo e modificar parâmetros para um determinado servidor. Espero que tenham gostado do artigo, até a próxima.

Sugestão para estudo: