Olá! Bem-vindo de volta.
Nas lições anteriores, exploramos como criar redes bridge personalizadas para isolar nossos serviços e como usar o mapeamento de portas (-p) para expor um serviço específico ao mundo exterior. Isso nos leva a uma questão central para qualquer aplicação moderna: se temos vários serviços (como uma API .NET e um banco de dados PostgreSQL) rodando em seus próprios contêineres dentro de uma rede isolada, como eles se comunicam entre si de forma confiável?
O objetivo desta lição é responder a essa pergunta, permitindo a comunicação entre contêineres em uma rede bridge personalizada usando a resolução de DNS interna do Docker. Você aprenderá por que usar o nome do contêiner em vez de um endereço IP é a abordagem correta e como isso simplifica drasticamente a configuração de aplicações multi-serviço, como as que você desenvolve em .NET.
O Problema: Comunicação em Ambientes Dinâmicos
Quando você inicia um contêiner, o Docker atribui a ele um endereço IP de um pool privado da rede à qual ele está conectado. Você poderia obter o IP de um contêiner de banco de dados com docker inspect e colocar esse IP na string de conexão da sua API.
No entanto, essa abordagem é extremamente frágil. Os contêineres são efêmeros; eles podem ser parados, reiniciados ou substituídos. Cada vez que isso acontece, o Docker pode atribuir um novo endereço IP, quebrando sua aplicação. Precisamos de um método de descoberta de serviço (service discovery) que seja estável.
A Solução: DNS Interno do Docker
A razão pela qual as redes bridge definidas pelo usuário (user-defined) são superiores à rede bridge padrão é que elas vêm com um benefício crucial: um servidor DNS integrado.
A documentação oficial do Docker explica isso muito bem.
Para entender por que as redes personalizadas são a prática recomendada, vamos começar com a fonte oficial. Este documento detalha as diferenças entre a rede bridge padrão e as redes bridge definidas pelo usuário.
Leia a seção 'Differences between user-defined bridges and the default bridge'. Preste atenção especial ao primeiro ponto: 'User-defined bridges provide automatic DNS resolution between containers'. Isso é o cerne da nossa lição.
Como você leu, quando você conecta contêineres a uma rede bridge definida pelo usuário, o Docker automaticamente registra o nome de cada contêiner em seu servidor DNS interno. Isso significa que, de dentro dessa rede, qualquer contêiner pode encontrar qualquer outro simplesmente usando seu nome.

Demonstração: A Diferença na Prática
Nada é mais claro do que ver o conceito em ação. O vídeo a seguir demonstra de forma concisa a limitação da rede padrão e o poder da rede personalizada.
Docker Networking Tutorial (Bridge - None - Host - IPvlan - Macvlan - Overlay)
Este vídeo de Anton Putra mostra um tutorial prático sobre redes Docker. Vamos focar nos segmentos que comparam diretamente a comunicação na rede padrão e em uma rede personalizada.
Assista aos seguintes trechos: Introdução à Rede Personalizada (4:06 - 4:47): Entenda a principal vantagem mencionada: a resolução de DNS. Falha na Rede Padrão (4:47 - 7:37): Observe como a comunicação por nome de contêiner falha na rede bridge padrão. Sucesso na Rede Personalizada (7:37 - 8:55): Veja como, ao criar uma rede personalizada, a comunicação por nome de contêiner funciona perfeitamente.
O vídeo ilustra o fluxo de trabalho exato:
- Na rede
bridgepadrão,curl myapp-v2falha. - Após criar a rede
my-bridge-nete conectar os contêineres a ela,curl myapp-v2funciona.
Hands-On: Conectando uma API a um Banco de Dados
Vamos aplicar isso a um cenário prático para você, como desenvolvedor .NET. Imagine que você tem uma API (my-api) que precisa se conectar a um banco de dados PostgreSQL (my-db).
Passo 1: Crie a rede
Primeiro, criamos uma rede dedicada para nossa aplicação. Isso isola nossos serviços de outros contêineres que possam estar rodando no host.
docker network create meu-projeto-net
Passo 2: Inicie o contêiner do banco de dados
Agora, iniciamos o contêiner do PostgreSQL. Damos a ele um nome (--name my-db) e o conectamos à nossa rede (--network meu-projeto-net).
# Nota: Este é um exemplo simplificado. Em produção, você usaria volumes para persistir dados.
docker run -d --name my-db --network meu-projeto-net \
-e POSTGRES_USER=user \
-e POSTGRES_PASSWORD=password \
-e POSTGRES_DB=mydb \
postgres:16-alpine
O contêiner my-db está agora rodando e seu nome foi registrado no DNS da rede meu-projeto-net.
Passo 3: Inicie o contêiner da API
Agora, vamos iniciar nossa API. Supondo que você já tenha uma imagem chamada minha-api:1.0, o comando seria:
docker run -d --name minha-api --network meu-projeto-net \
-p 8080:8080 \
minha-api:1.0
O Ponto Chave: A String de Conexão
Dentro do código da sua API .NET, a string de conexão no appsettings.json não usará localhost ou um IP. Ela usará o nome do contêiner do banco de dados:
{
"ConnectionStrings": {
"DefaultConnection": "Host=my-db;Port=5432;Database=mydb;Username=user;Password=password"
}
}
Com essa configuração, sua API fará uma consulta DNS para my-db, o Docker resolverá para o IP interno correto do contêiner do banco de dados e a conexão será estabelecida. Simples, robusto e portátil.
Test your understanding!
Você está montando um sistema com dois serviços: um serviço de auth-service e um serviço de user-profile-service. O user-profile-service precisa fazer uma chamada HTTP para o auth-service no endpoint http://auth-service/validate-token.
Escreva a sequência de comandos docker necessários para:
- Criar uma rede chamada
servicos-internos. - Rodar um contêiner (pode usar a imagem
nginx:alpinepara simular) chamadoauth-servicenessa rede. - Rodar um segundo contêiner (use
curlimages/curl) para simular ouser-profile-servicee fazer uma chamada parahttp://auth-service/.
Show answer
A sequência de comandos seria:
# 1. Criar a rede
docker network create servicos-internos
# 2. Rodar o serviço de autenticação
docker run -d --name auth-service --network servicos-internos nginx:alpine
# 3. Rodar um contêiner temporário para fazer a chamada
# O comando irá executar, fazer o curl, imprimir o HTML do nginx e o contêiner será removido (--rm)
docker run --rm --network servicos-internos curlimages/curl http://auth-service/
Se o último comando retornar o HTML da página de boas-vindas do Nginx, significa que a resolução de nome DNS funcionou corretamente.
Conclusão
Hoje, você adicionou uma peça fundamental ao seu conhecimento de Docker, uma que é vital para a construção de sistemas distribuídos.
Principais pontos a reter:
- Comunicação por IP é frágil: Endereços IP de contêineres são efêmeros e não devem ser usados para configuração.
- Redes
bridgepersonalizadas fornecem DNS: Esta é a principal razão para sempre criar redes dedicadas para suas aplicações multi-serviço. - O nome do contêiner é o hostname: Use o nome que você define com
--namediretamente em suas strings de conexão ou URLs de API. - Isolamento e Descoberta: Redes personalizadas oferecem o melhor de dois mundos: isolamento do mundo exterior e descoberta de serviço automática no interior.
Até agora, cobrimos como os contêineres se comunicam entre si e com o mundo exterior. No entanto, há um problema crítico que não abordamos: o que acontece com os dados do seu banco de dados se você remover o contêiner my-db? Eles são perdidos para sempre. Na próxima lição, vamos resolver esse problema crucial explorando a diferença entre volumes, bind mounts e armazenamento persistente.