Tráfego de saída no Kubernetes e proxy: sidecar, egress e NO_PROXY
Sumário do artigo
- Fundamentos: por que no cluster tudo é diferente
- Mergulho profundo: os três níveis onde o proxy vive
- Variáveis de ambiente no manifesto e o papel do no_proxy
- O que não captura variáveis de ambiente
- Segredos: credenciais de proxy via secret, e não no manifesto
- Abordagem sidecar: quando se justifica e o que oferece
- Gateway egress: endereço de saída estável para o cluster
- Diagnóstico do tráfego de saída no cluster
- Erros típicos que vale a pena evitar
- Ferramentas e recursos
- Casos e resultados
- Faq: perguntas frequentes dos engenheiros
- Conclusão: montando o checklist de implantação
Um engenheiro acostumado com servidores comuns chega ao Kubernetes e faz o que sempre fez: exporta HTTP_PROXY no ambiente, reinicia o processo e espera que todo o tráfego de saída passe pelo proxy. Às vezes funciona. Muito mais frequentemente isso quebra metade do cluster, e a outra metade continua acessando a internet diretamente, ignorando o proxy. E aí começa a parte mais interessante: por que alguns pods capturaram as variáveis e outros não, por que os serviços pararam de se enxergar e por que o healthcheck de repente passou por um servidor proxy que não está na lista de confiáveis.
Este artigo é uma análise detalhada de como o tráfego de saída no cluster realmente funciona e onde o proxy se encaixa corretamente. Não vamos repassar a configuração básica de variáveis de ambiente: você já sabe disso. A conversa será sobre as particularidades do Kubernetes, onde abordagens familiares produzem efeitos inesperados. Todos os exemplos usam fragmentos reais de manifestos, e no papel de infraestrutura de proxy entra o Proxeon (proxeon.net).
Fundamentos: por que no cluster tudo é diferente
Vamos começar pelo básico. Em um servidor clássico, o conceito de tráfego de saída é trivial: há uma interface de rede, uma tabela de rotas, variáveis de ambiente do sistema, e quase tudo o que você executa as herda do shell pai. Exportou a variável no profile, todo processo de usuário a enxerga.
No Kubernetes não existe esse ponto único. Aqui existe o pod, unidade mínima de implantação, dentro da qual vivem um ou vários containers. O pod tem seu próprio namespace de rede, seu próprio endereço IP, seu próprio conjunto de variáveis de ambiente definido pelo manifesto. Não existe shell de onde o processo possa herdar algo. Uma variável de ambiente só aparece no container se você a declarou explicitamente na especificação do pod ou na imagem.
O que é tráfego de saída do pod
Quando o container acessa um endereço externo, o pacote percorre um caminho longo. Primeiro sai do namespace de rede do container por uma interface virtual. Depois entra na pilha de rede do nó, onde é tratado pelo plugin CNI, componente responsável pela rede do cluster. Em seguida entram em cena as regras de iptables ou eBPF, o mecanismo de SNAT (substituição do endereço de origem), e o pacote sai pela interface de rede do nó rumo ao mundo externo.
Ponto-chave: do ponto de vista do serviço externo, a requisição chega não do endereço do pod, mas do endereço do nó do cluster. O pod fica oculto atrás da tradução de endereços. Isso é a primeira coisa que surpreende os iniciantes ao tentar configurar acesso por lista de IPs permitidos: eles adicionam o endereço do pod à lista, mas o tráfego chega de um endereço completamente diferente.
Dois tipos de tráfego de saída
É importante desde o início separar dois fluxos fundamentalmente diferentes:
- Leste-oeste - tráfego entre serviços dentro do cluster. Um pod acessa outro por meio de um serviço, ClusterIP ou nome DNS como my-service.namespace.svc.cluster.local.
- Norte-sul - tráfego para fora, para APIs externas, bancos de dados, serviços parceiros, armazenamentos de objetos.
O proxy quase sempre só é necessário para o tráfego norte-sul. E o erro mais comum e doloroso é que uma configuração incorreta de proxy acaba capturando também o tráfego leste-oeste, rompendo a comunicação interna. Por isso a lista de exceções aqui é mais importante do que em qualquer outro lugar. Mas falaremos disso mais adiante.
Mergulho profundo: os três níveis onde o proxy vive
Existem exatamente três níveis arquiteturais nos quais se pode integrar o proxy ao caminho de saída do pod. Cada um resolve o problema a sua maneira, cada um tem seu preço. Vamos analisar os três com honestidade, com prós e contras.
Nível 1: o próprio container
É quando a aplicação dentro do container conhece o proxy. Ela lê as variáveis de ambiente HTTP_PROXY e HTTPS_PROXY ou tem o proxy em sua própria configuração e direciona suas requisições HTTP por ele. A lógica de proxy está embutida na biblioteca cliente da própria aplicação.
Prós. Simplicidade máxima de implantação no começo. Nada precisa ser adicionado ao cluster, nenhum componente extra. Controle no nível do pod específico: você sabe exatamente qual aplicação acessa o quê.
Contras. Cada aplicação precisa saber ler essas variáveis, e muitas não sabem. A configuração se espalha por dezenas de manifestos. Atualizar o endereço do proxy significa percorrer todos os deployments. É fácil esquecer um serviço, e ele acessará diretamente. Não há política centralizada.
Nível 2: container sidecar
Aqui, ao lado do container principal no mesmo pod, roda um segundo container - o sidecar. Ele intercepta o tráfego de saída e o direciona pelo proxy. A aplicação pode nem saber que o proxy existe: ela envia requisições normalmente, e o sidecar as proxeia de forma transparente. É assim que funcionam as service meshes como Istio e Linkerd, e pelo mesmo princípio é possível instalar um agente proxy local leve.
Prós. Transparência para a aplicação. Política unificada via template de pod. Possibilidade de adicionar, além do proxeamento, observabilidade: métricas, tracing, retentativas, timeouts. O sidecar é isolado no pod e compartilha seu ciclo de vida.
Contras. Custos adicionais: cada pod agora tem dois containers, ou seja, mais memória e CPU. A depuração fica mais complexa - aparece um elo extra na cadeia. A ordem de inicialização dos containers importa: se a aplicação inicia antes do sidecar, as primeiras requisições podem falhar. No Kubernetes 1.28+, esse problema é resolvido com native sidecar containers como init-containers com política restartPolicy Always.
Nível 3: gateway egress do cluster
É um nó ou pod dedicado pelo qual todo o tráfego de saída do cluster é forçado a passar. Os pacotes são roteados para o gateway egress pelos meios do CNI, e ele se comunica com o proxy externo ou ele mesmo atua como ponto de saída com IP de origem estável.
Prós. Ponto único de controle para todo o cluster. Endereço de saída estável e previsível, conveniente para adicionar a listas de IPs permitidos de parceiros externos. A política é alterada em um só lugar. As aplicações não sabem de nada.
Contras. O gateway egress se torna ponto crítico: se cair, todo o norte-sul para. Precisa de redundância e monitoramento. A configuração é mais complexa e exige suporte do CNI. A granularidade é menor: é mais difícil definir políticas diferentes para aplicações diferentes sem regras adicionais.
Como escolher o nível
Orientação prática baseada em experiência: projeto pequeno com dois ou três serviços que precisam de proxy externo - nível do container. Cluster médio com dezenas de serviços e exigência de política unificada - sidecar. Infraestrutura grande, onde parceiros externos exigem IP de saída fixo e auditoria de todo o tráfego - gateway egress. Muitas vezes os níveis são combinados: gateway egress para o controle básico mais variáveis de ambiente para ajuste fino de pods específicos via Proxeon.
Variáveis de ambiente no manifesto e o papel do NO_PROXY
Vamos à parte mais subestimada. As variáveis HTTP_PROXY, HTTPS_PROXY e NO_PROXY no manifesto são definidas pelo bloco env da especificação do container. Os nomes clássicos costumam ser duplicados em minúsculas, porque parte das bibliotecas lê justamente as variantes em minúsculas.
apiVersion: apps/v1
kind: Deployment
metadata:
name: worker
spec:
replicas: 2
selector:
matchLabels:
app: worker
template:
metadata:
labels:
app: worker
spec:
containers:
- name: app
image: registry.example.com/worker:1.4.0
env:
- name: HTTP_PROXY
value: "http://gate.proxeon.net:8080"
- name: HTTPS_PROXY
value: "http://gate.proxeon.net:8080"
- name: NO_PROXY
value: "localhost,127.0.0.1,10.0.0.0/8,.svc,.svc.cluster.local,.cluster.local,kubernetes.default"
- name: http_proxy
value: "http://gate.proxeon.net:8080"
- name: https_proxy
value: "http://gate.proxeon.net:8080"
- name: no_proxy
value: "localhost,127.0.0.1,10.0.0.0/8,.svc,.svc.cluster.local,.cluster.local,kubernetes.default"Por que sem o NO_PROXY correto o cluster desmorona
Aqui está a essência do problema. Assim que você declara HTTP_PROXY, o cliente HTTP da aplicação começa a direcionar pelo proxy absolutamente todas as requisições, incluindo acessos a serviços vizinhos dentro do cluster. Mas os serviços internos só são acessíveis dentro da rede do cluster - o servidor proxy externo não consegue alcançá-los fisicamente. Resultado: uma requisição para orders.default.svc.cluster.local vai para o proxy externo, ele tenta resolver esse nome, não consegue, e devolve erro. A comunicação interna quebra instantaneamente.
NO_PROXY é a lista de exceções, endereços e domínios que devem ser contornados fora do proxy e enviados diretamente. Em um servidor comum, colocam-se aí o localhost e talvez algumas sub-redes internas. No Kubernetes, essa lista se torna criticamente importante, porque deve cobrir toda a topologia interna do cluster. Esqueceu um sufixo, e parte do tráfego entre serviços passou pelo proxy externo.
O que obrigatoriamente deve estar no NO_PROXY do cluster
- localhost e 127.0.0.1 - acessos dentro do próprio pod.
- Faixa do Pod CIDR - sub-rede da qual saem os endereços dos pods, por exemplo 10.0.0.0/8 ou a faixa específica do seu cluster.
- Faixa do Service CIDR - sub-rede dos ClusterIPs dos serviços, frequentemente 10.96.0.0/12.
- Sufixos DNS do cluster - .svc, .svc.cluster.local, .cluster.local. São eles que cobrem todos os nomes DNS internos dos serviços.
- kubernetes.default - nome do servidor de API, acessado por aplicações e agentes.
- Metadados da nuvem - o endereço 169.254.169.254, se você estiver em ambiente de nuvem, para que acessos a metadados não passem pelo proxy.
Detalhes de sintaxe do NO_PROXY
Aqui se esconde toda uma camada de sutilezas nas quais até engenheiros experientes tropeçam.
Primeiro, bibliotecas diferentes interpretam as entradas de maneiras diferentes. Algumas consideram que .cluster.local com ponto inicial significa sufixo e combina com todos os subdomínios. Outras exigem o formato cluster.local sem ponto. Prática: indique os dois formatos, com e sem ponto, para cobrir o máximo de clientes.
Segundo, o suporte à notação CIDR não é universal. A biblioteca Go entende 10.0.0.0/8, mas versões antigas de alguns clientes em outras linguagens não, e precisam de endereços individuais ou faixas em outro formato. Verifique o comportamento da sua stack específica.
Terceiro, portas. Se a entrada no NO_PROXY for indicada sem porta, geralmente vale para qualquer porta do host. Mas alguns clientes comparam estritamente com a porta. Em portas não padrão de serviços internos, isso é importante verificar.
Insight da prática: nove de cada dez incidentes de comunicação interna quebrada após a introdução de proxy são NO_PROXY incompleto. Monte uma lista de referência para o seu cluster uma única vez, coloque-a em um ConfigMap comum e reutilize em todos os deployments. Isso economiza dezenas de horas de depuração.
O que NÃO captura variáveis de ambiente
A crença ingênua de que "coloquei HTTP_PROXY, então todo o tráfego passou pelo proxy" no cluster está duplamente errada. Existe toda uma classe de componentes que simplesmente ignora essas variáveis. É preciso conhecê-los de antemão.
kubelet e componentes de sistema
As variáveis de ambiente do container são visíveis apenas para processos dentro desse container. O kubelet - agente do nó que baixa imagens, inicia containers e se comunica com o servidor de API - vive no nível do nó, não do pod. Ele não lê env do manifesto. Se você precisa que o kubelet puxe imagens através do proxy, a configuração é feita no nível do serviço systemd do kubelet ou na configuração do container runtime, não na especificação do pod.
# /etc/systemd/system/kubelet.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://gate.proxeon.net:8080"
Environment="HTTPS_PROXY=http://gate.proxeon.net:8080"
Environment="NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,.svc,.cluster.local"O mesmo vale para o container runtime - containerd ou CRI-O. O download de imagens passa pelo contexto de rede próprio deles. A configuração de proxy para o registry de imagens é feita no config do runtime, e é uma história separada dos pods.
Imagens com configuração própria
Muitas imagens populares têm configurações de rede embutidas que sobrepõem as variáveis de ambiente. Por exemplo, gerenciadores de pacotes, servidores web ou ferramentas de proxy dentro da imagem podem ler seu próprio arquivo de configuração, e não o ambiente. Se a aplicação dentro do container usa, digamos, um arquivo de configuração com parâmetros de conexão explicitamente definidos, ela simplesmente não verá suas variáveis de env.
Categoria separada - aplicações cujo cliente de rede é inicializado antes da leitura do ambiente ou que faz cache das configurações na inicialização. Alterar a variável sem reiniciar o processo não terá efeito aqui.
SDKs e runtimes de linguagem específicos
Este é o grupo mais traiçoeiro. Respeitar HTTP_PROXY é uma convenção, não um padrão. Alguns seguem, outros não.
- Go. O http.Client padrão via ProxyFromEnvironment respeita as variáveis. Mas se o código cria um transporte com Proxy explicitamente nil, as variáveis são ignoradas.
- Python. A biblioteca requests lê o ambiente por padrão. Já sockets de baixo nível, alguns clientes gRPC e bibliotecas assíncronas - não.
- Java. A JVM usa suas próprias propriedades de sistema http.proxyHost e https.proxyHost, e não lê variáveis de ambiente por padrão. Elas precisam ser passadas via JAVA_TOOL_OPTIONS.
- Node.js. O módulo http nativo não respeita variáveis de ambiente. São necessários agentes de terceiros que as leiam.
- gRPC. Parte das implementações lê uma variável especial grpc_proxy, e não as padrão.
A conclusão é simples: não se pode presumir que, por a variável estar declarada, todo o tráfego passará por ela. Verifique cada runtime separadamente. Para JVM, por exemplo, o manifesto fica assim:
env:
- name: JAVA_TOOL_OPTIONS
value: "-Dhttp.proxyHost=gate.proxeon.net -Dhttp.proxyPort=8080 -Dhttps.proxyHost=gate.proxeon.net -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=localhost|127.0.0.1|*.svc|*.cluster.local"Observe: na JVM o separador de exceções é a barra vertical, e não a vírgula, e os padrões usam asterisco. Outra armadilha para quem copia mecanicamente o NO_PROXY.
Segredos: credenciais de proxy via Secret, e não no manifesto
Se o seu proxy exige autenticação, surge a questão de armazenar login e senha. A tentação é grande: colocá-los direto na URL dentro do valor de env. Isso não pode ser feito, e eis por quê.
Manifestos de deployment quase sempre ficam em sistema de controle de versão. Senha em texto claro no env é vazamento de credenciais no histórico do Git, acessível a todos com acesso ao repositório. Além disso, valores de env são visíveis a qualquer um que possa executar kubectl describe pod. É violação direta do princípio do menor privilégio.
O caminho correto é o objeto Secret. Ele armazena dados sensíveis separadamente, com possibilidade de restringir o acesso via RBAC e habilitar criptografia no armazenamento etcd.
apiVersion: v1
kind: Secret
metadata:
name: proxeon-credentials
type: Opaque
stringData:
proxy-user: my_account
proxy-pass: s3cr3t_token_valueEm seguida, conectamos os valores do secret às variáveis de ambiente do container via secretKeyRef, e a própria URL do proxy é montada de forma que as credenciais não apareçam no manifesto:
env:
- name: PROXY_USER
valueFrom:
secretKeyRef:
name: proxeon-credentials
key: proxy-user
- name: PROXY_PASS
valueFrom:
secretKeyRef:
name: proxeon-credentials
key: proxy-pass
- name: HTTPS_PROXY
value: "http://$(PROXY_USER):$(PROXY_PASS)@gate.proxeon.net:8080"
- name: NO_PROXY
value: "localhost,127.0.0.1,10.0.0.0/8,.svc,.cluster.local"O Kubernetes substitui valores de variáveis declaradas anteriormente pela sintaxe com cifrão e parênteses. Isso permite montar a URL com credenciais em tempo de execução, sem colocar a senha diretamente no texto do manifesto. Atenção: o valor montado de HTTPS_PROXY ainda será visível no env do container em execução, então restrinja adicionalmente quem pode executar exec e describe nesses pods.
Boas práticas com secrets
- Habilite a criptografia do etcd em repouso - caso contrário, os secrets ficam armazenados apenas em base64, o que não é proteção.
- Restrinja o acesso aos secrets via RBAC: o pod deve ver apenas o seu próprio secret.
- Rotacione as credenciais do proxy regularmente e automatize o restart dos pods após a rotação.
- Considere gerenciadores externos de secrets com integração via driver CSI, para que as credenciais nem cheguem ao etcd.
- Nunca registre em log a URL do proxy montada - a senha vazará para o sistema de coleta de logs.
Abordagem sidecar: quando se justifica e o que oferece
Já mencionamos o sidecar como um dos três níveis. Agora vamos analisá-lo como estratégia independente com mais detalhes, porque é a ferramenta mais flexível, se você estiver disposto a pagar por ela com recursos.
Quando o sidecar se justifica
- A aplicação não sabe ler HTTP_PROXY e reescrever seu código é impossível ou caro.
- É necessária uma política única de proxeamento sem alterar cada aplicação.
- É necessária interceptação transparente de tráfego, incluindo protocolos não HTTP.
- É necessária observabilidade: métricas de conexões de saída, tracing, auditoria.
- São necessárias políticas de retentativas, timeouts, quebra de circuito no nível das conexões.
O que o sidecar oferece além do proxeamento
Aqui está o verdadeiro valor. O sidecar não é apenas um redirecionador de pacotes. Bem configurado, ele se transforma em ponto de controle e observação de todo o tráfego de saída do pod.
- Métricas. Quantas requisições saíram, para quais hosts, com que latência, quantos erros. Sem sidecar, esses dados teriam que ser coletados em cada aplicação separadamente.
- Tracing. Traces distribuídos de chamadas de saída, vinculados às requisições de entrada.
- Políticas de confiabilidade. Retentativas automáticas de requisições idempotentes, timeouts, limitação de conexões concorrentes.
- TLS unificado. O sidecar pode terminar e estabelecer conexões seguras de forma centralizada.
- Auditoria e conformidade. Registro completo de para onde exatamente a aplicação acessou - indispensável para compliance.
Exemplo de pod com proxy sidecar
Abaixo, um template simplificado em que o container principal direciona requisições HTTP de saída para o sidecar local, e este as proxeia adiante via Proxeon. Para a aplicação, o endereço do proxy é o localhost, o que automaticamente exclui o tráfego entre serviços do proxeamento externo, se a aplicação acessar os vizinhos diretamente.
apiVersion: v1
kind: Pod
metadata:
name: app-with-proxy-sidecar
labels:
app: billing
spec:
containers:
- name: app
image: registry.example.com/billing:2.1.0
env:
- name: HTTP_PROXY
value: "http://127.0.0.1:3128"
- name: HTTPS_PROXY
value: "http://127.0.0.1:3128"
- name: NO_PROXY
value: "localhost,127.0.0.1,10.0.0.0/8,.svc,.cluster.local"
- name: egress-proxy
image: registry.example.com/egress-agent:1.0.0
ports:
- containerPort: 3128
env:
- name: UPSTREAM_PROXY
value: "http://gate.proxeon.net:8080"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128MiNative sidecar e ordem de inicialização
O problema clássico do sidecar é a corrida na inicialização. Se a aplicação principal inicia e faz a primeira requisição de saída antes de o sidecar estar pronto para aceitar conexões, a requisição falha. A partir do Kubernetes 1.28 surgiu suporte a containers sidecar nativos: eles são declarados no bloco initContainers com restartPolicy Always e iniciam garantidamente antes, permanecendo vivos até o container principal. Isso resolve o problema da corrida de forma limpa, sem gambiarras como atrasos e retentativas na inicialização.
spec:
initContainers:
- name: egress-proxy
image: registry.example.com/egress-agent:1.0.0
restartPolicy: Always
ports:
- containerPort: 3128Gateway egress: endereço de saída estável para o cluster
O terceiro nível merece uma conversa separada, porque é justamente ele que resolve a tarefa pela qual a maioria chega ao proxy no cluster: IP de saída previsível.
Para que serve um IP de saída fixo
Parceiros externos, gateways de pagamento, fornecedores de dados não raramente operam com lista de IPs permitidos. Eles dizem: aceitamos requisições apenas destes IPs. Em um cluster comum, o endereço de saída do pod é o endereço do nó onde o pod acabou naquele momento. Os nós são muitos, escalam, são substituídos, adicionados no autoscaling. O endereço é imprevisível. O parceiro não pode adicionar à lista permitida todo o pool de nós, que ainda por cima muda.
O gateway egress resolve isso: todo o tráfego de saída se concentra em um ponto com endereço fixo. O parceiro adiciona à lista permitida um ou dois IPs estáveis - e tudo funciona. Ao usar o Proxeon como ponto de saída, você obtém um endereço externo estável, que fornece aos parceiros uma única vez.
Como o tráfego é roteado para o gateway egress
O mecanismo depende do CNI. Alguns plugins CNI suportam o objeto de política egress, em que você descreve: o tráfego de pods com tais labels, saindo para fora, deve sair por tal nó ou endereço. O plugin configura as regras correspondentes de SNAT e roteamento automaticamente. Conceitualmente, fica assim:
apiVersion: policy.example.io/v1
kind: EgressPolicy
metadata:
name: billing-egress
spec:
selector:
matchLabels:
egress: proxeon
egressIP: 203.0.113.10
destinationCIDRs:
- 0.0.0.0/0A sintaxe exata difere entre CNIs, então consulte a documentação do seu plugin. A ideia é imutável: marcar os pods cujo tráfego é direcionado pelo gateway e definir um endereço de saída estável.
Confiabilidade do gateway egress
Se o gateway é ponto único, ele também é ponto único de falha. Regras de sobrevivência:
- Redundância: no mínimo dois nós-gateway com failover automático.
- Monitore a disponibilidade da saída com uma requisição sintética externa a cada minuto.
- Acompanhe a capacidade: todo o norte-sul flui pelo gateway, e o gargalo afetará tudo.
- Separe políticas: tráfego crítico e de fundo é melhor dividir em caminhos diferentes, para que a carga de fundo não atrapalhe o importante.
Diagnóstico do tráfego de saída no cluster
Quando algo dá errado - e vai dar - é preciso um arsenal de verificações. Vamos reunir um conjunto prático de comandos e abordagens.
Passo 1: descobrir o IP de saída real do pod
A primeira coisa a verificar: de qual endereço o pod é visto pelo mundo externo. Execute um container efêmero de depuração ou faça exec em um pod existente e acesse um serviço que retorna seu endereço público.
kubectl run nettest --rm -it --image=registry.example.com/nettools:1.0 --restart=Never -- sh
# dentro do container
curl -s https://api.ipify.org
curl -s -x http://gate.proxeon.net:8080 https://api.ipify.orgA primeira requisição mostra o endereço sem proxy, a segunda - via proxy explicitamente. Se ambas forem iguais e você esperava diferentes, o proxy não está sendo aplicado. Se a segunda retorna o endereço esperado do Proxeon, o proxy funciona e o problema está na configuração da aplicação.
Passo 2: verificar se a aplicação vê as variáveis
kubectl exec deploy/worker -c app -- env | grep -i proxySe a saída estiver vazia, as variáveis não chegaram ao container. Verifique o manifesto e se o pod foi recriado após a alteração. Lembrete: alterar env exige recriar o pod, não é capturado em tempo real.
Passo 3: verificar a resolução de DNS por dentro
Muitos erros se mascaram como problema de proxy, quando na verdade é DNS. Verifique a resolução de um nome interno e de um externo:
kubectl exec deploy/worker -c app -- nslookup orders.default.svc.cluster.local
kubectl exec deploy/worker -c app -- nslookup api.external-partner.comSe o nome interno não resolver, o problema está no CoreDNS ou no fato de a requisição ter ido para o proxy externo por falta do sufixo no NO_PROXY. Isso é confirmação direta de que a lista de exceções está incompleta.
Passo 4: onde procurar as falhas
- Logs da aplicação. Procure erros de conexão, timeouts, falha de autenticação no proxy (normalmente código 407).
- Logs do sidecar. Se houver, mostram quais requisições ele aceitou e para onde as direcionou.
- Métricas do gateway egress. Aumento de falhas e latências no gateway.
- Eventos do pod. kubectl describe pod mostrará problemas de inicialização, incluindo a corrida do sidecar.
- NetworkPolicy. Verifique se a política de rede não está bloqueando o tráfego de saída - causa frequente de falhas silenciosas.
Assinaturas típicas de problemas
- Código 407 do proxy - credenciais erradas ou ausentes. Verifique o Secret.
- Timeout ao acessar um serviço interno após habilitar o proxy - NO_PROXY incompleto.
- IPs de saída diferentes em requisições repetidas - o tráfego não passa pelo gateway egress, funciona o SNAT comum do nó.
- As primeiras requisições após a inicialização falham, depois tudo funciona - corrida do sidecar, migre para native sidecar.
- Aplicação Java ignora o proxy - esqueceram do JAVA_TOOL_OPTIONS e das propriedades de sistema.
Erros típicos que vale a pena evitar
Vamos reunir em um só lugar as armadilhas mais frequentes. Verifique-se nessa lista antes, e não depois, do incidente.
NO_PROXY incompleto
Líder absoluto. Esqueceram o Service CIDR, esqueceram o sufixo .svc, não consideraram o endereço de metadados da nuvem - e surgiu uma cascata de falhas misteriosas. Sempre parta de uma lista de referência completa para o seu cluster.
Senha do proxy em texto claro no manifesto
Vazamento no Git e na saída do describe. Apenas Secret, apenas acesso restrito.
Presumir que todos os runtimes respeitam as variáveis
JVM, Node.js, parte dos clientes gRPC ignoram HTTP_PROXY. Verifique cada stack separadamente e configure da forma nativa.
Ignorar o kubelet e o runtime
O download de imagens via proxy é configurado no nível do nó, não do pod. Não se surpreenda se as imagens não forem baixadas quando você configurou apenas o env no manifesto.
Corrida na inicialização do sidecar
A aplicação inicia antes do proxy e derruba as primeiras requisições. A solução são os native sidecar containers.
Gateway egress sem redundância
Ponto único de falha sem duplicação derruba todo o tráfego de saída. Redundância e monitoramento.
Mistura de formatos CIDR
Indicou 10.0.0.0/8 para um cliente que não entende CIDR. Verifique qual formato a sua biblioteca aceita e ofereça alternativa.
Falta de recriação dos pods após mudança de env
Alterou a variável no deployment, mas os pods antigos continuam funcionando com os valores antigos até o restart. Garanta o rollout.
Esquecer o minúsculo nas variáveis
Parte das bibliotecas lê apenas http_proxy em minúsculas. Duplique os nomes nos dois casos.
Ferramentas e recursos
O que manter à mão para trabalhar com tráfego de saída no cluster.
Diagnóstico
- kubectl exec e kubectl debug - base para verificações de dentro do pod.
- Containers efêmeros de depuração - permitem conectar um conjunto de ferramentas de rede a um pod em execução sem reconstruir a imagem.
- Imagem com utilitários de rede - curl, dig, nslookup, traceroute, reunidos em um container para verificações rápidas.
- Serviço de retorno de IP público - verificação do endereço de saída real.
Infraestrutura
- ConfigMap com NO_PROXY de referência - fonte única de verdade para todos os deployments.
- Secret e gerenciadores externos de secrets - para as credenciais do proxy.
- Service mesh - se você precisa de sidecar com observabilidade pronta.
- Políticas egress do CNI - para roteamento ao gateway.
- Proxeon (proxeon.net) - infraestrutura de proxy para endereço de saída estável e acesso autenticado.
Monitoramento
- Métricas de conexões de saída do sidecar ou gateway egress.
- Verificações sintéticas de disponibilidade de endereços externos a partir do cluster.
- Alertas para aumento de códigos 407 e timeouts de requisições de saída.
- Dashboard com a distribuição do tráfego de saída por hosts de destino.
Casos e resultados
Vamos analisar três cenários generalizados que mostram como a escolha do nível influencia o resultado.
Caso 1: integração de pagamento e lista de IPs permitidos
A equipe integrou um gateway de pagamento externo que aceita requisições apenas de endereços acordados. Primeiro tentaram variáveis de ambiente no nível do container - e se depararam com o fato de que, no autoscaling, os pods se espalhavam por novos nós com novos endereços, e o IP de saída continuava sendo o endereço do nó, não o do proxy. Parte das requisições começou a ser rejeitada.
Solução: migraram o serviço de pagamento para egress via Proxeon com endereço de saída fixo. O parceiro adicionou um IP à lista permitida. As rejeições por endereço desconhecido desapareceram. Efeito adicional - registro centralizado de todos os acessos ao gateway de pagamento para auditoria.
Caso 2: quebra da comunicação entre serviços após a introdução do proxy
A empresa adicionou HTTP_PROXY em todos os deployments de uma vez, por um template comum. Em poucos minutos começaram os erros: os serviços pararam de se enxergar. As chamadas internas iam para o proxy externo, que não conseguia resolvê-las.
O diagnóstico levou tempo, até verificarem a resolução de DNS de dentro do pod e verem que o nome .svc.cluster.local ia para o proxy. A causa - o NO_PROXY continha apenas localhost. Montaram uma lista de referência completa com Pod CIDR, Service CIDR e todos os sufixos DNS, colocaram em um ConfigMap, conectaram em todos os pods. A comunicação interna foi restaurada. Desde então, o NO_PROXY de referência é parte obrigatória do template de deployment.
Caso 3: observabilidade do tráfego de saída via sidecar
Exigência de segurança: saber exatamente para onde cada aplicação acessa o exterior, com registro completo. As aplicações estavam em linguagens diferentes, e parte não sabia ler HTTP_PROXY. Alterar o código de dezenas de serviços era caro demais.
Implementaram um proxy sidecar no template de pod. As aplicações direcionam o tráfego de saída para o agente local, que proxeia via Proxeon e grava métricas: host de destino, latência, código de resposta. Surgiu um dashboard de acessos de saída por serviço. Além disso, configuraram timeouts e retentativas no nível do sidecar, o que reduziu o número de falhas em cascata na indisponibilidade momentânea de APIs externas. O custo foram recursos extras para o sidecar, mas o ganho em observabilidade e confiabilidade justificou.
FAQ: perguntas frequentes dos engenheiros
Por que o tráfego para um pod vizinho passa pelo proxy externo, se é obviamente um endereço interno?
Porque o cliente HTTP não sabe que o endereço é interno. Ele vê o nome ou o endereço e, se ele não estiver no NO_PROXY, direciona a requisição ao proxy conforme as regras. O cliente não distingue leste-oeste de norte-sul sozinho - a lista de exceções faz isso por ele. Adicione os sufixos e sub-redes internas ao NO_PROXY.
É necessário configurar proxy também para baixar imagens?
Sim, mas não via env do pod. O download de imagens é feito pelo container runtime e pelo kubelet no nível do nó. O proxy para o registry é configurado no runtime ou no unit systemd do kubelet. As variáveis de ambiente do container não influenciam isso de forma alguma.
O que é mais importante para um IP de saída estável - sidecar ou gateway egress?
Gateway egress. O sidecar proxeia o tráfego de um pod específico, mas o endereço de saída continua sendo determinado por onde a conexão vai adiante. Para um endereço garantidamente fixo, que o parceiro externo aceitará, é preciso um gateway egress ou uma saída por um ponto externo com endereço fixo, por exemplo via Proxeon.
Por que a aplicação Java ignora HTTP_PROXY?
A JVM, por razões históricas, não lê variáveis de ambiente para proxy. Ela usa propriedades de sistema próprias http.proxyHost, https.proxyHost e exceções http.nonProxyHosts. Passe-as via JAVA_TOOL_OPTIONS. E lembre-se: o separador de exceções na JVM é a barra vertical, e os padrões usam asterisco, não CIDR.
Como armazenar com segurança a senha do proxy?
Em um objeto Secret com acesso restrito por RBAC e criptografia do etcd habilitada. Conecte os valores via secretKeyRef. Não escreva a senha em texto claro no manifesto, senão ela vazará no Git e na saída do describe. Para exigências mais altas, use um gerenciador externo de secrets via CSI.
Alterei a variável de ambiente, mas o comportamento não mudou - por quê?
As variáveis de ambiente são fixadas na inicialização do container. Enquanto o pod não for recriado, ele funciona com os valores antigos. Atualize o deployment para que ocorra o rollout dos pods. Verifique também se a aplicação realmente lê o ambiente, e não faz cache das configurações na inicialização.
Como descobrir qual formato de NO_PROXY a minha biblioteca precisa?
Empiricamente: configure o proxy, acesse um nome interno de dentro do pod e veja se a requisição foi para o proxy ou diretamente. Se foi para o proxy, o formato não foi reconhecido. Tente a variante com ponto inicial e sem, adicione endereços individuais em vez de CIDR, verifique o caso do nome da variável. A documentação do cliente específico é a melhor referência.
Vale sempre usar service mesh para proxeamento?
Não. Service mesh é uma ferramenta poderosa, com observabilidade e políticas, mas traz custos operacionais e sobrecarga consideráveis. Se a tarefa é apenas direcionar o tráfego de saída pelo proxy, um agente sidecar leve ou um gateway egress saem mais baratos. Implante mesh quando você realmente precisar de todos os seus recursos.
Como lidar com tráfego que não é HTTP?
As variáveis HTTP_PROXY funcionam apenas para clientes HTTP e HTTPS que as respeitam. Para conexões TCP arbitrárias, é necessária interceptação transparente no nível do sidecar ou do gateway egress com regras de roteamento correspondentes. Aqui as variáveis de ambiente não ajudam.
É possível definir políticas de proxy diferentes para pods diferentes?
Sim. No nível do container - via env diferentes em deployments diferentes. No nível egress - via seletores de labels nas políticas egress, direcionando os pods marcados para a saída desejada. A combinação traz flexibilidade: saída básica pelo gateway mais configurações individuais para serviços específicos.
Conclusão: montando o checklist de implantação
Percorremos o caminho desde por que a configuração "como em um servidor comum" não funciona no cluster até os três níveis de integração do proxy, as sutilezas do NO_PROXY, secrets, sidecar, gateway egress e diagnóstico. A principal conclusão: no Kubernetes, o tráfego de saída não é uma variável, mas um sistema em camadas, em que o mais importante não é como habilitar o proxy, e sim como não quebrar com ele a comunicação interna.
Vamos reunir o checklist final de implantação, que vale a pena manter à vista em cada implementação.
Checklist de implantação de proxy no cluster
- Definiram o nível. Escolheram container, sidecar ou gateway egress de forma consciente, para a tarefa específica.
- Montaram o NO_PROXY completo.