📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Cap. 09 (Nginx) do Guia de Redes. Recomendado revisar antes de começar.
🚀 Projeto: Nginx com Balanceamento de Carga (Java 21 / Spring Boot)`
Criar um ambiente que simula um sistema web real, demonstrando visualmente como o Nginx atua como um Reverse Proxy e distribui a carga (Load Balancing) entre duas instâncias idênticas de uma API Spring Boot.
Este projeto é a versão em Java/Spring Boot do mesmo experimento feito com Node.js em infra_nginx_01_nodejs — a diferença central é que aqui as duas instâncias de backend rodam a partir do mesmo .jar, diferenciadas apenas por uma variável de ambiente (SERVER_ID), em vez de serem dois projetos separados.`
graph TD
subgraph "Cliente"
User["Navegador (Browser)"]
end
subgraph "Docker Compose Network"
Nginx["Nginx (Load Balancer & Proxy Reverso)"]
API_01["api-01 (Spring Boot, SERVER_ID=API-01)"]
API_02["api-02 (Spring Boot, SERVER_ID=API-02)"]
Visualizer["Visualizador (Static Files)"]
end
User -->|":80 /"| Nginx
Nginx -->|"/api (Load Balancing)"| API_01
Nginx -->|"/api (Load Balancing)"| API_02
Nginx -->|"Server Files"| Visualizer
Visualizer -->|"fetch('/api')"| Nginx
spring-boot-starter-web).infra_nginx_02_spring/
├── src/main/java/br/com/tarefas/load_balancing_01/
│ ├── ApiController.java
│ └── LoadBalancing01Application.java
├── src/main/resources/application.properties
├── pom.xml
├── Dockerfile
├── nginx/
│ ├── nginx.conf
│ └── Dockerfile
├── visualizer/
│ └── index.html
└── docker-compose.yml
Ao contrário da versão Node.js (dois projetos api-01/api-02), aqui existe um único projeto Spring Boot. As duas “instâncias” no Docker Compose sobem a mesma imagem, cada uma com um valor diferente de SERVER_ID injetado via variável de ambiente.
pom.xml — dependências principais: spring-boot-starter-web (Java 21).src/main/java/.../ApiController.java:
package br.com.tarefas.load_balancing_01;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.Date;
@RestController
public class ApiController {
// Injeta o valor da variável de ambiente 'SERVER_ID'
// Se não for definida, usará 'API-DEFAULT'
@Value("${SERVER_ID:API-DEFAULT}")
private String serverId;
@GetMapping("/api")
public Map<String, Object> getApiData() {
System.out.println("[" + serverId + "] Recebeu requisição");
return Map.of(
"servidor", serverId,
"timestamp", new Date().toString()
);
}
}
src/main/resources/application.properties:
spring.application.name=load_balancing_01
# Estágio 1: Build (Compilação)
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# Estágio 2: Run (Execução)
FROM eclipse-temurin:21-jre-jammy
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
# A variável SERVER_ID será injetada pelo Docker Compose
ENTRYPOINT ["java", "-jar", "app.jar"]
nginx/nginx.conf:
upstream backend_servers {
# O padrão é round-robin (alterna entre eles)
# Note a porta 8080, padrão do Spring Boot
server api-01:8080;
server api-02:8080;
}
events { }
http {
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
location /api {
rewrite /api(.*) /$1 break;
proxy_pass http://backend_servers;
proxy_set_header Host $host; proxy_set_header X-Real-IP$remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto$scheme;
}
}
}
nginx/Dockerfile:
FROM nginx:1.23-alpine
# Copia nossa configuração personalizada (relativo ao 'context')
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
# Copia os arquivos do visualizer (relativo ao 'context')
COPY ./visualizer /usr/share/nginx/html
visualizer/index.html consome fetch('/api') a cada 200ms e monta um gráfico de pizza (Chart.js via CDN) com a contagem de respostas de cada SERVER_ID, igual ao dashboard de infra_nginx_01_nodejs — o payload JSON (servidor/timestamp) é o mesmo formato, então o mesmo dashboard funciona sem alteração.
services:
api-01:
build:
context: .
dockerfile: ./Dockerfile
environment:
- SERVER_ID=API-01
api-02:
build:
context: .
dockerfile: ./Dockerfile
environment:
- SERVER_ID=API-02
nginx:
build:
context: .
dockerfile: ./nginx/Dockerfile
ports:
- "80:80"
depends_on:
- api-01
- api-02
Note que api-01 e api-02 usam o mesmo Dockerfile/contexto — a diferença entre eles é só a variável de ambiente SERVER_ID.
1. Suba os serviços:
docker-compose up --build
Isso constrói a imagem Spring Boot (usada duas vezes, para api-01 e api-02) e a imagem do Nginx.
2. Acesse o Visualizador:
Abra http://localhost. O dashboard começa a fazer requisições automaticamente e o gráfico de pizza deve convergir para ~50%/50% entre API-01 e API-02.
3. (Opcional) Teste de carga com k6:
Reaproveite o load-test.js de infra_nginx_01_nodejs apontando para http://localhost/api — a rota é idêntica.
4. Para desligar:
docker-compose down
No seu editor/IDE, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:
cd infra_nginx_02_spring
terraform init && terraform plan
# ou docker-compose up -d
[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code, basta navegar até a pasta antes de executar: cd catalogo_projetos/infra_nginx_02_spring
O diagrama abaixo ilustra a topologia de componentes, balanceamento, esteiras de automação e observabilidade na subpasta api-spring/:
flowchart TD
subgraph Config["Configuração & IaC"]
C1["Manifestos Declarativos"] --> C2["Variáveis & Segredos"]
end
subgraph Orquestracao["Orquestração & Redes"]
O1["Load Balancer / Ingress"] --> O2["Pods / Contêineres de Serviço"]
end
subgraph Observabilidade["Observabilidade & Segurança"]
M1["Telemetria Prometheus / k6"] --> M2["Scanners SAST & Dashboards"]
end
subgraph Qualidade["Homologação & Qualidade"]
Q1["Automação CI/CD"] --> Q2["Checkpoints CP-01..05 PASS"]
end
Config --> Orquestracao
Orquestracao --> Observabilidade
Observabilidade --> Qualidade
style Config fill:#e3f2fd,stroke:#1976d2,stroke-width:1.5px
style Orquestracao fill:#fffde7,stroke:#fbc02d,stroke-width:1.5px
style Observabilidade fill:#e8f5e9,stroke:#388e3c,stroke-width:1.5px
style Qualidade fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1.5px
| ID | Persona | História de Usuário | Critério de Aceite | Prioridade |
|---|---|---|---|---|
| US-01 | Arquiteto Java | Desejo balancear microsserviços Spring Boot 3 com Nginx Least Connections | Roteamento inteligente enviando requisições ao nó com menor número de conexões ativas | Alta |
| US-02 | Engenheiro de Confiabilidade | Desejo integração com Spring Boot Actuator /health no balanceador | Monitoramento contínuo de integridade através de endpoint padrão corporativo | Alta |
| US-03 | Desenvolvedor Backend | Desejo balanceamento com ponderação de pesos (weights) por hardware | Capacidade de atribuir pesos diferentes para nós de capacidades distintas | Alta |
| US-04 | Operador de TI | Desejo container multi-stage build empacotando jar enxuto do Spring | Dockerfile otimizado em camadas com Eclipse Temurin Java 21 | Média |
| US-05 | Auditor de Disponibilidade | Desejo testes comprovando zero downtime durante reinício de nós | Bateria de testes de carga simulando restart rotativo de instâncias | Média |
| ID | Descrição do Requisito | Criticidade |
|---|---|---|
| RF-01 | Algoritmo Least Connections e ponderação por pesos (weight) no Nginx | Alta |
| RF-02 | Microsserviço Spring Boot 3 com endpoints REST e Actuator /health | Alta |
| RF-03 | Orquestração multi-contêiner com health checks configurados no compose | Alta |
| RF-04 | Painel visual de status dos contêineres e tráfego em tempo real | Média |
| RF-05 | Módulo de testes automatizados com simulação de carga contínua | Média |
| ID | Especificação Arquitetural | Criticidade |
|---|---|---|
| RNF-01 | Suporte a tráfego concorrente elevado com pool de conexões otimizado | Alta |
| RNF-02 | Garantia de inicialização em menos de 15 segundos por réplica Java | Média |
| RNF-03 | Isolamento de dependências com código fonte Java na pasta api-spring/ | Alta |
| RNF-04 | Conformidade com padrões corporativos Spring Boot e Java 21 LTS | Alta |
| RNF-05 | Artefatos de infraestrutura segregados na pasta nginx/ | Alta |
| User Story | Requisito Funcional | Requisito Não-Funcional | Módulo / Artefato de Código | Status de Homologação |
|---|---|---|---|---|
| US-01 | RF-01 |
RNF-01, RNF-05 |
nginx/nginx.conf |
Testado |
| US-02 | RF-02 |
RNF-01, RNF-04 |
api-spring/src/ |
Testado |
| US-03 | RF-01 |
RNF-01, RNF-05 |
nginx/nginx.conf |
Testado |
| US-04 | RF-03 |
RNF-02, RNF-03 |
api-spring/Dockerfile |
Testado |
| US-05 | RF-05 |
RNF-01, RNF-04 |
tests/load_test.sh |
Testado |
| Checkpoint | Escopo da Validação | Artefato Verificado | Status |
|---|---|---|---|
| CP-01 | Configuração Declarativa do Ambiente | api-spring/ |
PASS ✅ |
| CP-02 | Provisionamento & Orquestração | Deployments / Services |
PASS ✅ |
| CP-03 | Redes, Proxies & Segurança de Borda | Ingress / Security Groups |
PASS ✅ |
| CP-04 | Observabilidade, Métricas & SAST | Metrics / Scanners |
PASS ✅ |
| CP-05 | Homologação com Suíte de Testes | Test Suite / CI Gate |
PASS ✅ |