O objetivo desta série é projetar uma infraestrutura resiliente, escalável e de alta performance para lidar com aplicações que exigem alta taxa de transações (muitos writes/reads por segundo). Neste post o foco é o balanceamento de carga utilizando Nginx na frente de múltiplas instâncias Spring Boot + Postgres, em ambiente dockerizado. A meta prática: suportar múltiplos serviços concorrentes, além de aplicações web e integrações externas (como bots).
Quando falamos de aplicações em produção, não basta apenas “funcionar”, é preciso escala, resiliência e desempenho. Um dos primeiros passos nesse caminho é o uso de load balancers, responsáveis por distribuir requisições entre múltiplos servidores e garantir que nenhum deles se torne um gargalo.
+---------------------+
| |
+------------> | Container 1 |
+-----------------+ | | |
| | | +---------------------+
| Nginx |-------+
| | | +---------------------+
+-----------------+ | | |
+------------> | Container 2 |
| |
+---------------------+Na documentação do Docker é possível encontrar recursos que permitem escalar múltiplas instâncias de um mesmo serviço, deixando que o próprio Docker gerencie a distribuição das conexões entre elas. No entanto, o objetivo aqui é entender os fundamentos do balanceamento de carga utilizando diretamente o Nginx como ponto central.
No docker-compose foi configurada a execução de duas instâncias da mesma aplicação. Abaixo segue um exemplo simplificado de como essa configuração foi estruturada:
networks: define a rede interna compartilhada entre as instâncias da aplicação e o Nginx. Como apenas o Nginx é exposto externamente, é necessário que todos os containers backend estejam na mesma rede para que o tráfego seja roteado corretamente.
services:
api_server_a:
build:
context: .
dockerfile: Dockerfile
environment:
POSTGRESQL_URL: jdbc:postgresql://postgres:5432/${POSTGRESQL_DATABASE}
DB_USER: ${POSTGRESQL_USERNAME}
DB_PASSWORD: ${POSTGRESQL_PASSWORD}
networks:
- znet
api_server_b:
build:
context: .
dockerfile: Dockerfile
environment:
DB_URL: jdbc:postgresql://postgres:5432/${POSTGRESQL_DATABASE}
DB_USER: ${POSTGRESQL_USERNAME}
DB_PASSWORD: ${POSTGRESQL_PASSWORD}
networks:
- znet
nginx_lb:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- api_server_a
- api_server_b
networks:
- znet
networks:
znet:No arquivo nginx.conf foram configurados os hostnames das duas instâncias da aplicação dentro do bloco upstream api_backend.
Importante destacar que apenas o Nginx foi exposto externamente; os containers das APIs permanecem acessíveis apenas pela rede interna.
-
upstream api_backend { ... }: Cria um grupo de servidores backend com o nome api_backend. Esse nome vai ser usado depois noproxy_pass. -
least_conn;: Estratégia de balanceamento: direcionar a requisição pro servidor que tiver menos conexões ativas. -
server api-server-a:8080 ...: Diz que o servidor api-server-a (um container no caso) está rodando na porta 8080 e faz parte do pool. -
max_fails=3: Se o servidor falhar 3 vezes seguidas, o Nginx para de mandar tráfego pra ele temporariamente. -
fail_timeout=30s: O servidor fica sem receber tráfego por 30 segundos antes do Nginx tentar usá-lo de novo. -
location / { ... }: Todas as requisições que baterem em/(ou seja, qualquer coisa) vão cair aqui.
events {
worker_connections 1024;
}
http {
client_max_body_size 5m;
upstream api_backend {
least_conn;
server api-server-a:8080 max_fails=3 fail_timeout=30s;
server api-server-b:8080 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
error_log /var/log/nginx/error-backend.log warn;
access_log /var/log/nginx/access-backend.log;
location / {
proxy_pass http://api_backend/;
proxy_set_header Host $host;
proxy_read_timeout 120s;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
}
}
}Obs: Este é um post introdutório, portanto as configurações apresentadas foram simplificadas para fins de compreensão. É importante destacar que o uso de um Load Balancer contribui para distribuir carga e melhorar a resiliência, mas não resolve, por si só, todos os problemas de performance. Existem outras abordagens de otimização que serão tratadas em publicações futuras.