Redes no Kubernetes: CNI, Services e Ingress Sem Mistério
Como um Pod ganha IP, como Services dão endereço estável a alvos efêmeros e como o Ingress expõe HTTP para fora. O modelo de rede do Kubernetes explicado.
Neste artigo
A rede é a parte do Kubernetes que mais assusta, e por um bom motivo: ela é invisível quando funciona e incompreensível quando quebra. O tráfego atravessa camadas de abstração que ninguém vê no dia a dia, e quando uma requisição não chega ao seu destino, a pergunta "por onde ela deveria passar?" costuma não ter resposta fácil na cabeça de quem opera. Mas, assim como o resto do Kubernetes, a rede é construída sobre poucas ideias coerentes. Quando você entende como um Pod ganha um endereço, como o tráfego encontra alvos que nascem e morrem, e como o mundo externo entra, o mistério se dissolve.
Este artigo percorre o modelo de rede do Kubernetes de baixo para cima: o contrato de rede que tudo assume, o plugin que o implementa, os Services que dão estabilidade ao efêmero, e o Ingress que abre a porta para fora. No fim, você deve conseguir traçar o caminho de um pacote da internet até o container, sem lacunas.
O modelo de rede plana: a regra que tudo assume#
Antes de qualquer componente, existe um contrato. O Kubernetes define um modelo de rede com uma regra fundamental que todas as implementações precisam respeitar: todo Pod recebe seu próprio endereço IP, e todos os Pods conseguem se comunicar entre si diretamente por esses IPs, sem NAT, independentemente do nó em que rodam.
Essa regra parece simples e é profundamente libertadora. Ela significa que, do ponto de vista da aplicação, o cluster inteiro é uma rede plana: um Pod no nó A fala com um Pod no nó B usando o IP do segundo, como se estivessem na mesma rede local, sem se preocupar com a topologia física dos nós. Não há tradução de endereço no meio, não há portas mapeadas de forma imprevisível. O IP que um Pod tem é o IP pelo qual ele é alcançado de qualquer lugar do cluster.
O Kubernetes define esse contrato mas não o implementa. Ele delega a implementação, e é aí que entra a próxima peça. O importante é fixar que toda a complexidade de rede existe para honrar essa regra simples: cada Pod com seu IP, todos se enxergando sem NAT. Guarde isso, porque cada camada acima serve a esse contrato.
CNI: quem entrega o IP ao Pod#
A implementação concreta da rede fica a cargo de um plugin que segue a Container Network Interface, a CNI. É um padrão que define como o Kubernetes pede a um plugin de rede que configure a conectividade de um Pod recém-criado, e como esse plugin responde.
Quando um Pod nasce, o kubelet, ao prepará-lo, chama o plugin CNI configurado no cluster. O plugin faz o trabalho concreto: cria a interface de rede virtual do Pod, atribui a ela um IP de um intervalo reservado para Pods, e configura as rotas necessárias para que esse IP seja alcançável de outros nós, honrando o modelo de rede plana. Quando o Pod morre, o plugin recolhe o IP de volta.
Existem vários plugins CNI, e a escolha tem consequências reais. Alguns implementam a rede plana com roteamento direto, outros com redes sobrepostas que encapsulam o tráfego entre nós, com diferentes trade-offs de desempenho e simplicidade. Muitos oferecem, além da conectividade básica, recursos de network policy, que são regras de firewall no nível do Pod, permitindo declarar quais Pods podem falar com quais, algo que o Kubernetes puro não faz sozinho. A CNI é onde decisões importantes de rede e segurança são tomadas, e trocar de plugin muda o que o cluster consegue fazer. O ponto a reter é que o IP de todo Pod vem de um plugin CNI, e é esse plugin que faz a rede plana existir de fato.
O problema dos alvos efêmeros#
Agora um problema que motiva a próxima abstração. Pods são efêmeros: nascem, morrem, são substituídos, e cada novo Pod ganha um IP novo. Se você tem um serviço de backend com várias réplicas, os IPs dessas réplicas mudam o tempo todo conforme Pods são recriados por deploys, falhas ou escalonamento.
Como, então, um serviço de frontend encontra o backend? Ele não pode simplesmente memorizar os IPs dos Pods do backend, porque esses IPs são instáveis e mudam sem aviso. Precisa de um endereço estável, que continue válido mesmo quando os Pods por trás dele são inteiramente substituídos. Esse é exatamente o problema que o Service resolve, e entender que ele existe para dar estabilidade ao que é efêmero é a chave para não se perder.
Service: endereço estável para o que muda#
Um Service é uma abstração que dá um nome e um IP estáveis a um conjunto de Pods que mudam. Ele funciona como um balanceador de carga interno e um ponto de entrada fixo.
O mecanismo é elegante. O Service seleciona os Pods de destino por meio de labels: ele diz "meu alvo são todos os Pods com o label app: backend". Conforme Pods com esse label nascem e morrem, o Kubernetes mantém atualizada a lista dos IPs vivos que correspondem ao seletor, os endpoints do Service. O Service ganha um IP virtual próprio e estável, o ClusterIP, que nunca muda durante a vida do Service. Quando o frontend acessa esse ClusterIP, o tráfego é distribuído entre os Pods vivos do backend, e essa distribuição é reprogramada automaticamente quando o conjunto de Pods muda. O frontend nunca precisa saber os IPs reais do backend; ele fala com o ClusterIP estável, e o Kubernetes cuida do resto.
Some a isso a descoberta por DNS. O cluster roda um DNS interno que dá a cada Service um nome previsível. Assim, o frontend não usa nem o IP; ele acessa o backend por um nome como backend, e o DNS resolve para o ClusterIP, que roteia para os Pods. Nomes estáveis para serviços cujos Pods são voláteis: essa é a promessa cumprida.
Existem tipos diferentes de Service, e a distinção importa para saber o alcance de cada um. O ClusterIP, padrão, expõe o Service apenas dentro do cluster, para comunicação entre serviços. O NodePort abre uma porta em todos os nós, expondo o Service para fora de forma crua. O LoadBalancer provisiona, em ambientes de nuvem, um balanceador de carga externo do provedor apontando para o Service, sendo a forma comum de expor um serviço à internet. Cada tipo amplia o alcance, do interno ao externo.
Ingress: a porta HTTP para o mundo#
Expor cada serviço à internet com um LoadBalancer próprio funciona, mas não escala bem: cada balanceador do provedor custa dinheiro e cada um dá um endereço diferente. Quando você tem muitos serviços HTTP para expor, quer algo mais inteligente, capaz de rotear por hostname e por caminho a partir de um único ponto de entrada. Esse é o papel do Ingress.
O Ingress é uma configuração que descreve regras de roteamento HTTP e HTTPS de fora para dentro do cluster. Ele permite dizer coisas como "requisições para api.exemplo.com vão para o Service da API" e "requisições para exemplo.com/loja vão para o Service da loja", tudo entrando por um único endereço externo. É roteamento na camada de aplicação, entendendo hostnames e caminhos, algo que os Services sozinhos não fazem.
Um detalhe que confunde: o recurso Ingress é só a declaração das regras. Quem efetivamente aplica essas regras e recebe o tráfego é o Ingress Controller, um componente que você instala no cluster e que observa os recursos Ingress, reconfigurando-se para cumpri-los. Sem um Ingress Controller rodando, criar um Ingress não faz nada, porque não há ninguém para executar as regras. É o mesmo padrão de controlador e loop de reconciliação que atravessa todo o Kubernetes: o Ingress declara o estado desejado de roteamento, e o controller reconcilia a realidade com ele. O Ingress Controller costuma ser o lugar onde também se resolve a terminação de TLS, centralizando os certificados HTTPS num ponto só.
O Pod, o namespace de rede e o localhost compartilhado#
Um detalhe do modelo merece atenção porque é fonte constante de confusão: o IP pertence ao Pod, não ao container. Quando um Pod tem mais de um container, todos eles compartilham o mesmo endereço IP e o mesmo espaço de portas. Isso não é coincidência nem conveniência; é consequência direta de como containers são isolados no Linux. Os containers de um Pod são colocados no mesmo network namespace, aquela partição do kernel que dá a um grupo de processos sua própria pilha de rede, com interfaces e portas próprias.
A implicação prática é elegante. Como compartilham o network namespace, os containers de um mesmo Pod se enxergam por localhost, exatamente como dois processos na mesma máquina. Um container de aplicação e um sidecar que faz proxy do tráfego conversam por 127.0.0.1 sem precisar de rede externa, e por isso não podem escutar na mesma porta, pois disputam o mesmo espaço de portas. Isso também explica por que dois Pods distintos podem, cada um, escutar na porta 8080 sem colidir: cada Pod tem seu próprio network namespace, e a porta 8080 de um não é a porta 8080 do outro. Se essa mecânica de isolamento por namespace te interessa por baixo do capô, vale entender os fundamentos de como containers funcionam no Linux, que são a base sobre a qual todo o modelo de rede do Kubernetes é construído.
O caminho completo de um pacote#
Vale amarrar tudo traçando a jornada de uma requisição da internet até o container, porque é aí que as peças se encaixam. Um usuário acessa api.exemplo.com. O DNS público resolve para o endereço externo do balanceador que serve o Ingress Controller. O tráfego chega ao Ingress Controller, que consulta as regras de Ingress e decide, pelo hostname, que aquilo vai para o Service da API. Ele encaminha para o ClusterIP desse Service. O Service, por sua vez, distribui a requisição entre os Pods vivos que correspondem ao seu seletor de labels. O pacote chega ao IP do Pod escolhido, IP esse que foi atribuído pelo plugin CNI quando o Pod nasceu, honrando o modelo de rede plana. E finalmente o container dentro do Pod atende.
Cada camada tem um papel claro: a CNI dá o IP e a conectividade base, o Service dá estabilidade e balanceamento a alvos efêmeros, o Ingress dá roteamento HTTP e a porta externa. Nenhuma delas é mágica; cada uma resolve um problema específico e concreto. Quando uma requisição não chega, o diagnóstico deixa de ser um chute e vira uma checagem camada por camada dessa cadeia: o DNS resolve? O Ingress tem a regra? O Service tem endpoints vivos? O Pod está saudável e escutando? A rede do Kubernetes é complexa, mas é uma complexidade que se percorre passo a passo, e não um labirinto sem mapa.