DevOps

Docker Volumes e Redes: Persistência e Comunicação Já leu

8 min de leitura

Docker Volumes e Redes: Persistência e Comunicação
Containers são projetados para ser efêmeros. Quando um container é removido, tudo que foi escrito em seu sistema de arquivos desaparece junto. Para uma aplicação web stateless isso é perfeito — mas para um banco de dados

Containers são projetados para ser efêmeros. Quando um container é removido, tudo que foi escrito em seu sistema de arquivos desaparece junto. Para uma aplicação web stateless isso é perfeito — mas para um banco de dados, um sistema de arquivos de uploads ou qualquer serviço que precisa persistir dados entre reinicializações, essa característica é um problema.

Da mesma forma, containers são isolados por padrão — um container não enxerga os outros. Uma aplicação Node.js em um container não consegue se conectar a um PostgreSQL em outro container sem que essa comunicação seja explicitamente configurada.

Volumes e redes são as duas funcionalidades do Docker que resolvem esses dois problemas. Entendê-las é essencial para compor sistemas realistas com múltiplos serviços.


Volumes: Persistindo Dados

Um volume é um mecanismo de armazenamento gerenciado pelo Docker que existe independentemente do ciclo de vida dos containers. Quando um container é removido, o volume permanece intacto — e pode ser montado em um novo container.

Existem três formas de montar armazenamento externo em um container:

Volumes gerenciados pelo Docker — o Docker controla onde os dados ficam no host (/var/lib/docker/volumes/). É a forma recomendada para a maioria dos casos.

Bind mounts — mapeia um diretório específico do host para dentro do container. O desenvolvedor controla exatamente onde os dados ficam. Muito usado em desenvolvimento para que mudanças no código local reflitam imediatamente no container.

tmpfs mounts — armazenamento em memória, não persiste em disco. Útil para dados sensíveis temporários.


Trabalhando com Volumes

# Cria um volume gerenciado
docker volume create dados-postgres

# Lista volumes existentes
docker volume ls

# Inspeciona um volume — mostra onde os dados ficam no host
docker volume inspect dados-postgres

# Remove um volume
docker volume rm dados-postgres

# Remove volumes não utilizados por nenhum container
docker volume prune

Montando o volume em um container de banco de dados:

docker run -d \
  --name postgres \
  -e POSTGRES_USER=app \
  -e POSTGRES_PASSWORD=senha123 \
  -e POSTGRES_DB=producao \
  -v dados-postgres:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16

O flag -v dados-postgres:/var/lib/postgresql/data instrui o Docker a montar o volume dados-postgres no caminho /var/lib/postgresql/data dentro do container — que é exatamente onde o PostgreSQL armazena seus arquivos de dados.

Para verificar que a persistência funciona:

# Conecta ao banco e cria uma tabela
docker exec -it postgres psql -U app -d producao -c \
  "CREATE TABLE teste (id serial, nome text);"

docker exec -it postgres psql -U app -d producao -c \
  "INSERT INTO teste (nome) VALUES ('dado persistido');"

# Remove o container
docker rm -f postgres

# Sobe um novo container com o mesmo volume
docker run -d \
  --name postgres-novo \
  -e POSTGRES_USER=app \
  -e POSTGRES_PASSWORD=senha123 \
  -e POSTGRES_DB=producao \
  -v dados-postgres:/var/lib/postgresql/data \
  postgres:16

# Verifica que os dados ainda existem
docker exec -it postgres-novo psql -U app -d producao -c \
  "SELECT * FROM teste;"

A tabela e os dados estarão presentes no novo container — o volume sobreviveu à remoção do container original.


Bind Mounts em Desenvolvimento

Em desenvolvimento, é comum usar bind mounts para que alterações no código local reflitam imediatamente dentro do container, sem precisar reconstruir a imagem:

docker run -d \
  --name app-dev \
  -v $(pwd)/src:/app/src \
  -v $(pwd)/package.json:/app/package.json \
  -p 3000:3000 \
  minha-app:dev

O $(pwd) captura o diretório atual. O resultado é que /app/src dentro do container aponta diretamente para ./src na máquina local — qualquer arquivo salvo no editor de texto aparece instantaneamente no container.

Em produção, bind mounts devem ser evitados — a imagem deve ser autossuficiente e não depender de arquivos do host.


Redes Docker: Comunicação entre Containers

Por padrão, cada container tem sua própria interface de rede isolada. Para que containers se comuniquem, precisam estar na mesma rede Docker.

O Docker cria automaticamente três redes ao ser instalado:

bridge — a rede padrão. Containers conectados a ela podem se comunicar via IP, mas não por nome.

host — remove o isolamento de rede. O container usa diretamente a interface de rede do host. Útil em casos específicos de performance, mas sacrifica isolamento.

none — sem rede. O container é completamente isolado.


Criando e Usando Redes Customizadas

Redes customizadas são superiores à rede bridge padrão por um motivo fundamental: elas oferecem resolução de nomes automática. Containers na mesma rede customizada podem se referenciar pelo nome do container, sem precisar conhecer IPs.

# Cria uma rede customizada
docker network create rede-app

# Lista redes existentes
docker network ls

# Inspeciona uma rede — mostra containers conectados e configurações
docker network inspect rede-app

# Remove uma rede
docker network rm rede-app

Subindo múltiplos serviços na mesma rede:

# Sobe o banco de dados na rede customizada
docker run -d \
  --name postgres \
  --network rede-app \
  -e POSTGRES_USER=app \
  -e POSTGRES_PASSWORD=senha123 \
  -e POSTGRES_DB=producao \
  -v dados-postgres:/var/lib/postgresql/data \
  postgres:16

# Sobe o Redis na mesma rede
docker run -d \
  --name redis \
  --network rede-app \
  redis:7-alpine

# Sobe a aplicação na mesma rede
docker run -d \
  --name api \
  --network rede-app \
  -e DATABASE_URL=postgresql://app:senha123@postgres:5432/producao \
  -e REDIS_URL=redis://redis:6379 \
  -p 3000:3000 \
  minha-app:1.0.0

Observa-se que a variável DATABASE_URL usa postgres como hostname — o nome do container — e não um endereço IP. O Docker resolve esse nome automaticamente dentro da rede rede-app. Da mesma forma, redis como hostname aponta para o container Redis.


Verificando Conectividade entre Containers

Para testar se um container consegue alcançar outro:

# Entra no container da API
docker exec -it api sh

# Testa conectividade com o banco
ping postgres

# Testa a porta do banco
nc -zv postgres 5432

# Testa o Redis
nc -zv redis 6379

exit

Se os containers estão na mesma rede customizada, todos esses testes devem ter sucesso.


Conectando um Container a Múltiplas Redes

Um container pode pertencer a mais de uma rede simultaneamente — útil para separar tráfego de frontend e backend:

# Cria redes separadas
docker network create rede-frontend
docker network create rede-backend

# O container de API pertence às duas redes
docker network connect rede-frontend api
docker network connect rede-backend api

# O banco pertence apenas ao backend
docker network connect rede-backend postgres

# O container de frontend pertence apenas ao frontend
docker run -d \
  --name frontend \
  --network rede-frontend \
  -p 80:80 \
  meu-frontend:1.0.0

Nessa configuração, o container frontend não consegue se comunicar diretamente com postgres — só com api. A API faz a ponte entre as duas redes, respeitando o princípio de menor exposição.


Um Ambiente Completo sem Docker Compose

Reunindo volumes e redes em um ambiente com três serviços:

# Cria infraestrutura
docker network create rede-producao
docker volume create dados-postgres
docker volume create dados-redis

# Banco de dados
docker run -d \
  --name postgres \
  --network rede-producao \
  -v dados-postgres:/var/lib/postgresql/data \
  -e POSTGRES_USER=app \
  -e POSTGRES_PASSWORD=senha_segura \
  -e POSTGRES_DB=minha_app \
  postgres:16

# Cache
docker run -d \
  --name redis \
  --network rede-producao \
  -v dados-redis:/data \
  redis:7-alpine redis-server --appendonly yes

# Aplicação
docker run -d \
  --name api \
  --network rede-producao \
  -e DATABASE_URL=postgresql://app:senha_segura@postgres:5432/minha_app \
  -e REDIS_URL=redis://redis:6379 \
  -e NODE_ENV=production \
  -p 3000:3000 \
  minha-app:1.0.0

# Verifica tudo rodando
docker ps
docker network inspect rede-producao

Esse conjunto de comandos funciona, mas já evidencia a necessidade do Docker Compose — gerenciar múltiplos containers manualmente se torna tedioso e propenso a erros rapidamente.


O Que Vem a Seguir

O próximo artigo apresenta o Docker Compose — a ferramenta que descreve em um único arquivo YAML toda a infraestrutura que foi construída com múltiplos comandos neste artigo. O ambiente inteiro sobe com um único docker compose up.


Referências para Aprofundamento

Documentação oficial

Leitura técnica

Prática

  • Play with Docker — O ambiente permite criar múltiplos nós e praticar redes entre containers sem nenhuma instalação local.
Comentários

Mais em DevOps

GitLab Self-Hosted: Soberania Total sobre Código e Pipelines
GitLab Self-Hosted: Soberania Total sobre Código e Pipelines

Há contextos em que usar um serviço SaaS para hospedar código e pipelines não...

Gerando e Revisando Pipelines com IA
Gerando e Revisando Pipelines com IA

O GitHub Copilot foi lançado em 2021. O ChatGPT em novembro de 2022. O Cursor...

Publicando Imagens no Docker Hub e GitHub Container Registry
Publicando Imagens no Docker Hub e GitHub Container Registry

Uma imagem que existe apenas na máquina do desenvolvedor não serve a nenhum p...