

Software Architect
Pós-graduado em arquitetura de software e soluções. Conecto profundidade técnica com resultados de negócio para entregar produtos que as pessoas realmente usam. Também mentoro desenvolvedores e criadores em programas ao vivo, podcasts e iniciativas de comunidade focadas em tecnologia inclusiva.
Produtos gratuitos e pagos para transformar ideias em uma base que você consegue executar.
13 produtos disponíveisContinue explorando tópicos similares

The Twelve-Factor App remains a blueprint for scalable, maintainable, and cloud-native development — discover how each factor evolved to fit 2025’s tech landscape.

Designing APIs that match what clients actually need, not your internal architecture.

Um serviço lento pode derrubar toda sua arquitetura. O Circuit Breaker é o disjuntor que previne cascatas de falha, inspired em sistemas elétricos. Aprenda a implementar com Hystrix, Resilience4j e Polly.
Checklist de 47 pontos para encontrar bugs, riscos de segurança e problemas de performance antes do lançamento.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.
Em 2014, uma equipe de plataforma europeia mantinha 38 microservices atendendo 11 aplicações cliente (web, iOS, Android, três parceiros B2B, dois sistemas internos e quatro scripts de ingestão). Cada serviço implementava, isoladamente, JWT validation, throttling, auditoria, tracing, logs estruturados e rate limiting por tenant. O código era cópia com pequenas variações: um filtro Spring Security num serviço, um middleware Express noutro, uma annotation Go num terceiro. Quando uma vulnerabilidade crítica apareceu no jsonwebtoken@8.x em 2022, foram necessárias 11 noites para corrigir todas as instâncias, três deploys emergenciais causaram indisponibilidade e duas regressões passaram porque os testes de cada serviço não cobriam o caso "token com kid ausente". O pós-mortem apontou uma verdade cara: repetição de cross-cutting concerns não é "próxima" do código de negócio, é distância de negócio.
O API Gateway existe para resolver exatamente este problema. Ele concentra um conjunto finito de responsabilidades transversais (autenticação, autorização, throttling, roteamento, observabilidade, transformação, terminação de TLS, injeção de identidade) num único processo que fica na borda do sistema, à frente dos serviços. Os serviços de negócio param de tratar essas responsabilidades transversais como trabalho próprio e voltam a cuidar exclusivamente do domínio.
A literatura chama essas responsabilidades de cross-cutting concerns. O Microservices Patterns de Chris Richardson (2018) lista 14 responsabilidades comuns que aparecem em gateways maduros, mas a realidade é mais nuançada. Nem toda empresa precisa de todas as 14. Mais importante: muitas empresas pagam o preço de um gateway sem obter os benefícios, porque implementaram o padrão sem o problema. Em 2023, a Kong reportou que 27% das organizações que implantaram um gateway não tinham mais que cinco serviços no backend; metade delas acabou criando um monolito distribuído via gateway porque as rotas reproduziam fielmente os módulos do monolito antigo.
A regra pragmática é direta: o API Gateway faz sentido quando (a) o número de serviços internos cresce o suficiente para que código repetido de borda vire um problema real, (b) o número de clientes externos ou semiexternos (mobile, partners, B2B) é grande o bastante para que cada um exigiria uma fachada dedicada, e (c) a organização está disposta a operar o gateway com o mesmo nível de cuidado que dá aos serviços de receita.
Este artigo cobre os 15 pontos que importam para chegar a essa decisão com clareza e implementar com cuidado.
Antes de avançar, três confusões comuns que precisam ser esclarecidas:
Muitas equipes decidem "vamos repetir o auth em cada serviço porque é simples". O custo parece baixo. Mas há um efeito cumulativo que aparece depois:
O gateway não elimina a complexidade: ele a centraliza. A diferença é que complexidade centralizada tem dono, métricas, SLO e plano de manutenção. Complexidade distribuída tem 38 donose nenhum.
A maioria dos artigos sobre gateways apresenta uma lista de "10 responsabilidades mágicas". A realidade é menos glamorosa. Cada responsabilidade tem uma versão funcional e uma versão problemática. Esta seção apresenta as 10 mais comuns com honestidade sobre os trade-offs.
O gateway recebe HTTPS na borda, termina o TLS e fala HTTP/2 ou gRPC com os serviços internos. Parece trivial, mas tem três implicações que incomodam times sêniores:
Implementações modernas (Envoy, Kong, NGINX) suportam TLS 1.3, mTLS interno, e HSTS, OCSP stapling e CRL. A discussão que interessa é: onde termina o TLS do gateway e começa o TLS interno? Se o cluster Kubernetes inteiro roda com mTLS via service mesh, o gateway pode terminar o TLS externo e confiar no mesh para criptografar de novo até o pod. Se o cluster é mais simples, o gateway fala HTTP puro dentro do cluster, e aí mora o perigo.
O gateway mapeia https://api.example.com/v1/orders/{id} para o pod orders-svc no namespace correto, possivelmente reescrevendo o path, injetando cabeçalhos e validando parâmetros. A parte fácil é o roteamento. A parte difícil é a composição: /v1/checkout não bate em um serviço só, bate em três (orders, payments, inventory) e precisa agregar a resposta.
Existem dois caminhos:
A recomendação pragmática: comece com roteamento puro. Migre para BFF somente quando houver evidência de overhead de rede no cliente (medido em tempo total de carregamento, não em QPS do gateway).
O gateway valida tokens (JWT, OAuth2, opaque), verifica assinatura contra chaves públicas, valida claims obrigatórios (aud, iss, exp) e injeta identidade em cabeçalhos internos (X-User-Id, X-Tenant-Id). Em ambientes com múltiplos protocolos, também faz terminação de mTLS.
A armadilha: o gateway não deve ser a fonte de verdade sobre identidade. Ele verifica, mas a decisão de autorização é sempre do serviço que possui o recurso. Gateway que tenta centralizar lógica de autorização acaba virando um monolito de regras de negócio.
O gateway aplica regras simples: "este token pode chamar esta rota?". Decisões complexas (RBAC por recurso, ABAC, OPA, Cedar) vão para o serviço ou para um PDP dedicado. A recomendação atual: use o gateway para verificar que o JWT tem a claim certa e o caminho é permitido pelo escopo do token. Use um PEP dedicado (Open Policy Agent, Cerbos, oso) para decisões finas.
Aplica limites por cliente, por rota, por IP, por tenant. Detalhamento na seção 5.
Reescrita de path, normalização de cabeçalhos, conversão de protocolo (REST para gRPC, XML para JSON), masking de PII em logs, injeção de correlation IDs, compressão (gzip, brotli). A maioria dos gateways oferece isso, mas cada transformação customizada adiciona latência e dificulta upgrades.
Cache de respostas imutáveis (/v1/catalog/products/{id}) ou por TTL curto. Funciona bem para endpoints públicos com baixa cardinalidade de variação, mas exige invalidação explícita quando dados mudam. Cache no gateway é a fonte de bugs de "dados velhos" mais comum em arquiteturas de microservices. Só ative cache no gateway para endpoints onde você consegue garantir a invalidação.
O gateway emite access logs padronizados (LTSV, JSON) com latência, status, route, tenant, user ID, trace ID. Injeta traceparent (W3C Trace Context) para tracing distribuído. Detalhes na seção 7.
Red, USE, SLI: latência p50/p95/p99 por rota, taxa de erro por classe, saturação de upstream. Métricas por gateway, por cluster de gateway, por rota. Cardinalidade é o inimigo (seção 7.3).
Validação de payload contra OpenAPI/JSON Schema antes de encaminhar ao serviço. Bloqueia requests malformadas na borda, economiza CPU dos serviços, mas adiciona latência. Use para APIs públicas, pule para APIs internas com contratos fortes.
| Responsabilidade | Vale a pena? | Custo real | Quando evitar |
|---|---|---|---|
| Terminação TLS | Quase sempre | Baixo (a menos que PCI/HIPAA) | Tráfego < 1 RPS |
| Roteamento | Sempre | Baixo | Trivial em < 3 serviços |
| Autenticação | Quase sempre | Médio | Sistemas single-tenant |
| Autorização coarse | Sim, no gateway | Médio | Lógica ABAC complexa |
| Rate limiting | Sim | Médio | Tráfego humano, não API |
| Transformação | Cuidado | Alto latência | Múltiplas regras por rota |
| Cache | Casos específicos | Alto ops | Dados muito mutáveis |
| Logging | Sempre | Baixo | Nunca desative |
| Métricas | Sempre | Médio (cardinalidade) | Sempre |
| Validação schema | Para APIs externas | Médio | APIs internas com gRPC + protobuf |
| Composição/BFF | Casos específicos | Alto | Acopla serviços |
A próxima seção trata de quando o padrão, mesmo com responsabilidade clara, não compensa.
A decisão de adotar um API Gateway não é técnica. É econômica, operacional e organizacional. Esta seção apresenta critérios quantitativos e qualitativos.
| Anti-pattern | Sintoma | Consequência | Correção |
|---|---|---|---|
| Mega Gateway | Gateway faz 12 responsabilidades, inclusive lógica de negócio | Equipe de plataforma vira gargalo de toda entrega | Limite a 5-7 responsabilidades transversais |
| Gateway síncrono | Gateway faz chamada síncrona para enriquecer request e só então roteia | Latência soma, falhas se propagam, sem circuit breaker | Use cache local ou enriqueça no client |
| Gateway sem HA | Uma instância do gateway, failover manual | Outage total em deploy, update, falha de hardware | Mínimo 2 réplicas em zonas diferentes |
| Gateway sem upgrade | Versão fixada em 2022, plugins desatualizados | CVEs acumulados, perda de features | Plano de upgrade mensal, LTS ativo |
| Cache sem invalidação | Cache de 1h sem mecanismo de purge | Dados velhos para usuário final | Invalidação explícita + TTL curto + cache busting por evento |
| Gateway como monolito de regras | Lógica de pricing, fraude, scoring no gateway | Mudar regra de negócio exige deploy do gateway | Regras no serviço, gateway só verifica scope |
| Roteamento por string match | Regex complexa no path que cobre 80% dos casos | Casos de borda quebram, debug doloroso | Use ID por rota, prefix matching, nada de regex elaborado |
| Sem timeout | Gateway chama upstream sem timeout | Um serviço lento trava o gateway inteiro | Timeout agressivo (1-3s) com circuit breaker |
| Sem observabilidade | Logs em stdout, sem métricas | Primeiro outage vira caça às bruxas | Prometheus + tracing distribuído obrigatório |
| Plugins não versionados | Lua/Kong/Varnish/Envoy filter custom sem testes | Bug em produção que ninguém consegue reproduzir | Repositório com CI, testes de integração, code review |
A topologia define quem roda o gateway, onde, e como se relaciona com os serviços e o mesh. As três escolhas canônicas estão abaixo. Composições também são possíveis.
Modelo clássico: um cluster de gateways (2 a 12 réplicas, dependendo do volume) fica à frente de todos os serviços. Cada serviço é exposto apenas via gateway.
Internet
|
[LB / CDN]
|
+--+--+
| GW | <- cluster com 3+ replicas
+--+--+
|
+--+--+--+--+--+
| svc|svc|svc|svc|
+---+-+--+-+--+-+
Prós: ponto único de policy, fácil de operar, claro para audit, fácil de escalar horizontalmente.
Contras: ponto único de falha (mitigado com HA), hot path de toda chamada externa, latência extra, pode virar gargalo sob carga alta.
Quando usar: organização com time de plataforma dedicado, múltiplos clientes externos, requisitos claros de compliance.
Modelo: cada pod de serviço tem um sidecar (geralmente Envoy) que cuida de tráfego de saída e, opcionalmente, entrada. Istio, Linkerd e Consul implementam isso.
Internet
|
[LB]
|
+--+--+
| GW |
+--+--+
|
+--+--+--+--+
|svc|svc|svc|
+sxsx+sxsx+
| | |
(sidecars para east-west)
Prós: abstração uniforme para todos os serviços, service discovery, retry, circuit breaker, mTLS por padrão, observabilidade nativa.
Contras: adiciona latência por hop (mesmo dentro do cluster), overhead de CPU/memória por pod (10-30MB por sidecar), complexidade operacional, debugging exige entender o sidecar.
Quando usar: muitos serviços (50+), requisitos de mTLS universal, equipe com maturidade em Kubernetes e observabilidade.
A escolha mais comum em produção: o gateway centralizado cuida do tráfego norte-sul (externo para o cluster), e o service mesh cuida do tráfego leste-oeste (entre serviços internos).
Internet
|
[LB / CDN]
|
+--+--+
| GW | <- Kong / Envoy Gateway / NGINX
+--+--+
|
+--+--+--+--+
|svc|svc|svc|svc| <- cada um com sidecar
+--+sxsx+--+sxsx+
| |
comunicação mTLS via sidecars
Prós: defesa em profundidade, mTLS interno garantido, observabilidade distribuída rica.
Contras: dois sistemas para operar, duas superfícies de bug, latência extra em ambos os lados.
Quando usar: produção séria, ambientes regulados, times com capacidade para operar os dois.
Em algumas arquiteturas, especialmente serverless ou Function-as-a-Service, o gateway chama o serviço por service discovery sem passar por sidecars. Lambda, Cloud Run, FaaS em geral.
Prós: simplicidade operacional, billing por chamada.
Contras: visibilidade limitada, retry complexo, mTLS opcional.
Quando usar: sistemas event-driven, FaaS, baixo acoplamento.
| Critério | Centralizado | Sidecar | Mesh + Gateway | Edge proxy |
|---|---|---|---|---|
| Latência | Baixa | Média (por hop) | Média-alta | Baixa |
| Operacional | Médio | Alto | Muito alto | Baixo |
| mTLS interno | Manual | Automático | Automático | Não |
| Observabilidade | Boa | Excelente | Excelente | Básica |
| Custo (CPU/RAM) | Baixo-médio | Alto | Alto | Baixo |
| Risco operacional | Concentrado | Distribuído | Distribuído | Mínimo |
| Adequado para | 5-50 serviços | 20+ serviços | 50+ serviços | < 10 serviços ou FaaS |
A topologia centralizada é o caso comum. Sidecar puro é raro em gateways (é domínio de service mesh). Mesh + Gateway é o estado da arte para sistemas grandes. Edge proxy é para FaaS e sistemas leves.
Rate limiting, circuit breaking e retry são três primitivas de resiliência que costumam ser implementadas no gateway porque ele é o ponto de controle. Esta seção cobre os algoritmos, as configurações e os erros comuns.
Um balde virtual armazena tokens. Cada request consome um token. Tokens são repostos a uma taxa fixa. Permite bursts até o tamanho do balde.
# Kong plugin
plugins:
- name: rate-limiting
config:
minute: 60
hour: 1000
policy: local # ou "redis" para cluster
limit_by: credential
Vantagem: lida bem com tráfego em rajada. Desvantagem: sem token, request falha imediatamente, mesmo que a capacidade esteja ociosa.
Requests entram num balde e saem a uma taxa constante. Suaviza picos, mas tem latência adicional (a request espera na fila).
Vantagem: protege o upstream com curva constante. Desvantagem: introduz latência variável.
Mantém contadores em janelas deslizantes (por exemplo, últimos 60 segundos). Mais preciso que janela fixa, mais caro de calcular.
// Implementação sliding window com Redis
async function rateLimit(
key: string,
limit: number,
windowSec: number
): Promise<{ allowed: boolean; remaining: number }> {
const now = Date.now() / 1000;
const cutoff = now - windowSec;
// Remove timestamps antigos
await redis.zremrangebyscore(`rl:${key}`, 0, cutoff);
// Conta timestamps na janela
const count = await redis.zcard(`rl:${key}`);
if (count >= limit) {
return { allowed: false, remaining: 0 };
}
// Adiciona timestamp atual
await redis.zadd(`rl:${key}`, now, `${now}-${Math.random()}`);
await redis.expire(`rl:${key}`, windowSec + 1);
return { allowed: true, remaining: limit - count - 1 };
}
Vantagem: precisão. Desvantagem: storage cresce com a taxa, exigindo eviction agressiva.
Contador por janela fixa (minuto, hora). Simples. Permite burst no limite da janela (2x do limite em 2 segundos, no pior caso).
Vantagem: memória O(1). Desvantagem: imprecisão.
limite × número_de_replicas.A maioria das implantações usa Redis com sliding window. Para volumes muito altos (10k+ req/s por gateway), considere armazenar o estado no próprio Envoy com cache local + sincronização eventual.
Circuit breaker no gateway protege contra serviços internos que param de responder. Três estados: fechado (normal), aberto (falha, não chama upstream), semi-aberto (testa se voltou).
# Envoy cluster config
circuit_breakers:
thresholds:
- priority: DEFAULT
max_connections: 1024
max_pending_requests: 1024
max_requests: 1024
max_retries: 3
track_remaining: true
- priority: HIGH
max_connections: 1024
max_pending_requests: 1024
max_requests: 1024
max_retries: 3
outlier_detection:
consecutive_5xx: 5
interval: 30s
base_ejection_time: 30s
max_ejection_percent: 50
Os parâmetros críticos são consecutive_5xx (quantos erros consecutivos abrem o circuito) e max_ejection_percent (qual fração do cluster pode ser ejetada simultaneamente, prevenindo que o circuit breaker derrube o cluster inteiro).
Retry no gateway é perigoso se não for combinado com idempotência. Três regras:
Idempotency-Key.# Envoy retry policy
retry_policy:
retry_on: "5xx,gateway-error,reset,connect-failure,refused-stream"
num_retries: 3
per_try_timeout: 2s
retry_back_off:
base_interval: 0.1s
max_interval: 1s
retriable_status_codes: [503, 504]
O Idempotency-Key é um cabeçalho HTTP padrão (RFC) que o cliente envia e o servidor usa para deduplicar. Sem isso, retry em POSTs duplica cobranças, duplica pedidos, duplica envios de email.
Default conservador: 30s. Default sensato: 5s. Default agressivo: 1s para APIs internas, 3s para APIs externas.
# Envoy route config
timeout: 5s
max_stream_duration: 30s
A regra: timeout total < SLO do cliente. Se o cliente espera resposta em 3s e o gateway chama 4 serviços em série, cada serviço tem 700ms.
O gateway é o ponto natural para autenticação. Esta seção cobre JWT, OAuth2/OIDC, mTLS, API keys e integração com provedores de identidade.
JWT (JSON Web Token) é o padrão dominante. O gateway valida:
iss (issuer), aud (audience), exp (expiration), nbf (not before).scope, roles, tenant_id, user_id.alg deve ser um algoritmo permitido (rejeitar none, HS256 com chave pública).# Kong JWT plugin
plugins:
- name: jwt
config:
key_claim_name: kid
secret_is_base64: false
claims_to_verify:
- exp
- nbf
- iss
- aud
maximum_expiration: 3600
O IdP expõe JWKS (JSON Web Key Set) num endpoint. O gateway precisa cachear essas chaves para evitar lookup em cada request. TTL típico: 5 a 15 minutos. Cache mais agressivo aumenta risco de aceitar tokens com chaves revogadas; cache menos agressivo adiciona latência.
JWTs são stateless por design, então revogação imediata é difícil. Padrões comuns:
exp do token. Lookup em cada request.jti: claim jti única por token, revogação por jti.A maioria das empresas usa a combinação de tokens de curta duração (15min) com blocklist apenas para incidentes específicos. Tokens revogáveis totalmente (logout imediato) exigem state server-side.
OAuth2 é o framework de autorização. OIDC (OpenID Connect) é a camada de identidade em cima dele. O gateway geralmente implementa apenas o Resource Server (validação de access tokens). Authorization Server fica em provider separado (Auth0, Keycloak, Okta, AWS Cognito, Azure AD).
Fluxos comuns:
// Verificação JWT com JWKS em Node.js (exemplo didático)
import { jwtVerify, createRemoteJWKSet } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://idp.example.com/.well-known/jwks.json')
);
export async function verifyToken(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://idp.example.com',
audience: 'api.example.com',
algorithms: ['RS256', 'ES256'],
});
return payload;
}
mTLS (mutual TLS) garante que tanto cliente quanto servidor autenticam com certificados. Em gateways, é usado de duas formas:
# Envoy listener com mTLS
listeners:
- name: ingress_mtls
address:
socket_address:
address: 0.0.0.0
port_value: 443
filter_chains:
- transport_socket:
name: envoy.transport_sockets.tls
typed_config:
common_tls_context:
tls_certificate_sds_secret_configs:
- name: default
sds_config:
api_config_source:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: sds_cluster
validation_context_sds_secret_config:
name: validation_context
sds_config:
api_config_source:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: sds_cluster
A complexidade operacional de mTLS (PKI, rotação, revocação) é razão suficiente para usar service mesh, que cuida disso automaticamente com SPIFFE/SPIRE.
Para integrações B2B de baixo risco ou scripts de ingestão, API keys ainda são padrão. O gateway:
X-API-Key ou Authorization: ApiKey <key>.// Verificação de API key com cache
async function verifyApiKey(apiKey: string): Promise<ApiKeyContext> {
const cached = await redis.get(`apikey:${apiKey}`);
if (cached) {
return JSON.parse(cached);
}
const key = await db.apiKey.findUnique({
where: { value: hashApiKey(apiKey) },
include: { tenant: true, scopes: true },
});
if (!key || key.revoked) {
throw new UnauthorizedError('invalid_api_key');
}
const context = {
tenantId: key.tenantId,
scopes: key.scopes.map(s => s.name),
rateLimit: key.tenant.rateLimit,
};
await redis.set(
`apikey:${apiKey}`,
JSON.stringify(context),
'EX',
300 // 5 min cache
);
return context;
}
Após autenticação, o gateway injeta identidade em cabeçalhos para os serviços:
X-User-Id: ID do usuário (sub ou userId).X-Tenant-Id: tenant (multi-tenancy).X-Scopes: scopes separados por espaço.X-Request-Id: correlation ID para tracing.X-Forwarded-For: IP original.Os serviços internos nunca revalidam o token. Confiam nos cabeçalhos. Essa confiança só vale dentro do perímetro do cluster. Se um serviço for exposto acidentalmente fora do cluster, ele está comprometido.
Para integrações enterprise com SAML (frequentes em B2B), o gateway pode atuar como SAML SP (Service Provider) ou redirecionar para o IdP. Kong e Tyk têm plugins SAML, mas a recomendação é manter o SAML no IdP e expor OIDC no gateway (a maioria dos IdPs modernos converte SAML para OIDC).
Um gateway sem observabilidade é uma caixa preta que cai sem avisar. Esta seção cobre os três pilares, com foco em armadilhas específicas de gateway.
{
"timestamp": "2026-04-09T14:23:45.123Z",
"request_id": "01HV8ZP3K4XM9FQJ",
"trace_id": "5f3a8b9c1d2e4f6a8b9c1d2e4f6a8b9c",
"span_id": "1d2e4f6a8b9c1d2e",
"method": "POST",
"path": "/v1/orders",
"status": 201,
"latency_ms": 142,
"upstream": "orders-svc.cluster.local:8080",
"upstream_latency_ms": 118,
"user_id": "usr_8HqxVz",
"tenant_id": "tnt_4kVxJ",
"client_ip": "203.0.113.42",
"user_agent": "okhttp/4.12.0",
"bytes_in": 1247,
"bytes_out": 583,
"route": "orders.create",
"gateway_pod": "kong-7d4f8b9c-x2k7n"
}
Campos mínimos: timestamp, request_id, trace_id, method, path, status, latency_ms, upstream, user_id (ou hash), tenant_id, route. PII (email, nome, documento) nunca no log.
Métricas RED para cada rota:
Métricas USE para o gateway:
# p99 latência por rota no gateway
histogram_quantile(
0.99,
sum by (le, route) (
rate(gateway_request_duration_seconds_bucket[5m])
)
)
# Taxa de erro 5xx por upstream
sum by (upstream) (
rate(gateway_requests_total{status=~"5.."}[5m])
)
# Taxa de erro por tenant (cuidado com cardinalidade)
sum by (tenant_id) (
rate(gateway_requests_total{status=~"5.."}[5m])
)
W3C Trace Context é o padrão. O gateway injeta traceparent (e opcionalmente tracestate) em cada request. Cada serviço propaga ou cria novo span.
traceparent: 00-5f3a8b9c1d2e4f6a8b9c1d2e4f6a8b9c-1d2e4f6a8b9c1d2e-01
| | | |
v v v v
version trace-id (32) parent-id (16) flags
Implementações comuns: OpenTelemetry (OTel) SDK, Jaeger, Zipkin, Tempo, Datadog APM. OpenTelemetry é o padrão de fato desde 2021.
Cardinalidade é o número de séries temporais únicas. Cada combinação de label (route, status, tenant_id, etc.) gera uma série. Cardinalidade alta custa caro em Prometheus e degrada queries.
Regras:
user_id como label. Use hash ou agregue por tenant.tenant_id é arriscado. Em sistema com 10k tenants ativos, você tem 10k séries por métrica. Use apenas para top-N tenants e agregue o resto em other.route é obrigatório (sem route, métrica é inútil).status pode virar label (taxa de erro separada por classe).method é razoável (5-8 valores).A fórmula: cardinalidade = método × status × route × ... ≤ 100k é saudável em Prometheus single-tenant.
Cada span adiciona latência (1-5ms) e bytes (200-500B por span). Sampling é obrigatório em produção:
# OTel collector config
processors:
tail_sampling:
decision_wait: 10s
num_traces: 50000
expected_new_traces_per_sec: 1000
policies:
- name: errors
type: status_code
status_code:
status_codes: [ERROR]
- name: slow
type: latency
latency:
threshold_ms: 1000
- name: probabilistic
type: probabilistic
probabilistic:
sampling_percentage: 5
Em vez disso, meça: taxa de cache hit global, latência por rota, latência por upstream, taxa de erro por upstream.
| SLI | Definição | SLO razoável |
|---|---|---|
| Disponibilidade | (requests sem 5xx) / (requests total) | 99,95% |
| Latência p99 | p99 de latência de gateway | < 50ms (acima do upstream) |
| Latência p95 | p95 de latência de gateway | < 20ms |
| Taxa de 5xx | requests 5xx / total | < 0,05% |
| Erro de auth | requests 401+403 / total | < 1% (depende do tráfego legítimo) |
O gateway não controla latência do upstream, então o SLO de latência total precisa separar "latência do gateway" e "latência do upstream". A primeira é responsabilidade do time de plataforma; a segunda, do time do serviço.
A escolha do gateway depende de carga, equipe, vendor lock-in e custo total. Esta seção compara as cinco opções mais comuns com prós, contras e contexto de uso.
| Solução | Tipo | Modelo de execução | Linguagem de config | Pontos fortes | Pontos fracos |
|---|---|---|---|---|---|
| Kong | API Gateway | Open source + Enterprise | YAML/DB-less declarative | Ecossistema de plugins, fácil de operar, modo DB-less | Menos flexível que Envoy para regras complexas |
| Envoy | Proxy + Gateway | Open source | YAML bootstrap + xDS | Performance, flexível, base de Istio, filtros customizados em C++/WASM | Curva de aprendizado, config verbosa |
| Zuul | API Gateway (Netflix) | Open source | Java/Groovy | Integração natural com stack JVM, filters dinâmicos | Menos usado fora de Netflix, manutenção comunidade menor |
| NGINX | Reverse proxy + Gateway | Open source + Plus | nginx.conf | Maduro, performante, conhecido | Pouca lógica de gateway sem módulos pagos |
| AWS API Gateway | Managed | Serviço gerenciado | JSON/Swagger/OpenAPI | Zero operação, integração nativa AWS, billing | Vendor lock-in, custo em escala, limites de timeout 29s |
| Apigee (Google) | API Management | Managed | Policies + proxies | API Management completo, portal dev | Caro, foco enterprise, lock-in |
| Tyk | API Gateway | Open source + Cloud | YAML/Tyk OAS | Open source maduro, dashboard bom | Comunidade menor que Kong |
Kong é o API Gateway open source mais adotado em 2026. Roda em OpenResty (NGINX + Lua), com plugins em Lua, Go ou Python (via plugin server externo). Suporta DB-less mode (configuração em YAML, sem Postgres) ou com Postgres para runtime config.
# Kong declarative config (db-less)
_format_version: "3.0"
services:
- name: orders-service
url: http://orders-svc.orders.svc.cluster.local:8080
routes:
- name: orders-route
paths:
- /v1/orders
methods:
- GET
- POST
- PUT
- DELETE
plugins:
- name: jwt
- name: rate-limiting
config:
minute: 100
policy: local
- name: prometheus
- name: payments-service
url: http://payments-svc.payments.svc.cluster.local:8080
routes:
- name: payments-route
paths:
- /v1/payments
Quando Kong é a resposta certa:
Quando Kong não é ideal:
Envoy é o proxy de borda open source usado por Lyft (criador), Istio (service mesh), e diversas empresas. Mais baixo nível que Kong, mais flexível. Configuração via YAML bootstrap, complementada por xDS (Discovery Service) para config dinâmica.
# Envoy listener bootstrap
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/v1/orders"
route:
cluster: orders_cluster
timeout: 5s
retry_policy:
retry_on: "5xx,gateway-error"
num_retries: 3
- match:
prefix: "/v1/payments"
route:
cluster: payments_cluster
http_filters:
- name: envoy.filters.http.router
Quando Envoy é a resposta certa:
Quando Envoy não é ideal:
Zuul é o gateway da Netflix, escrito em Java. Integrado ao stack Spring Cloud Netflix. Filters dinâmicos em Groovy. Hoje é menos ativo que Kong ou Envoy, mas ainda usado em empresas com stack JVM.
Quando Zuul é a resposta certa:
Quando não usar:
NGINX é o reverse proxy mais usado do mundo. Como API Gateway, suporta:
nginx-jwt.# NGINX como API Gateway
upstream orders_backend {
server orders-svc:8080;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/certs/api.example.com.crt;
ssl_certificate_key /etc/nginx/certs/api.example.com.key;
location /v1/orders {
limit_req zone=orders_limit burst=20 nodelay;
proxy_pass http://orders_backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Request-ID $request_id;
}
}
Quando NGINX é a resposta certa:
Quando não usar:
Serviço gerenciado da AWS. Suporta REST APIs, HTTP APIs e WebSockets. HTTP APIs é a versão mais barata e suficiente para a maioria dos casos.
Prós: zero operação, integração nativa com Lambda, Cognito, IAM.
Contras: vendor lock-in forte, custo em escala ($1-3 por milhão de requests + data transfer), limite de 29 segundos de timeout (problemático para integrações síncronas longas), customização limitada.
# AWS API Gateway HTTP API com OpenAPI
openapi: "3.0.1"
info:
title: orders-api
paths:
/v1/orders:
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri: http://orders-svc.orders.svc.cluster.local:8080/v1/orders
Quando AWS API Gateway é a resposta certa:
Quando não usar:
| Critério | Kong | Envoy | Zuul | NGINX | AWS API GW |
|---|---|---|---|---|---|
| Open source | Sim | Sim | Sim | Sim | Não |
| Custo de operação | Médio | Médio-alto | Médio | Baixo | Zero (paga por request) |
| Performance | Boa | Excelente | Boa | Excelente | Média |
| Flexibilidade | Alta | Máxima | Alta (JVM) | Média | Baixa |
| Curva de aprendizado | Média | Alta | Média (Java) | Baixa | Baixa |
| Vendor lock-in | Não | Não | Não | Não | Sim |
| Ecossistema de plugins | Grande | Médio (WASM/Go) | Médio (Groovy) | Grande (módulos) | Limitado |
| Adequado para escala | Sim | Sim | Sim | Sim | Caro |
| Adequado para multi-cloud | Sim | Sim | Sim | Sim | Não |
| Documentação | Boa | Boa | Média | Excelente | Excelente |
| Comunidade em 2026 | Grande | Grande | Média | Enorme | AWS-only |
A recomendação pragmática: Kong para o caso comum, Envoy para service mesh e alta performance, NGINX para simplicidade, AWS API Gateway para stack AWS puro com Lambda.
Esta seção mostra uma implementação real de Kong em Kubernetes, com Helm, plugins essenciais e configuração GitOps.
helm repo add kong https://charts.konghq.com
helm repo update
helm install kong kong/kong \
--namespace kong \
--create-namespace \
--set ingressController.enabled=true \
--set ingressController.installCRDs=true \
--set proxy.type=LoadBalancer \
--set env.database=off \
--set env.plugins=rate-limiting,jwt,prometheus,cors \
--set replicaCount=3
Modo DB-less (database=off) usa configuração declarativa em YAML, ideal para GitOps. Kong recarrega config sem restart.
Kong expõe CRDs (Custom Resource Definitions) para gerenciar serviços, rotas e plugins via Kubernetes.
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: global-rate-limit
namespace: kong
labels:
global: "true"
plugin: rate-limiting
config:
minute: 1000
hour: 50000
policy: redis
redis_host: redis-master.default.svc.cluster.local
redis_port: 6379
redis_database: 0
limit_by: credential
hide_client_headers: false
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: jwt-validation
namespace: kong
labels:
global: "true"
plugin: jwt
config:
key_claim_name: kid
claims_to_verify:
- exp
- iss
- aud
maximum_expiration: 3600
---
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: prometheus-exporter
namespace: kong
labels:
global: "true"
plugin: prometheus
config:
status_code_metrics: true
latency_metrics: true
bandwidth_metrics: true
upstream_health_metrics: true
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: orders-ingress
namespace: orders
annotations:
konghq.com/strip-path: "true"
konghq.com/plugins: "jwt-validation,rate-limit-orders"
konghq.com/protocols: "https"
konghq.com/https-only: "true"
spec:
ingressClassName: kong
rules:
- host: api.example.com
http:
paths:
- path: /v1/orders
pathType: Prefix
backend:
service:
name: orders-svc
port:
number: 8080
tls:
- hosts:
- api.example.com
secretName: api-example-com-tls
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: rate-limit-orders
namespace: orders
plugin: rate-limiting
config:
minute: 200
hour: 10000
policy: redis
redis_host: redis-master.default.svc.cluster.local
limit_by: consumer
Kong identifica o consumer pelo claim sub do JWT ou pelo header X-API-Key. Aplica rate limit por consumer, permitindo que tenants premium tenham limite mais alto (configurado no plugin por chave).
Configuração de log shipping via Fluent Bit para Loki:
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: logging
data:
fluent-bit.conf: |
[INPUT]
Name tail
Path /var/log/containers/kong-*.log
Parser docker
Tag kong.*
Refresh_Interval 5
[FILTER]
Name kubernetes
Match kong.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Merge_Log On
[OUTPUT]
Name loki
Match *
Host loki.logging.svc.cluster.local
Port 3100
Labels job=kong
Métricas Prometheus via Kong plugin:
apiVersion: v1
kind: ServiceMonitor
metadata:
name: kong-monitor
namespace: kong
spec:
selector:
matchLabels:
app.kubernetes.io/name: kong
endpoints:
- port: metrics
interval: 30s
path: /metrics
apiVersion: configuration.konghq.com/v1
kind: KongUpstreamPolicy
metadata:
name: orders-health
namespace: orders
spec:
algorithm: round-robin
healthchecks:
threshold: 5
active:
healthy:
interval: 10
successes: 2
unhealthy:
interval: 5
http_failures: 3
tcp_failures: 3
timeouts: 3
http_path: /healthz
timeout: 1
concurrency: 2
Git repo (config/)
├── kong/
│ ├── plugins/
│ │ ├── jwt.yaml
│ │ ├── rate-limit.yaml
│ │ └── prometheus.yaml
│ ├── ingresses/
│ │ ├── orders.yaml
│ │ ├── payments.yaml
│ │ └── users.yaml
│ └── secrets/
│ └── tls.yaml (encrypted with SOPS)
└── argocd-app.yaml
ArgoCD aplica os manifests. Mudanças em PR passam por review. Kong detecta mudanças em CRDs e atualiza config sem reiniciar pods.
Envoy é mais baixo nível. Esta seção cobre configuração como gateway (não como sidecar de mesh), com rate limit service e filtros customizados.
Em 2026, o time do Envoy mantém dois caminhos:
A recomendação: comece com Envoy Gateway via CRDs. Migre para bootstrap direto quando precisar de recursos não cobertos pelos CRDs.
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.2.0 \
--namespace envoy-gateway-system \
--create-namespace
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: api-gateway
namespace: envoy-gateway-system
spec:
gatewayClassName: envoy
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: api-example-com-tls
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: orders-route
namespace: orders
spec:
parentRefs:
- name: api-gateway
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /v1/orders
backendRefs:
- name: orders-svc
port: 8080
timeouts:
request: 5s
backendRequest: 3s
retries:
attempts: 3
perTryTimeout: 1s
retryOn:
- 5xx
- gateway-error
- connect-failure
Envoy não tem rate limit embutido por padrão (até a v1.30 não tem como parte do core). Usa um serviço externo:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: rate-limited-proxy
namespace: envoy-gateway-system
spec:
rateLimit:
backend:
type: TCP
tcp:
address: ratelimit-service.default.svc.cluster.local:8081
O rate limit service é um binário Go (envoyproxy/ratelimit) que mantém estado em Redis. Configuração via YAML:
# ratelimit-config.yaml
domain: api_gateway
descriptors:
- key: path
descriptors:
- key: remote_address
descriptors:
- key: path
value: /v1/orders
rate_limit:
unit: minute
requests_per_unit: 100
- key: path
value: /v1/payments
rate_limit:
unit: minute
requests_per_unit: 60
- key: authenticated
value: "true"
descriptors:
- key: subject
descriptors:
- key: path
rate_limit:
unit: minute
requests_per_unit: 1000
Envoy suporta filtros em C++ (compilados), Go (via plugin service), WASM ou Lua. Para a maioria dos casos, Lua é suficiente:
-- Filtro Lua para adicionar correlation ID se ausente
function envoy_on_request(request_handle)
local headers = request_handle:headers()
local rid = headers:get("x-request-id")
if rid == nil then
rid = generate_uuid()
headers:add("x-request-id", rid)
end
request_handle:logInfo(string.format("rid=%s method=%s path=%s",
rid, headers:get(":method"), headers:get(":path")))
end
Para filtros mais complexos, WASM com Go ou Rust:
// Filtro WASM em Go (proxy-wasm)
package main
import (
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
)
func main() {
proxywasm.SetVMContext(&vmContext{})
}
type vmContext struct{}
func (*vmContext) OnVMStarted(int) {
proxywasm.LogInfo("wasm filter started")
}
func (vc *vmContext) NewPluginContext(uint32) types.PluginContext {
return &pluginContext{}
}
type pluginContext struct{}
func (*pluginContext) OnPluginStart(int) {
proxywasm.LogInfo("plugin started")
}
func (ctx *pluginContext) NewHttpContext(uint32) types.HttpContext {
return &httpContext{}
}
type httpContext struct{}
func (h *httpContext) OnHttpRequestHeaders(int, bool) types.Action {
headers, _ := proxywasm.GetHttpRequestHeaders()
rid := headers.Get("x-request-id")
if rid == "" {
rid = generateUUID()
proxywasm.AddHttpRequestHeader("x-request-id", rid)
}
proxywasm.LogInfof("request %s %s rid=%s",
headers.Get(":method"),
headers.Get(":path"),
rid)
return types.ActionContinue
}
func (h *httpContext) OnHttpResponseHeaders(int, bool) types.Action {
return types.ActionContinue
}
func generateUUID() string {
return "01HV" + randomHex(28)
}
func randomHex(n int) string {
// implementação omitida
return ""
}
Filtros WASM são compilados para .wasm e distribuídos como ConfigMap ou Image.
Envoy pode configurar mTLS no cluster contra upstream:
clusters:
- name: orders_cluster
type: STRICT_DNS
load_assignment:
cluster_name: orders_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: orders-svc.orders.svc.cluster.local
port_value: 8080
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
common_tls_context:
tls_certificate_sds_secret_configs:
- name: spiffe_cert
sds_config:
api_config_source:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: spiffe_agent
validation_context_sds_secret_config:
name: validation_context
sds_config:
api_config_source:
api_type: GRPC
grpc_services:
- envoy_grpc:
cluster_name: spiffe_agent
A combinação Envoy Gateway + SPIFFE/SPIRE oferece identidade forte para todos os serviços sem gerenciar PKI manualmente.
A pergunta mais comum em times que herdaram monolito: "como adiciono um gateway sem quebrar tudo?". Esta seção cobre o caminho seguro.
O nome vem de Martin Fowler (2004). Em vez de reescrever o monolito, coloca-se o gateway na frente e migra-se uma rota por vez.
Antes:
[Cliente] -> [Monolito]
Depois (gradual):
[Cliente] -> [Gateway] -> [Monolito] (rotas antigas)
\-> [Microsserviço] (rotas migradas)
Etapas:
Se o monolito tem 4 tipos de clientes (web, iOS, Android, partner) com payloads diferentes, a estratégia é criar um BFF (Backend for Frontend) por cliente, cada um implementado em cima do gateway:
[Web] -> [BFF Web] -> [Gateway]
[iOS] -> [BFF Mobile] -> [Gateway]
[Partner] -> [BFF B2B] -> [Gateway]
|
v
[Monolito / Microservices]
Vantagem: cada cliente tem um ponto de adaptação dedicado, sem poluir o gateway com lógica de cliente. Desvantagem: mais serviços para manter.
Em vez de routing por path, em sistemas legados pode ser necessário rotear por header durante a transição:
# Kong route com match por header
routes:
- name: legacy-route
paths:
- /v1/users
headers:
- "X-Client-Version:legacy"
- name: new-route
paths:
- /v1/users
headers:
- "X-Client-Version:v2"
Clientes migrados enviam o header novo. Demais continuam na rota legada.
O gateway envia uma cópia do request para o novo serviço sem afetar a resposta ao cliente. Compara as respostas (status, latency, body hash) e alerta sobre divergências.
# Kong shadow plugin
plugins:
- name: shadow
config:
target_uri: http://users-v2-svc.users.svc.cluster.local:8080
disable_body_shadowing: false
Cuidado: shadow gera carga real no novo serviço. Se o endpoint v2 tem efeito colateral (cobrança, email, banco), shadow breakage potencial. Para esses casos, use dados sintéticos.
Migração de monolito para microservices geralmente quebra sessões porque o cookie de sessão do monolito não é reconhecido pelos serviços novos. Soluções:
O novo gateway precisa falar com o monolito. Em sistemas legados, isso pode ser HTTP/1.1 com payloads grandes, XML ou SOAP. O gateway atua como adaptador:
Plugins prontos em Kong (XML-to-JSON, SOAP) facilitam. Em Envoy, filtro customizado é necessário.
A regra: migrar de monolito para microservices só vale a pena quando o monolito está limitando a velocidade da equipe. Se o time está entregando rápido no monolito, mantenha-o.
Esta seção lista os anti-patterns mais frequentes em implantações reais. Cada um vem com sintoma, consequência e exemplo.
Sintoma: o gateway implementa 15 responsabilidades, incluindo lógica de negócio. Time de plataforma virou gargalo de toda entrega.
Consequência: deploy de mudança em pricing exige deploy do gateway. Mudança de feature em serviço exige aprovação do time de plataforma.
Correção: extrair lógica de negócio para serviços. Gateway mantém apenas cross-cutting concerns.
Sintoma: para cada request, o gateway chama 2-3 serviços internos antes de rotear para o serviço alvo, agregando dados no header.
Consequência: latência somada, três pontos de falha por request, debug doloroso.
Correção: o serviço alvo busca o que precisa de outros serviços, ou usa event-driven para enriquecimento assíncrono.
Sintoma: uma réplica do gateway. Failover manual.
Consequência: cada deploy causa outage. Cada crash, recuperação de 15-30 minutos.
Correção: mínimo 3 réplicas em 2 zonas de availability. PDB (PodDisruptionBudget) agressivo. Anti-affinity para distribuir pods.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: kong-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: kong
Sintoma: Kong 2.8.0 rodando em 2026, plugins desatualizados, CVEs acumulados.
Consequência: incidente de segurança explorando CVE conhecido. Vendor não fornece mais patches.
Correção: ciclo de upgrade trimestral. Testar nova versão em cluster de staging. Plano de rollback documentado.
Sintoma: cache de 1 hora no gateway, sem mecanismo de purge, dados mudam constantemente.
Consequência: usuário vê preço de produto errado, status de pedido desatualizado, saldo de conta incorreto.
Correção: invalidação explícita via webhook do serviço que altera o dado. TTL máximo conservador. Cache busting por tag ou chave de versão.
// Padrão de invalidação
async function updateProduct(id: string, data: ProductData) {
await db.product.update({ where: { id }, data });
await cache.del(`product:${id}`);
await gateway.cache.purge(`/v1/products/${id}`); // chama webhook
}
Sintoma: gateway chama upstream sem timeout configurado. Default do NGINX é 60s, do Kong é 0 (sem timeout).
Consequência: um serviço lento trava threads do gateway, degrada todo o tráfego.
Correção: timeout agressivo por rota, sempre. Default sugerido: 5s para APIs externas, 1-3s para internas.
Sintoma: rotas usam regex complexa com 5 grupos de captura e lookarounds.
Consequência: bugs sutis, performance ruim, debug impossível.
Correção: prefix matching, ID por rota, nada de regex. Kong e Envoy suportam prefix, exact, regex, mas regex deve ser evitado.
Sintoma: Lua filter custom em Kong, sem testes de integração.
Consequência: bug em produção que ninguém consegue reproduzir, downtime até resolver.
Correção: cada plugin custom em repositório com CI, testes de integração, code review obrigatório.
Sintoma: empresa trata o gateway como ponto único de auditoria de segurança. "Se passou pelo gateway, está auditado".
Consequência: bypass no gateway (acesso direto ao pod) passa despercebido. Comprometimento de um pod não é detectado.
Correção: defesa em profundidade. Gateway é uma camada, não a única. mTLS interno, network policies, audit logs em todos os serviços críticos.
Sintoma: dashboards bonitos com 50 métricas, ninguém olha.
Consequência: outage em produção, métrica mostrava o problema há 2 horas, ninguém viu.
Correção: cada métrica tem alerta. Cada alerta tem runbook. Cada runbook tem dono.
Sintoma: IdP tem uma chave de assinatura. JWTs válidos até expirar.
Consequência: chave comprometida exige invalidar todos os JWTs em circulação. Usuários deslogados forçadamente.
Correção: IdP com rotação periódica de chaves (90 dias). JWKS endpoint expõe múltiplas chaves. Gateway aceita kid atual e anterior durante transição.
Sintoma: rate limit apenas para tráfego externo. Serviços internos podem ser chamados em loop sem qualquer proteção.
Consequência: bug em serviço chama outro serviço em loop, derruba dependente.
Correção: rate limit também para east-west traffic, ou service mesh com circuit breaker.
Implementar um gateway em produção exige cuidado. Esta seção apresenta o checklist dividido em três fases.
[ ] Decisão documentada: por que o gateway? Qual o problema?
[ ] Responsabilidades definidas: o que vai entrar, o que não vai.
[ ] Topologia definida: centralizado, sidecar, mesh.
[ ] Escolha de tecnologia justificada: Kong, Envoy, NGINX, AWS.
[ ] Estimativa de carga: RPS esperado, latência adicional tolerada.
[ ] Estimativa de custo: infraestrutura, operação, licenças.
[ ] Equipe designada: donos do gateway, on-call rotation.
[ ] SLO definido: disponibilidade, latência.
[ ] Plano de HA: réplicas, zonas, failover testado.
[ ] Plano de upgrade: como atualizar sem outage.
[ ] Plano de rollback: como voltar atrás se algo falhar.
[ ] Observability stack: métricas, logs, tracing prontos.
[ ] Alertas configurados: cada métrica crítica tem alerta.
[ ] Runbooks escritos: o que fazer em cada cenário de falha.
[ ] Testes de carga executados: sintético, em staging, antes de produção.
[ ] Chaos engineering: testes de falha (kill -9, network partition, latency injection).
[ ] Security review: pentest, CVE scan, configurações validadas.
[ ] Compliance review: LGPD, PCI, HIPAA conforme aplicável.
[ ] Documentação: como desenvolver, como debugar, como operar.
[ ] Canary 1%: 24h de observação.
[ ] Canary 5%: 24h.
[ ] Canary 25%: 24h.
[ ] Canary 50%: 24h.
[ ] Canary 100%: gradual, com shadow ainda ativo.
[ ] Shadow desativado após 7 dias sem divergências.
[ ] Métricas comparadas com baseline pré-gateway.
[ ] SLO atingido (disponibilidade, latência).
[ ] Alertas funcionando.
[ ] Dashboards atualizados.
[ ] On-call treinado no gateway.
[ ] Post-mortem agendado para 30 dias depois.
[ ] Métricas de negócio inalteradas ou melhoradas.
[ ] Latência total dentro do esperado.
[ ] Taxa de erro inalterada ou melhorada.
[ ] Time de plataforma operando o gateway confortavelmente.
[ ] Documentação atualizada com lições aprendidas.
[ ] Templates de plugin publicados para outros times.
[ ] Onboarding criado para novos engenheiros.
[ ] Roadmap de evolução definido (próximas features, upgrades).
[ ] Custos reais medidos vs estimativa inicial.
[ ] Dependências críticas (Redis, IdP) com plano de contingência.
Três decisões precisam de dados concretos. Esta seção define as métricas que importam.
| Métrica | Threshold para adotar | Como medir |
|---|---|---|
| Número de serviços internos | ≥ 8 | Contagem simples |
| Número de clientes externos diferentes | ≥ 3 | Tipos distintos (web, mobile, partner, B2B) |
| Linhas duplicadas de cross-cutting concerns | ≥ 1000 | Soma de código de auth, rate limit, logging nos serviços |
| Tempo gasto em mudanças de cross-cutting | ≥ 1 pessoa-mês/mês | Tracking de issues |
| Incidentes recentes por auth/rate limit duplicado | ≥ 1 por trimestre | Post-mortems |
| Métrica | Threshold para sidecar/mesh |
|---|---|
| Número de serviços | ≥ 50 |
| Latência entre serviços internos | Sensível (P99 < 50ms) |
| Requisitos de mTLS interno | Obrigatório |
| Maturidade da equipe K8s | Alta |
| Critério | Kong | Envoy | NGINX | AWS API GW |
|---|---|---|---|---|
| Plugin pronto necessário | ✓ | ✗ | parcial | parcial |
| Service mesh presente | ✗ | ✓ | ✗ | ✗ |
| Multi-cloud | ✓ | ✓ | ✓ | ✗ |
| Lambda backend | parcial | parcial | parcial | ✓ |
| Performance crítica | parcial | ✓ | ✓ | ✗ |
| Self-hosted | ✓ | ✓ | ✓ | ✗ |
API Gateway não é um padrão para adotar "porque todo mundo adota". É uma decisão econômica: faz sentido quando o custo de centralizar responsabilidades transversais é menor que o custo de mantê-las distribuídas.
A escolha certa depende de:
A armadilha mais comum: implementar gateway como monolito de regras de negócio. A segunda mais comum: operar sem HA e descobrir a falha durante o primeiro deploy.
O caminho seguro é: documento o problema antes de implementar; defina 5-7 responsabilidades; escolha a topologia pelo número de serviços e maturidade da equipe; implemente com HA desde o dia 1; meça SLOs desde o dia 1; trate o gateway como produto, não como side project.
Se você chegou até aqui, você tem clareza para tomar a decisão certa. O resto é execução.