O Timeout que funcionava na minha máquina: como diagnosticamos SNAT Port Exhaustion no Azure
Semana passada tivemos instabilidade em uma das nossas aplicações. Timeouts intermitentes, usuários reclamando, e o pior: nenhuma mudança recente no código.
Se você já passou por isso, sabe o desespero. Quando não há deploy recente para culpar, a investigação pode ir para qualquer direção.
A Pista Falsa
Nas análises iniciais, encontramos alguns locks no banco de dados. As tabelas envolvidas eram justamente as da mesma rota que estava causando os timeouts. Parecia fazer sentido: locks no banco → queries lentas → timeout.
Seguimos essa direção. Analisamos queries, índices, planos de execução. Gastamos tempo precioso.
Lição aprendida: correlação não é causação. Só porque dois problemas aparecem juntos não significa que um causa o outro.
A Sala de Guerra
Hoje pela manhã, como o problema persistia, montamos uma sala de guerra com os envolvidos. Às vezes é isso que precisamos: parar, respirar, e olhar o problema com mais calma.
Ao analisar os logs novamente, percebi algo que tinha passado despercebido: o timeout não era no banco. Era na chamada de uma API externa.
Pontuei isso para o time, e então veio a frase que mudou tudo:
"Mas quando eu chamo direto da minha máquina eu consigo gerar várias vezes sem erro. O problema parece ser apenas no Azure."
BINGO.
Quando algo funciona local mas falha no Azure, especialmente em App Service, existe um suspeito clássico: SNAT Port Exhaustion.
O que é SNAT Port Exhaustion?
Quando seu App Service precisa fazer uma chamada de saída (para uma API externa, por exemplo), ele não usa seu próprio IP. O Azure usa SNAT (Source Network Address Translation) para rotear o tráfego através de um pool compartilhado de IPs e portas.
O problema é que esse pool é limitado. Cada instância do App Service tem aproximadamente 128 portas SNAT disponíveis para conexões com o mesmo endpoint de destino.
Pense assim:
- Sua aplicação precisa chamar
api.exemplo.com:443 - Cada conexão TCP ativa usa uma porta SNAT
- Se você abrir muitas conexões simultâneas (ou não fechá-las corretamente), as portas acabam
- Novas conexões falham com timeout
Por que funciona na sua máquina? Porque seu computador tem seu próprio pool de portas efêmeras (geralmente 16.000+). No App Service, você compete por um pool compartilhado e muito menor.
Como Diagnosticar
Agora que sabemos o que procurar, vamos às ferramentas.
1. Azure Monitor Metrics
No portal do Azure, acesse seu App Service → Metrics e adicione:
- SNAT Connection Count: número de conexões SNAT ativas
- SNAT Connection Failed Count: conexões que falharam por falta de portas
Se você vê Failed Count subindo, é confirmação do problema.
2. Diagnose and Solve Problems
No blade do App Service, clique em Diagnose and solve problems. O Azure tem detectores automáticos para SNAT exhaustion:
- Procure por "TCP Connections" ou "SNAT"
- O detector mostra gráficos de conexões por estado (Established, Time Wait, etc.)
- Muitas conexões em TIME_WAIT indicam que conexões não estão sendo reutilizadas
3. Application Insights
Se você usa Application Insights, analise:
- Dependency calls: procure por chamadas com alta latência ou falhas para o mesmo endpoint
- Performance: veja se há correlação entre picos de requisições e timeouts nas dependências
- Failures: filtre por exceptions de timeout em chamadas HTTP
4. Kudu / Advanced Tools
Para uma análise mais profunda:
- Acesse
https://seuapp.scm.azurewebsites.net - Vá em Debug console → CMD
- Execute:
netstat -ano | findstr ESTABLISHED
Muitas conexões para o mesmo IP externo? Provavelmente SNAT exhaustion.
Causas Comuns
O problema geralmente está no código:
- HttpClient instanciado diretamente: cada
new HttpClient()pode criar uma nova conexão. UseIHttpClientFactory. - Conexões não descartadas: se você não fecha conexões corretamente, elas ficam em TIME_WAIT por até 4 minutos.
- Alto volume para mesmo endpoint: mesmo com código correto, um volume muito alto de chamadas para a mesma API pode esgotar as portas.
Corrigir o código é importante, mas às vezes você precisa de uma solução de infraestrutura.
A Solução: VNet Integration + NAT Gateway
Para resolver definitivamente o problema de SNAT exhaustion, a solução recomendada pela Microsoft é usar VNet Integration com NAT Gateway.
Por que funciona?
Na arquitetura padrão, seu App Service usa o pool compartilhado de SNAT do Azure Load Balancer. É limitado e você não tem controle.
Com NAT Gateway:
- Cada IP público do NAT Gateway fornece 64.000 portas SNAT
- Você pode ter até 16 IPs públicos = mais de 1 milhão de portas
- As portas são alocadas sob demanda, não fixas por instância
- Você tem controle sobre o timeout (4-120 minutos)
Arquitetura
App Service → VNet Integration → Subnet → NAT Gateway → Internet/API Externa
O tráfego de saída do App Service passa pela sua VNet e sai pelo NAT Gateway, que tem um pool de portas muito maior.
Implementação
-
Criar uma VNet com uma subnet dedicada para integração
- A subnet precisa ter pelo menos /26 (64 endereços)
- Essa subnet será usada exclusivamente pelo App Service
-
Criar o NAT Gateway
- Crie um IP público (ou mais, se precisar de mais portas)
- Crie o NAT Gateway e associe o IP público
-
Associar o NAT Gateway à subnet
- Na configuração da subnet, selecione o NAT Gateway criado
-
Habilitar VNet Integration no App Service
- App Service → Networking → VNet Integration
- Selecione a VNet e subnet criadas
-
Rotear todo tráfego pela VNet
- Em Configuration, adicione:
WEBSITE_VNET_ROUTE_ALL=1 - Isso garante que TODO tráfego de saída use o NAT Gateway
- Em Configuration, adicione:
Considerações de Custo
NAT Gateway tem custo:
- Por hora de provisionamento (~R$ 0,20/hora)
- Por GB de dados processados (~R$ 0,20/GB)
Para aplicações com alto volume de chamadas externas, o custo se paga pela estabilidade. Compare com o custo de downtime e horas de investigação.
Lições Aprendidas
-
Quando funciona local mas não no Azure → pense em limites da plataforma. SNAT exhaustion é o exemplo clássico, mas existem outros (file handles, threads, etc.).
-
Não confie na primeira correlação. Os locks no banco eram reais, mas não eram a causa. Investigue até ter certeza.
-
Sala de guerra funciona. Às vezes você precisa de múltiplas perspectivas olhando o mesmo problema.
-
Soluções de código e infraestrutura são complementares. Use
IHttpClientFactoryE considere NAT Gateway. Um não substitui o outro.
Referências
Para se aprofundar no tema e implementar a solução:
Diagnóstico e Troubleshooting
- Troubleshoot intermittent outbound connection errors in Azure App Service — Guia oficial da Microsoft para diagnosticar SNAT exhaustion
VNet Integration
- Integrate your app with an Azure virtual network — Documentação completa sobre VNet Integration no App Service
- Tutorial: Connect to Azure services securely using VNet integration — Tutorial passo a passo
NAT Gateway
- What is Azure NAT Gateway? — Visão geral do serviço
- Tutorial: Create a NAT gateway using the Azure portal — Como criar via portal
- Design virtual networks with NAT gateway — Considerações de arquitetura
Boas Práticas de Código
- Use IHttpClientFactory to implement resilient HTTP requests — Como usar HttpClientFactory corretamente
- Manage connections in Azure Functions — Também aplicável a App Services
Já enfrentou SNAT exhaustion? Compartilhe sua experiência nos comentários.