Proxy Reverso e Service Discovery
Quando uma aplicação deixa de ser um monólito solitário e passa a operar em um cluster, a complexidade não aumenta apenas linearmente—ela explode. O desafio deixa de ser “como o código executa” e passa a ser “como o tráfego encontra o código”. Em ambientes modernos de containers e nuvem, onde endereços IP são tão efêmeros quanto um suspiro, confiar em configurações estáticas é o primeiro passo para o desastre.
A Efemeridade dos Endereços
Em um cenário de escalonamento horizontal, instâncias da nossa aplicação nascem e morrem constantemente. O Kubernetes pode matar um Pod em um nó e subi-lo em outro com um IP completamente diferente. Se o seu balanceador de carga espera encontrar a aplicação sempre no 10.0.0.5:8080, qualquer reinicialização causará um downtime imediato.
Abaixo, vemos o fluxo onde o Proxy precisa descobrir dinamicamente quem está pronto para receber carga:
flowchart TD
User((Usuário)) -- HTTPS --> Proxy[Proxy Reverso / Load Balancer]
subgraph "Cluster Dinâmico"
Proxy -- Request --> App1[App Instância A]
Proxy -- Request --> App2[App Instância B]
Proxy -- Request --> App3[App Instância C]
end
SD[(Service Discovery)] <--> Proxy
App1 -. Registro & Heartbeat .-> SD
App2 -. Registro & Heartbeat .-> SD
App3 -. Registro & Heartbeat .-> SD
style SD fill:#0096ff,fill-opacity:0.1,stroke:#0096ff
style Proxy fill:#ff6400,fill-opacity:0.1,stroke:#ff6400
1. O Guardião do Tráfego: Proxy Reverso
O Proxy Reverso é a face pública da sua infraestrutura. Além de prover segurança e terminação TLS, sua função primordial no escalonamento é o Load Balancing. Ele atua como um intermediário que recebe requisições e as distribui conforme algoritmos específicos (Round Robin, Least Connections, etc).
Configuração Dinâmica do Nginx
Em um cenário real, você não quer editar o nginx.conf manualmente a cada novo container. Existem duas abordagens principais para tornar o Nginx “vivo”:
- DNS Resolver (Nativo): O Nginx consulta um servidor de DNS (como o CoreDNS do Kubernetes ou o DNS do Consul) para resolver o nome do serviço.
- Consul Template (Sidecar): Uma ferramenta externa monitora o Service Discovery e reescreve o arquivo de configuração do Nginx automaticamente, disparando um
reload.
Abaixo, um exemplo de configuração usando o Resolver, que permite que o Nginx atualize os IPs do cluster sem precisar reiniciar:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# file: nginx-cluster.conf
worker_processes auto;
events {
worker_connections 1024;
}
http {
# Endereço do Servidor de DNS do Service Discovery (ex: Consul ou K8s)
# valid=10s força o Nginx a re-checar o DNS a cada 10 segundos
resolver 127.0.0.1:8600 valid=10s;
server {
listen 80;
server_name api.techblog.com;
# Redirecionamento para HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name api.techblog.com;
# Configuração de Certificados
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
location / {
# Usar uma variável força o Nginx a usar o 'resolver' acima
# em vez de resolver o IP apenas uma vez no startup.
set $upstream_endpoint order-service.service.consul;
proxy_pass http://$upstream_endpoint:8080;
proxy_http_version 1.1;
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;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
}
}
2. Service Discovery: O Cérebro da Operação
O Service Discovery resolve o problema do “catálogo de endereços”. Ele é um banco de dados de alta disponibilidade que mantém o mapeamento entre o Nome do Serviço (ex: payment-service) e seus Endereços Ativos (ex: 10.0.5.12:9090, 10.0.5.15:9090).
Por que precisamos dele?
Sem Service Discovery, você teria que atualizar o arquivo nginx.conf manualmente a cada novo container que subisse no cluster. O Service Discovery automatiza esse processo: a aplicação se registra ao subir e se remove ao descer.
Como funciona (Implementação com Spring Cloud)
Para que uma aplicação Java participe dessa orquestra, utilizamos o Eureka ou Consul. Veja o arquivo de configuração completo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!-- file: pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.techblog</groupId>
<artifactId>order-service</artifactId>
<version>1.0.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Dependência vital para o Service Discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# file: src/main/resources/application.yml
server:
port: 8080
spring:
application:
name: order-service # Nome pelo qual o serviço será encontrado no cluster
eureka:
client:
serviceUrl:
# Endereço do servidor central de descoberta
defaultZone: http://eureka-server:8761/eureka/
# Frequência de atualização da lista local de outros serviços
registry-fetch-interval-seconds: 30
instance:
# Registra o IP ao invés do hostname (mais robusto em redes Docker)
prefer-ip-address: true
# Intervalo de envio do Heartbeat
lease-renewal-interval-in-seconds: 30
3. O Mecanismo do Heartbeat
O Service Discovery não apenas “anota” o endereço; ele vigia a saúde da aplicação.
- Auto-registro: Ao iniciar, a App envia um POST para o Discovery Server com seu IP/Porta.
- Heartbeat: A cada N segundos (ex: 30s), a App envia um “estou viva”.
- Eviction: Se o Discovery Server ficar 90 segundos sem ouvir a App, ele a remove do catálogo. O Proxy, então, para de enviar tráfego para ela.
Tradeoffs: A Moeda de Troca da Automação
Nada é de graça em arquitetura de sistemas. Adotar Service Discovery traz compromissos sérios:
- Consistência Eventual vs. Disponibilidade: Se o servidor de descoberta demora a remover um nó morto, o Proxy pode enviar requisições para o limbo por alguns milissegundos.
- Complexidade Operacional: Agora você tem um novo “ponto crítico” (o Discovery Server). Se ele cair, novos serviços não podem entrar e a rede pode ficar cega.
- Latência de Inicialização: A aplicação agora depende da rede estar pronta para se registrar antes de poder receber tráfego real.
- Tráfego de Rede: Milhares de instâncias enviando heartbeats a cada 30 segundos geram uma carga constante de ruído na rede.
Alternativas ao Service Discovery
Se a complexidade de manter um Eureka ou Consul parece alta demais, existem caminhos alternativos:
- DNS Round Robin: Configurar múltiplos registros
Apara o mesmo domínio. Simples, mas sem health checks inteligentes (o DNS continuará entregando IPs de instâncias mortas até o TTL expirar). - Cloud Load Balancers (LBAAS): AWS ALB ou Google Cloud Load Balancer gerenciam o registro de instâncias automaticamente através de Target Groups. Você delega o Discovery para o provedor de nuvem.
- Configuração Estática via CI/CD: O seu pipeline de deploy atualiza o arquivo de configuração do Load Balancer a cada nova versão. Funciona bem para ambientes que não escalam automaticamente (Auto-scaling).
- Service Mesh (Istio/Linkerd): Leva o discovery para o nível da rede (L7). Cada serviço tem um Proxy (Sidecar) que conhece todos os outros serviços, eliminando a necessidade de lógica de discovery dentro do código da aplicação.
Aplicações Práticas
No mundo real, essa arquitetura é o que permite que empresas como a Netflix lidem com milhares de microserviços. Se o Kubernetes é o orquestrador que decide onde os containers rodam, o Service Discovery é o sistema de som que avisa para onde os dados devem fluir.
Takeaway Prático
Se você está começando um projeto com menos de 3 instâncias fixas, o Service Discovery pode ser um over-engineering. Mas, no momento em que você decide usar Auto-scaling (onde instâncias sobem sozinhas via métricas de CPU), o Service Discovery torna-se obrigatório. Sem ele, seu sistema é um carro veloz, mas sem volante.