Ajuste de Desempenho do Nginx: Otimize Processos de Trabalho e Conexões

Ajuste os processos de trabalho, conexões de trabalho, limites de arquivos, comportamento keepalive e capacidade upstream do Nginx sem adivinhação.

Ajuste de Desempenho do Nginx: Otimize Processos de Trabalho e Conexões

O ajuste de desempenho do Nginx geralmente começa com duas configurações: processos de trabalho e conexões de trabalho. Essas configurações determinam quantas requisições seu servidor pode lidar simultaneamente, então pequenos erros podem aparecer como páginas lentas, downloads travados ou erros de conexão durante picos de tráfego.

A boa notícia é que você não precisa adivinhar aleatoriamente. Você pode ajustar o Nginx combinando seu modelo de trabalho com sua CPU, limites de arquivos, padrão de tráfego e comportamento da aplicação upstream.

Como os Trabalhadores do Nginx Lidam com o Tráfego

O Nginx usa um processo mestre e um ou mais processos de trabalho. O processo mestre lê a configuração, inicia os trabalhadores e lida com recarregamentos. Os trabalhadores fazem o tratamento real das requisições.

Cada trabalhador pode lidar com muitas conexões porque o Nginx usa um modelo orientado a eventos. Isso é diferente de servidores web mais antigos que frequentemente precisavam de um processo ou thread por requisição. Um trabalhador do Nginx pode manter milhares de conexões keepalive ociosas abertas se o sistema operacional permitir.

As duas diretivas principais geralmente são colocadas em /etc/nginx/nginx.conf:

worker_processes auto;

events {
    worker_connections 1024;
}

worker_processes auto; instrui o Nginx a criar um trabalhador para cada núcleo de CPU disponível. Para a maioria dos servidores Linux modernos, este é o ponto de partida correto. Evita codificar um valor que se torna errado quando você redimensiona uma máquina virtual.

worker_connections define o número máximo de conexões simultâneas que cada trabalhador pode abrir. O limite superior aproximado é:

worker_processes * worker_connections

Se você tem 4 trabalhadores e 4096 conexões de trabalho, o máximo teórico é 16.384 conexões. Na vida real, o número utilizável é menor porque o tráfego de proxy reverso pode usar conexões tanto do lado do cliente quanto upstream.

Por exemplo, se o Nginx faz proxy de tráfego para um aplicativo Node.js, uma requisição de usuário pode consumir uma conexão de cliente mais uma conexão upstream. Isso significa que 16.384 conexões abertas podem suportar cerca de 8.000 requisições proxy ativas, dependendo do keepalive e do tempo das requisições.

Escolhendo Valores para Processos de Trabalho e Conexões

Comece com worker_processes auto a menos que você tenha um motivo específico para não fazê-lo. Definir manualmente este valor acima da contagem de CPUs raramente ajuda. Pode aumentar a troca de contexto e piorar o desempenho sob carga.

Em seguida, ajuste worker_connections com base na concorrência esperada. Uma ferramenta interna tranquila pode ficar bem com 1024. Um site público atrás de um balanceador de carga pode precisar de 4096, 8192 ou mais.

Uma linha de base prática para muitos servidores de produção se parece com isso:

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
}

worker_rlimit_nofile aumenta o limite de descritores de arquivo disponível para os trabalhadores do Nginx. Isso é importante porque cada soquete de rede usa um descritor de arquivo. Se o limite do sistema operacional permanecer baixo, aumentar worker_connections sozinho não ajudará.

Você também deve verificar o limite do gerenciador de serviços. Em sistemas systemd, o Nginx pode precisar de uma substituição como:

[Service]
LimitNOFILE=65535

Após alterar os limites do systemd, recarregue o systemd e reinicie o Nginx. Para uma referência de comandos mais ampla, veja Comandos de controle de serviço do Nginx.

Tenha cuidado com multi_accept on. Ele permite que um trabalhador aceite o máximo de novas conexões possível após receber uma notificação de prontidão. Isso pode ajudar durante picos, mas não é mágico. Se seu aplicativo upstream for lento, aceitar conexões mais rapidamente pode apenas encher as filas mais rápido.

Limites do Sistema Operacional que Afetam o Nginx

As configurações do Nginx ficam sobre os limites do Linux. Se esses limites forem muito pequenos, o Nginx atingirá um teto mesmo quando sua própria configuração parecer generosa.

Verifique estas áreas ao ajustar:

  • Limite de arquivos abertos para o processo Nginx
  • Configurações de backlog de rede do kernel
  • Disponibilidade de portas efêmeras para tráfego pesado de proxy
  • Comportamento keepalive upstream
  • Valores de tempo limite ocioso do balanceador de carga

O limite de arquivos abertos é o bloqueador mais comum. Se o Nginx registrar mensagens como worker_connections are not enough ou too many open files, você precisa olhar tanto para os limites do Nginx quanto do systemd.

As configurações de backlog são importantes quando muitos clientes se conectam ao mesmo tempo. Se a fila de aceitação do kernel encher, os usuários podem ver timeouts de conexão mesmo que o uso da CPU pareça normal. Valores como net.core.somaxconn e net.ipv4.tcp_max_syn_backlog são frequentemente revisados durante o ajuste de alto tráfego.

Não copie grandes valores de kernel de exemplos aleatórios sem testar. Uma pequena equipe executando um servidor de API não precisa das mesmas configurações que um nó de borda de CDN. Ajuste em etapas, meça e mantenha anotações.

Há outro detalhe que pega as pessoas: o limite de conexão do Nginx não é o único limite de conexão no caminho. Um balanceador de carga em nuvem tem timeouts ociosos. Um runtime de contêiner pode ter limites de tradução de endereço de rede. O aplicativo backend pode ter seu próprio pool de trabalhadores ou pool de conexão de banco de dados. Se o Nginx pode aceitar 20.000 conexões, mas o aplicativo pode processar apenas 200 requisições concorrentes, os usuários ainda esperarão.

É por isso que uma alteração no ajuste de conexão deve incluir uma verificação rápida de ponta a ponta. Execute um pequeno teste de carga de um host fora do servidor, observe as conexões ativas do Nginx e também observe o backend. Se a latência do backend aumentar acentuadamente enquanto o Nginx permanece calmo, o proxy está fazendo seu trabalho e o próximo limite está atrás dele.

Ajuste para Cargas de Trabalho de Proxy Reverso

Muitos servidores Nginx atuam como proxies reversos na frente de servidores de aplicação. Nesse papel, o comportamento upstream importa tanto quanto a capacidade do Nginx.

Use keepalive upstream quando o Nginx fala repetidamente com o mesmo pool de backend:

upstream app_backend {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://app_backend;
    }
}

Isso reduz o custo de abrir novas conexões de backend. É especialmente útil quando seu aplicativo recebe muitas requisições pequenas, como chamadas de API de um painel.

Verifique também seus valores de timeout. Timeouts de proxy muito longos podem manter as conexões dos trabalhadores ocupadas depois que os clientes desaparecem ou os aplicativos param de responder. Timeouts muito curtos podem quebrar requisições lentas legítimas. Corresponda os valores de timeout à carga de trabalho em vez de usar um padrão em todos os lugares.

Um cenário prático: seu site é rápido na maior parte do dia, mas fica lento durante o envio de uma newsletter. A CPU está em apenas 35%, mas os logs do Nginx mostram avisos de conexão. Isso aponta para longe da CPU bruta e para limites de conexão, descritores de arquivo ou enfileiramento upstream. Aumentar as conexões de trabalho pode ajudar, mas apenas se o aplicativo e o SO puderem suportar a carga extra.

Outro cenário comum é um aplicativo de painel que faz muitas chamadas de API pequenas de cada aba do navegador. Dez pessoas podem criar centenas de requisições curtas. Nesse caso, o keepalive upstream geralmente importa mais do que simplesmente aumentar worker_connections, porque a configuração repetida de TCP para o backend se torna uma sobrecarga desnecessária.

Para um serviço de download de arquivos, a história é diferente. Um pequeno número de usuários pode manter conexões abertas por um longo tempo enquanto baixa arquivos grandes. Você pode precisar de conexões de trabalho suficientes para transferências de longa duração, mas também deve verificar sendfile, throughput de disco, throughput de rede e comportamento de timeout do cliente.

Para aplicativos WebSocket ou de long-polling, conexões ociosas são normais. Um número alto de Waiting não é automaticamente ruim. A questão é se essas conexões ociosas deixam capacidade suficiente para novas requisições e se o uso de memória permanece previsível.

Lendo stub_status Durante o Ajuste

O módulo stub_status fornece uma visão rápida do comportamento das conexões:

Active connections: 291
server accepts handled requests
 1162447 1162447 4496426
Reading: 6 Writing: 17 Waiting: 268

Reading significa que o Nginx está lendo cabeçalhos de requisição. Um número alto sustentado pode apontar para clientes lentos, cabeçalhos grandes ou um padrão de ataque. Writing significa que o Nginx está enviando respostas. Isso pode aumentar quando os clientes são lentos para receber dados ou quando as respostas são grandes. Waiting significa conexões keepalive ociosas. Esse número pode ser alto em sites saudáveis.

Os contadores accepts e handled geralmente devem se mover juntos. Se as conexões aceitas aumentarem, mas as conexões tratadas ficarem para trás ou aparecerem erros, verifique os limites dos trabalhadores e os limites de descritores de arquivo. Verifique também se o kernel está descartando conexões antes que o Nginx possa tratá-las.

Esses contadores são básicos, mas são úteis porque separam a pressão de conexão da pressão da CPU. Se as conexões ativas são baixas e a CPU está alta, o problema provavelmente não é worker_connections. Se as conexões ativas são altas e a CPU está baixa, limites de conexão, comportamento keepalive, enfileiramento upstream ou clientes lentos se tornam mais prováveis.

Uma Configuração de Linha de Base Segura

Para um pequeno servidor de produção, prefiro começar conservadoramente e medir:

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    keepalive_timeout 30s;
    keepalive_requests 1000;
}

Esta não é uma configuração universalmente melhor. É um ponto de partida razoável para muitas cargas de trabalho normais de proxy reverso. Uma VM muito pequena pode precisar de menos. Um proxy de borda ocupado pode precisar de muito mais. A parte importante é que worker_connections e worker_rlimit_nofile estejam alinhados.

Após aplicar uma linha de base, compare as métricas antes e depois durante tráfego semelhante. Não julgue uma alteração de ajuste por um minuto de sorte após o recarregamento. Observe a latência p95 ou p99, taxa de erro, CPU, memória e enfileiramento do backend por tempo suficiente para ver o comportamento real.

Erros Que Tornam o Ajuste de Conexão Aleatório

O primeiro erro é contar requisições em vez de conexões. Um navegador pode reutilizar uma conexão para múltiplas requisições, e HTTP/2 pode multiplexar muitas requisições em uma conexão. Um cliente lento também pode manter uma conexão aberta enquanto faz muito pouco trabalho útil. Isso significa que "recebemos apenas 500 requisições por segundo" não informa quantas conexões o Nginx precisa.

O segundo erro é esquecer as conexões upstream. Se o Nginx serve arquivos estáticos, a maioria das conexões é voltada para o cliente. Se o Nginx faz proxy para um aplicativo, as requisições ativas geralmente precisam de soquetes de backend também. Se você usar keepalive para o upstream, algumas conexões de backend permanecem abertas para reutilização. Isso é bom, mas ainda consome descritores de arquivo em ambos os lados.

O terceiro erro é aumentar os limites do Nginx sem verificar o aplicativo. Suponha que o Nginx agora pode aceitar 12.000 conexões simultâneas, mas o aplicativo tem 16 processos de trabalho e um pool de banco de dados de 50 conexões. O Nginx aceitará mais trabalho do que o aplicativo pode concluir. Os usuários podem ver menos erros de conexão imediatos, mas a latência pode piorar porque as requisições esperam mais tempo nas filas.

O quarto erro é usar timeouts keepalive longos em todos os lugares. Keepalive é útil porque evita configuração repetida de TCP e TLS. Mas um timeout muito longo pode deixar muitos soquetes ociosos abertos após um pico de tráfego. Em um proxy de borda com muita memória, isso pode ser aceitável. Em uma VM pequena, pode sufocar o trabalho ativo. Se você vir uma contagem enorme de Waiting e baixa reutilização de requisições, tente um keepalive_timeout mais curto e meça novamente.

Exemplos de Solução de Problemas

Se o log de erros disser worker_connections are not enough, verifique o valor configurado, o número de trabalhadores e o limite de arquivos do processo:

grep -R "worker_connections\\|worker_processes\\|worker_rlimit_nofile" /etc/nginx/nginx.conf /etc/nginx/conf.d
cat /proc/$(pgrep -o nginx)/limits | grep "open files"

O comando pgrep -o nginx geralmente encontra o processo Nginx mais antigo, que geralmente é o mestre. Em alguns sistemas, você pode preferir systemctl status nginx para ver o PID principal.

Se o log de erros disser too many open files, não aumente apenas worker_connections. O processo está atingindo seu limite de descritores. Adicione ou ajuste LimitNOFILE para o serviço systemd, recarregue o systemd e reinicie o Nginx para que o novo limite seja realmente aplicado:

sudo systemctl edit nginx
sudo systemctl daemon-reload
sudo systemctl restart nginx

Se os usuários virem timeouts, mas o Nginx tiver CPU de sobra e nenhum aviso de conexão, olhe atrás do Nginx. Verifique o tempo de resposta upstream nos logs de acesso. Verifique o pool de trabalhadores do aplicativo. Verifique as conexões do banco de dados. Um proxy reverso pode aceitar tráfego suavemente enquanto o verdadeiro gargalo é um backend saturado.

Se um pico causar redefinições de conexão antes que as requisições apareçam nos logs de acesso, o problema pode ser anterior ao tratamento de requisições do Nginx. Olhe as configurações de backlog do kernel, logs do balanceador de carga, tabelas de estado do firewall e proteção SYN flood. O Nginx não pode registrar uma requisição que nunca recebeu.

Como Testar Alterações com Segurança

Nunca ajuste a produção editando e esperando. Teste a sintaxe primeiro:

sudo nginx -t

Em seguida, recarregue o Nginx para que as conexões ativas sejam tratadas graciosamente:

sudo systemctl reload nginx

Monitore o log de erros após cada alteração:

sudo tail -f /var/log/nginx/error.log

Você também deve monitorar a latência das requisições, taxas de 4xx e 5xx, conexões ativas, CPU, memória e tempo de resposta upstream. Uma alteração de ajuste que aumenta a capacidade de conexão, mas aumenta a latência do aplicativo, pode não ser uma vitória real.

Para etapas de validação mais aprofundadas, veja testando configurações do Nginx.

Quando Chamar um Especialista

Chame um engenheiro DevOps experiente ou especialista em desempenho web quando os erros do Nginx continuarem após o ajuste básico, quando picos de tráfego afetarem a receita, ou quando você estiver alterando configurações de rede do kernel em um sistema de produção. O mesmo se aplica se você estiver ajustando o Nginx na frente de fluxos de pagamento, sistemas de login ou APIs críticas.

Ajuda profissional também é útil quando o gargalo não está claro. O Nginx pode parecer o problema quando a verdadeira questão é um banco de dados lento, pool de aplicativos upstream esgotado, camada de terminação TLS sobrecarregada ou incompatibilidade de timeout do balanceador de carga.

A principal conclusão é simples: ajuste os processos de trabalho para corresponder à CPU, ajuste as conexões de trabalho para corresponder à concorrência e certifique-se de que os limites de arquivos do Linux suportem ambos. Altere uma camada de cada vez, teste a configuração antes de recarregar e meça o tráfego real em vez de confiar em máximos teóricos.