Por que dois containers no mesmo host não se enxergam por padrão
Suba o Postgres e sua API com um docker run simples, e a API não consegue alcançar o banco de dados, mesmo com os dois containers rodando na mesma máquina.
| |
Os dois caem na bridge padrão do Docker, aquela em que todo container Docker entra a menos que você diga o contrário. Eles recebem um endereço IP. O que não recebem é uma forma de se encontrarem pelo nome — a API teria que ter o IP interno do banco fixado no código, um endereço que muda a cada reinício do container.
Uma rede Docker resolve isso. É um switch virtual ao qual os containers se conectam, com sua própria faixa de IP e seu próprio DNS. Ao criar uma, os nomes dos containers viram hostnames, e você decide quem enxerga quem.
Qual driver de rede usar: bridge, host ou overlay
O Docker traz vários drivers de rede embutidos. Três deles cobrem quase tudo que você vai encontrar em um único host ou em um swarm:
| Driver | Escopo | Containers têm IP próprio | DNS por nome | Uso típico |
|---|---|---|---|---|
| bridge (padrão) | um único host | sim | não (só IP) | o que você tem sem a flag --network |
| bridge (user-defined) | um único host | sim | sim | a escolha padrão para desenvolvimento local e deploys de host único |
| host | um único host | não — compartilha a pilha de rede do host | n/a | serviços de alto throughput, ferramentas que precisam ver a rede real do host |
| overlay | vários hosts (swarm) | sim | sim | serviços distribuídos entre mais de um host Docker |
Também existe o none, que dá ao container apenas uma interface de loopback e nada mais. Útil para um job em lote que não precisa de nenhum acesso à rede.
Todo docker run novo cai na bridge padrão. Funciona, mas ela só roteia por IP; a antiga flag --link existia para contornar a falta de nomes, e o Docker a marca como legada. Uma bridge user-defined faz o mesmo trabalho com DNS embutido. É essa que você quer.
Como criar uma rede Docker e conectar containers a ela
| |
Isso é uma bridge privada com sub-rede própria, isolada da bridge padrão e de qualquer outra rede user-defined que você criar. Conecte os containers na hora de subir:
| |
Dentro do api, conectar em db:5432 funciona. Sem busca de IP, sem malabarismo com variáveis de ambiente: o servidor DNS embutido do Docker resolve o nome do container db para o endereço atual dele em app-net, e atualiza esse mapeamento quando o container reinicia com um IP novo. O Docker Compose roda sobre esse mesmo mecanismo. docker compose up cria uma rede para o stack, e cada serviço alcança os outros pelo nome do serviço.
Você também pode conectar um container que já está rodando, sem reiniciá-lo:
| |
Um container pode estar em mais de uma rede ao mesmo tempo. É assim que você dá a um serviço uma rede exposta publicamente e uma privada, compartilhada só com os containers de backend por trás dele.
Por que publicar uma porta não é como dois containers se comunicam
-p 8080:8080 mapeia a porta 8080 do host para a do container, o que permite acessar a API pelo navegador em localhost:8080. Isso não diz nada sobre se api consegue alcançar db. O tráfego entre containers na mesma rede user-defined fica interno, portas publicadas ou não.
Publique só as portas que algo fora do host Docker precisa alcançar. Um banco que só conversa com a API não precisa de -p 5432:5432: deixe essa porta sem publicar, e nem o host nem a internet conseguem se conectar diretamente ao banco. api continua alcançando o banco normalmente, porque os dois compartilham app-net.
Quando usar –network host, e quando não usar
| |
Com --network host, o container pula seu próprio namespace de rede e se vincula direto às interfaces do host. Sem NAT, sem bridge, sem mapeamento de portas: -p e -P são ignorados, e o Docker exibe um aviso se você os passar mesmo assim. Isso reduz um pouco a sobrecarga por pacote e dá ao container a visão real das interfaces de rede do host — o que interessa a ferramentas de monitoramento de rede e a softwares que precisam de uma faixa grande e contígua de portas.
Também tira o isolamento que uma bridge oferece. O container pode usar qualquer porta livre do host, e enxerga tudo que passa pela rede do host. No Linux, isso é totalmente suportado. O Docker Desktop para Mac e Windows exige ativação explícita (disponível a partir do Docker Desktop 4.34), porque a rede da VM do Docker não é a do host. Containers Windows não suportam isso de jeito nenhum. Para a maioria dos serviços, uma bridge user-defined com portas publicadas faz o mesmo trabalho com menos exposição.
Quando você precisa de uma rede overlay em vez de uma bridge
Redes bridge e host param na borda de um único host Docker. Rode Docker Swarm em várias máquinas, e um serviço no host A precisa de uma rede overlay para alcançar um serviço no host B:
| |
Uma overlay encapsula o tráfego dos containers e o roteia entre os daemons Docker de cada nó do swarm, então containers em máquinas físicas ou virtuais diferentes se encontram pelo nome exatamente como fariam numa bridge. A flag --attachable permite que containers standalone também entrem nela; sem ela, só os serviços do swarm conseguem se conectar. Se você não roda swarm ou outro orquestrador multi-host, pule esse driver. Uma rede bridge de host único cobre stacks do Compose e ambientes locais.
Como inspecionar e depurar uma rede Docker
| |
Todas as redes do host, incluindo bridge, host e none, que o Docker sempre cria.
| |
A sub-rede, o gateway e cada container conectado no momento com seu IP naquela rede. É o primeiro lugar a olhar quando um container não consegue alcançar outro. Leia a seção Containers: se algum dos dois estiver faltando, ele não está nessa rede, e o docker network connect o coloca lá.
Para testar a resolução DNS de dentro de um container em execução:
| |
Um IP de volta significa que a resolução de nomes funciona e o problema está na aplicação — porta errada, ou o serviço ainda não está escutando. Nada de volta significa que os dois containers não estão na mesma rede. Confirme com docker network inspect, ou pergunte a cada container a que ele está conectado:
| |
Uma rede por stack, com todo container nela
A bridge padrão é o que você ganha de graça. Não é sobre ela que você deve construir. Rode docker network create <app>-net para cada stack de aplicação, conecte nela todo container que esse stack precisar, e reserve --network host para o serviço raro que realmente precisa da pilha de rede do host.
Quando você passa a gerenciar mais de um par de containers na mão, pare de fazer isso na mão. O Docker Compose cria a rede e conecta os containers por você a partir de um único arquivo YAML. Os volumes Docker cobrem a outra metade de sobreviver a um reinício: os dados que esses containers escrevem.