<h2>Introdução: A Importância da Comunicação e Persistência de Dados em Containers</h2>
<p>Quando começamos a trabalhar com Docker, rapidamente percebemos que containers isolados são úteis, mas limitados. A verdadeira potência do Docker emerge quando precisamos orquestrar múltiplos containers que conversam entre si ou quando necessitamos persistir dados de forma confiável. Este artigo aborda exatamente isso: como criar redes sofisticadas entre containers e gerenciar volumes de dados de forma profissional.</p>
<p>Nos próximos tópicos, você compreenderá não apenas <em>como</em> usar bridge networks, overlay networks e bind mounts, mas <em>por que</em> cada uma existe e em que contextos específicos aplicá-las. Vou partir do pressuposto que você já conhece os fundamentos básicos do Docker (containers, images, Docker daemon), e focaremos em casos de uso reais.</p>
<h2>Docker Bridge Networks: Comunicação em Host Único</h2>
<h3>O que é uma Bridge Network?</h3>
<p>A bridge network é o driver padrão de rede do Docker quando você cria uma rede customizada. Diferente da bridge padrão (<code>docker0</code>), que apresenta limitações severas, uma bridge customizada oferece DNS automático entre containers, isolamento de rede melhorado e configuração simplificada. Ela é a escolha ideal para comunicação entre containers em um único host.</p>
<p>Quando um container se conecta a uma bridge network customizada, o Docker automaticamente resolve o hostname do container para seu endereço IP interno. Isso significa que você não precisa gerenciar IPs manualmente — pode referenciar containers pelo seu nome. Essa é uma diferença crucial em relação à bridge padrão, onde containers precisavam ser linkados explicitamente.</p>
<h3>Criando e Testando uma Bridge Network</h3>
<p>Vou demonstrar com um exemplo prático: dois containers (um com Nginx e outro com uma aplicação cliente) que precisam se comunicar.</p>
<pre><code class="language-bash"># Criar uma bridge network customizada
docker network create minha-bridge --driver bridge
Verificar a rede criada
docker network inspect minha-bridge</code></pre>
<p>Agora vamos criar um container de banco de dados e um container de aplicação que precisa acessá-lo:</p>
<pre><code class="language-bash"># Container 1: PostgreSQL
docker run -d \
--name postgres-db \
--network minha-bridge \
-e POSTGRES_PASSWORD=senha123 \
-e POSTGRES_USER=app_user \
postgres:15-alpine
Container 2: Uma aplicação que conecta ao PostgreSQL
docker run -d \
--name app-node \
--network minha-bridge \
-e DATABASE_HOST=postgres-db \
-e DATABASE_USER=app_user \
-e DATABASE_PASSWORD=senha123 \
node:18-alpine sleep 1000</code></pre>
<p>O ponto crucial aqui: o container <code>app-node</code> consegue resolver <code>postgres-db</code> para o IP correto automaticamente. Internamente, o Docker mantém um resolver DNS que mapeia nomes de containers para IPs.</p>
<pre><code class="language-bash"># Testar conectividade (dentro do container app-node)
docker exec app-node ping postgres-db
Resposta esperada: resolução bem-sucedida</code></pre>
<h3>Limitações e Quando Não Usar Bridge</h3>
<p>Bridge networks funcionam apenas em um único host Docker. Se você tem múltiplos hosts Docker (swarm ou Kubernetes), containers em hosts diferentes não conseguem se comunicar através de uma bridge network simples. Para isso, existem as overlay networks, que veremos a seguir.</p>
<h2>Docker Overlay Networks: Orquestração Multi-Host</h2>
<h3>Quando Docker Swarm Entra em Cena</h3>
<p>Overlay networks foram criadas especificamente para comunicação entre containers distribuídos em múltiplos hosts. Elas funcionam encapsulando o tráfego de rede entre hosts usando VXLAN (Virtual Extensible LAN), criando uma camada de abstração que torna a comunicação transparente — você programa como se todos os containers estivessem no mesmo host.</p>
<p>Diferente das bridge networks, overlay networks requerem que o Docker esteja operando em modo Swarm (orquestração nativa do Docker). Em um cluster Swarm, o Docker mantém um banco de dados distribuído de configurações que todos os nós acessam, permitindo que redes overlay funcionem perfeitamente através de múltiplos hosts.</p>
<h3>Inicializando Docker Swarm e Criando uma Overlay Network</h3>
<pre><code class="language-bash"># Inicializar o Swarm no seu host (host1)
docker swarm init
Resultado esperado: token de join para adicionar workers
docker swarm join --token SWMTKN-1-... <ip>:<porta>
Se você tiver outro host, execute o comando de join nele
Para este exemplo, simulamos localmente com apenas um manager
Criar uma overlay network
docker network create --driver overlay meu-overlay-swarm</code></pre>
<p>Agora criamos um serviço (não apenas um container) nessa rede:</p>
<pre><code class="language-bash"># Criar um serviço de banco de dados em modo overlay
docker service create \
--name postgres-service \
--network meu-overlay-swarm \
-e POSTGRES_PASSWORD=senha456 \
postgres:15-alpine
Criar um serviço de aplicação que depende do banco
docker service create \
--name app-service \
--network meu-overlay-swarm \
-e DATABASE_HOST=postgres-service \
alpine sleep 1000
Listar serviços
docker service ls</code></pre>
<p>A diferença fundamental: aqui usamos <code>docker service</code> em vez de <code>docker run</code>. Serviços são entidades de mais alto nível que mantêm o container rodando, podem ser escalados e distribuídos automaticamente pelo Swarm.</p>
<pre><code class="language-bash"># Verificar detalhe da rede overlay
docker network inspect meu-overlay-swarm
Você verá que a rede tem escopo "swarm" (não "local" como bridge)</code></pre>
<h3>Comparação Prática: Bridge vs Overlay</h3>
<div class="table-wrap"><table><thead><tr><th>Aspecto</th><th>Bridge Network</th><th>Overlay Network</th></tr></thead><tbody><tr><td><strong>Escopo</strong></td><td>Um host Docker</td><td>Múltiplos hosts Docker</td></tr><tr><td><strong>Requisito</strong></td><td>Docker daemon rodando</td><td>Docker em modo Swarm</td></tr><tr><td><strong>DNS Interno</strong></td><td>✓ Automático</td><td>✓ Automático</td></tr><tr><td><strong>Encapsulamento</strong></td><td>Nenhum (L2)</td><td>VXLAN (L3)</td></tr><tr><td><strong>Performance</strong></td><td>Mais rápido</td><td>Ligeiramente mais lento (overhead de encapsulamento)</td></tr><tr><td><strong>Use Case</strong></td><td>Desenvolvimento local, Docker Compose</td><td>Produção com múltiplos nós</td></tr></tbody></table></div>
<h2>Volumes e Bind Mounts: Persistência e Compartilhamento de Dados</h2>
<h3>Volumes vs Bind Mounts: Uma Distinção Crucial</h3>
<p>Docker oferece dois mecanismos principais para persistir dados: <strong>volumes gerenciados pelo Docker</strong> e <strong>bind mounts</strong>. Essa é uma distinção que frequentemente confunde iniciantes, mas é fundamental para trabalhar corretamente em produção.</p>
<p>Um <strong>volume</strong> é um caminho de armazenamento completamente gerenciado pelo Docker. O Docker decide onde armazenar os dados no host (normalmente em <code>/var/lib/docker/volumes/</code>), e você não precisa se preocupar com detalhes. Volumes são agnósticos ao sistema de arquivos host, portáveis entre sistemas operacionais e fáceis de fazer backup.</p>
<p>Um <strong>bind mount</strong> mapeia um diretório específico do host (que você controla) diretamente para um caminho dentro do container. É mais explícito, permite acesso direto aos arquivos do host, mas é também mais frágil — se o diretório host não existir, você terá problemas.</p>
<h3>Criando e Usando Volumes Gerenciados</h3>
<pre><code class="language-bash"># Criar um volume nomeado
docker volume create dados-aplicacao
Listar volumes existentes
docker volume ls
Inspecionar um volume (ver onde está armazenado fisicamente)
docker volume inspect dados-aplicacao</code></pre>
<p>Resultado esperado:</p>
<pre><code class="language-json">[
{
"Name": "dados-aplicacao",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/dados-aplicacao/_data",
"Labels": {},
"Scope": "local"
}
]</code></pre>
<p>Agora usamos esse volume em um container:</p>
<pre><code class="language-bash"># Container que escreve dados em um volume
docker run -d \
--name app-com-volume \
--volume dados-aplicacao:/app/data \
-e LOG_FILE=/app/data/app.log \
alpine sh -c 'while true; do echo "$(date): evento registrado" >> /app/data/app.log; sleep 5; done'
Aguardar alguns segundos e verificar os dados
sleep 10
Acessar o arquivo de log diretamente no volume
docker exec app-com-volume cat /app/data/app.log</code></pre>
<p>A vantagem aqui é portabilidade: se você mover esse container para outro host Docker, o volume pode ser facilmente restaurado desde que você tenha feito backup dos dados.</p>
<h3>Bind Mounts: Compartilhamento Bidirecional</h3>
<p>Bind mounts são extremamente úteis em desenvolvimento, onde você quer que mudanças no seu código local se reflitam imediatamente dentro do container.</p>
<pre><code class="language-bash"># Criar um diretório local (ele deve existir)
mkdir -p ~/meu-projeto/data
Container com bind mount
docker run -d \
--name app-dev \
--mount type=bind,source=$(pwd)/meu-projeto,target=/workspace \
-v ~/meu-projeto/data:/app/persistido \
node:18-alpine sleep 1000
Criar um arquivo no host
echo "Criado do host" > ~/meu-projeto/arquivo-teste.txt
Verificar dentro do container
docker exec app-dev cat /workspace/arquivo-teste.txt
Resultado: "Criado do host"
Agora criar um arquivo dentro do container
docker exec app-dev sh -c 'echo "Criado do container" > /workspace/container-arquivo.txt'
Verificar no host
cat ~/meu-projeto/container-arquivo.txt
Resultado: "Criado do container"</code></pre>
<p>Esse bidirecionalismo é perfeito para desenvolvimento, mas perigoso em produção — alguém pode acidentalmente modificar arquivos críticos do host.</p>
<h3>Estratégia de Persistência em Produção</h3>
<p>Em produção, recomendo uma abordagem estruturada:</p>
<pre><code class="language-bash"># Criar volumes para diferentes componentes
docker volume create db-data
docker volume create cache-data
docker volume create logs-data
Composição de um stack com volumes apropriados
docker service create \
--name postgres-prod \
--volume db-data:/var/lib/postgresql/data \
--network producao-overlay \
postgres:15-alpine
docker service create \
--name redis-prod \
--volume cache-data:/data \
--network producao-overlay \
redis:7-alpine
docker service create \
--name app-prod \
--volume logs-data:/var/log/aplicacao \
--network producao-overlay \
minha-app:latest</code></pre>
<p>Cada componente tem seu próprio volume, facilitando backup, restore e troubleshooting. Volumes gerenciados também possuem suporte a drivers customizados (NFS, iSCSI, cloud storage), permitindo arquitetura escalável.</p>
<h3>Permissões e Propriedade de Arquivos</h3>
<p>Um detalhe frequentemente ignorado: o UID/GID dos arquivos dentro do volume. Quando um container escreve em um volume, a propriedade do arquivo reflete o usuário dentro do container.</p>
<pre><code class="language-bash"># Entender o mapping de UIDs
docker run -d \
--name uid-test \
--volume dados-aplicacao:/dados \
alpine sh -c 'id > /dados/uid-info.txt; sleep 100'
Verificar no host (precisa de sudo)
sudo cat /var/lib/docker/volumes/dados-aplicacao/_data/uid-info.txt
Mostrará: uid=0(root) gid=0(root) (se rodou como root)
Solução: executar container com usuário específico
docker run -d \
--name uid-test-user \
--user 1000:1000 \
--volume dados-aplicacao:/dados \
alpine sh -c 'id > /dados/uid-info-user.txt; sleep 100'</code></pre>
<h2>Integração Prática: Um Stack Completo com Bridge, Overlay e Volumes</h2>
<h3>Cenário: Microserviços em Desenvolvimento e Produção</h3>
<p>Vamos consolidar tudo em um exemplo realista. Imagine uma aplicação com:</p>
<ul>
<li><strong>API em Node.js</strong> que precisa de um banco PostgreSQL</li>
<li><strong>Cache em Redis</strong> para performance</li>
<li><strong>Arquivo de logs</strong> persistido</li>
<li><strong>Dados do banco</strong> sempre salvos</li>
</ul>
<p>Para <strong>desenvolvimento local</strong> (usando bridge + bind mount):</p>
<pre><code class="language-bash"># Criar a bridge network
docker network create dev-network
PostgreSQL com volume local
docker run -d \
--name postgres-dev \
--network dev-network \
--volume postgres-dev-data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=dev123 \
postgres:15-alpine
Redis
docker run -d \
--name redis-dev \
--network dev-network \
redis:7-alpine
Aplicação Node (com bind mount para código)
docker run -d \
--name app-dev \
--network dev-network \
--mount type=bind,source=$(pwd)/src,target=/app/src \
--volume app-logs:/app/logs \
-e DB_HOST=postgres-dev \
-e REDIS_HOST=redis-dev \
-p 3000:3000 \
node:18-alpine node /app/src/index.js</code></pre>
<p>Para <strong>produção</strong> (usando overlay + volumes gerenciados):</p>
<pre><code class="language-bash"># Iniciar swarm (apenas uma vez)
docker swarm init
Criar overlay network
docker network create --driver overlay producao-network
Serviços
docker service create \
--name postgres-prod \
--network producao-network \
--volume postgres-prod-data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=$(openssl rand -base64 12) \
postgres:15-alpine
docker service create \
--name redis-prod \
--network producao-network \
--volume redis-prod-data:/data \
redis:7-alpine
docker service create \
--name app-prod \
--network producao-network \
--volume app-prod-logs:/app/logs \
-e DB_HOST=postgres-prod \
-e REDIS_HOST=redis-prod \
--publish 80:3000 \
minha-app-image:latest</code></pre>
<p>A transição entre desenvolvimento e produção é clara: mudamos driver de rede (bridge → overlay), mecanismo de execução (docker run → docker service), e vinculação de volumes (bind mount → volume gerenciado).</p>
<h2>Troubleshooting Comum</h2>
<h3>Containers não conseguem se comunicar</h3>
<p>Verificação passo a passo:</p>
<pre><code class="language-bash"># 1. Confirmar que ambos estão na mesma rede
docker inspect container1 | grep -A 5 "Networks" docker inspect container2 | grep -A 5 "Networks"
2. Testar DNS
docker exec container1 nslookup container2
Se falhar, eles não estão na mesma rede customizada
3. Verificar firewalls/iptables
docker network inspect nome-rede
Procura por "Containers" e confirme que ambos aparecem
4. Testar conectividade em baixo nível
docker exec container1 ping container2
docker exec container1 nc -zv container2 5432 # Para aplicações de rede</code></pre>
<h3>Volumes não aparecem onde esperado</h3>
<pre><code class="language-bash"># Confirmar mount point
docker inspect --format='{{json .Mounts}}' nome-container | jq .
Verificar permissões
docker exec nome-container ls -la /caminho/no/container
Para bind mount, confirmar que source existe no host
ls -la /seu/caminho/local</code></pre>
<h3>Overlay network não funciona entre hosts</h3>
<pre><code class="language-bash"># Confirmar que ambos hosts estão no mesmo Swarm
docker node ls
Deve listar ambos os nós
Verificar conectividade de rede (portas necessárias: 2377, 7946, 4789)
Entre os hosts
ss -tuln | grep -E '2377|7946|4789'
Inspecionar a rede overlay no detalhe
docker network inspect nome-overlay
Procura por peers em ambos os nós</code></pre>
<h2>Conclusão</h2>
<p>Compreender redes bridge, overlay e volumes não é apenas dominar sintaxe — é entender as abstrações que Docker fornece para resolver problemas reais de comunicação e persistência.</p>
<p>Primeiro aprendizado: <strong>bridge networks são suficientes e melhores para desenvolvimento local</strong>, enquanto <strong>overlay networks escalam para produção multi-host</strong> com o custo de complexidade adicionada. Escolher entre elas é uma decisão arquitetural que afeta toda sua operação.</p>
<p>Segundo aprendizado: <strong>volumes gerenciados e bind mounts servem propósitos diferentes</strong> — volumes para persistência profissional, bind mounts para desenvolvimento iterativo. Confundir os dois em produção é receita para desastre.</p>
<p>Terceiro aprendizado: <strong>a transição entre ambientes (dev → prod) deve ser clara e previsível</strong>. Se sua aplicação funciona localmente com bridge + bind mount, a versão em Swarm com overlay + volumes deve funcionar igualmente bem, com mudanças apenas no driver de rede e mecanismo de orquestração.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://docs.docker.com/network/" target="_blank" rel="noopener noreferrer">Docker Official Documentation - Networks</a></li>
<li><a href="https://docs.docker.com/storage/volumes/" target="_blank" rel="noopener noreferrer">Docker Official Documentation - Volumes</a></li>
<li><a href="https://docs.docker.com/engine/swarm/" target="_blank" rel="noopener noreferrer">Docker Swarm Mode Documentation</a></li>
<li><a href="https://github.com/moby/moby/blob/master/docs/articles/networking.md" target="_blank" rel="noopener noreferrer">Moby (Docker) GitHub - Networking Deep Dive</a></li>
<li><a href="https://www.amazon.com/Docker-Deep-Dive-Nigel-Poulton/dp/B01LXWQUFF" target="_blank" rel="noopener noreferrer">Nigel Poulton - "Docker Deep Dive" (Capítulos sobre Networking e Storage)</a></li>
</ul>
<p><!-- FIM --></p>