Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 20

Custom Bridge Networks and DNS for Container Communication

Recommended resources

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.

Bridge network driver

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.

Docker Bridge Network for Container Communication
Este diagrama ilustra o conceito. Um contêiner 'web' pode se conectar a um contêiner 'db' usando o nome 'db' (hostname), sem precisar saber seu endereço IP interno (172.17.0.x). A rede bridge lida com a resolução de nomes para o endereço IP correto.

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:

  1. Na rede bridge padrão, curl myapp-v2 falha.
  2. Após criar a rede my-bridge-net e conectar os contêineres a ela, curl myapp-v2 funciona.

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:

  1. Criar uma rede chamada servicos-internos.
  2. Rodar um contêiner (pode usar a imagem nginx:alpine para simular) chamado auth-service nessa rede.
  3. Rodar um segundo contêiner (use curlimages/curl) para simular o user-profile-service e fazer uma chamada para http://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 bridge personalizadas 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 --name diretamente 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.