A persistência de dados é um dos conceitos fundamentais para o uso profissional de Docker em ambientes de produção. Por padrão, os containers são efêmeros: quando eles param ou são removidos, todos os dados escritos nas camadas de leitura e escrita do container são perdidos. Para sysadmins, desenvolvedores e equipes de DevOps, isso significa que bancos de dados, logs de aplicação e arquivos de configuração precisam ser armazenados fora do ciclo de vida do container.
Neste tutorial técnico, vamos explorar como garantir a persistência de dados em ambientes Linux utilizando volumes Docker. Abordaremos desde a criação manual até o uso avançado com docker compose, explicando as diferenças entre bind mounts e volumes gerenciados pelo Docker, além de melhores práticas para infraestrutura estável.
O que são Volumes Docker?
Volumes são o mecanismo preferencial para persistir dados gerados e usados por containers Docker. Eles são diretórios (ou dispositivos) especiais que existem fora da hierarquia de arquivos do sistema de arquivos UnionFS do container. Isso oferece várias vantagens críticas em comparação com outras técnicas de montagem:
- Persistência independente: Os dados continuam existindo mesmo se o container for removido.
- Compartilhamento: Múltiplos containers podem montar o mesmo volume simultaneamente.
- Gerenciamento: Volumes podem ser gerenciados usando comandos Docker, facilitando backups e migrações.
- Performance: Em sistemas Linux,
bind mounts(montagens de diretório) podem ter sobrecarga de performance em certos sistemas de arquivos (como NFS), enquanto volumes Docker utilizam o driver nativo do kernel, oferecendo geralmente melhor desempenho para operações de I/O intensivas.
Conceito 1: Bind Mounts vs. Volumes Gerenciados
Antes de executar comandos, é crucial entender a diferença entre as duas principais abordagens de persistência no Linux:
Bind Mounts: Montam um diretório específico do host Linux dentro do container. O caminho no host é absoluto e conhecido pelo administrador. Se você montar /home/user/logs no container, o Docker usa exatamente esse diretório no sistema de arquivos subjacente. Isso é ideal para desenvolvimento local ou quando a aplicação precisa acessar configurações específicas do servidor hospedeiro.
Volumes Gerenciados (Managed Volumes): O Docker cria e gerencia o diretório em um caminho interno, geralmente localizado em /var/lib/docker/volumes/. O administrador não sabe exatamente onde os dados estão fisicamente no disco do host sem consultar o Docker. Isso é ideal para bancos de dados e aplicações stateful em produção, pois isola a aplicação da estrutura de diretórios do servidor.
Passo 1: Criando e Usando Volumes Gerenciados
A maneira mais robusta de persistir dados em produção é criar volumes gerenciados explicitamente. Vamos usar o PostgreSQL como exemplo, pois ele exige persistência rigorosa para evitar perda de dados.
1.1 Criando um Volume via Linha de Comando
No terminal do seu servidor Linux, execute o comando abaixo para criar um volume nomeado chamado postgres-data:
docker volume create postgres-data
Você pode verificar se o volume foi criado listando todos os volumes disponíveis:
docker volume ls
A saída deve exibir algo como local postgres-data.
1.2 Executando o Container com o Volume
Agora, inicie um container PostgreSQL montando esse volume no diretório padrão onde o PostgreSQL armazena seus dados (/var/lib/postgresql/data):
docker run --name postgres-db -e POSTGRES_PASSWORD=senha_segura -v postgres-data:/var/lib/postgresql/data -d postgres:latest
Neste comando, a flag -v (ou --volume) associa o volume nomeado ao diretório interno do container. Note que não especificamos um caminho no host; o Docker cuida disso automaticamente.
1.3 Verificando a Persistência
Para testar, entre no container e crie um arquivo de teste:
docker exec -it postgres-db bash
touch /var/lib/postgresql/data/teste_persistencia.txt
exit
Agora, remova o container:
docker rm -f postgres-db
Se você criar um novo container usando o mesmo volume postgres-data, o arquivo teste_persistencia.txt estará lá. Isso confirma que os dados sobreviveram à remoção do container.
Passo 2: Utilizando Bind Mounts para Desenvolvimento e Configuração
Bind mounts são extremamente úteis quando você precisa editar arquivos de configuração diretamente no host ou compartilhar código fonte entre o host e o container durante o desenvolvimento.
2.1 Estrutura de Diretórios
Crie um diretório local no seu servidor Linux para armazenar os dados da aplicação:
mkdir -p /opt/meu-app/data
mkdir -p /opt/meu-app/config
2.2 Montando o Bind Mount
Inicie um container nginx simples, montando seus diretórios locais:
docker run --name nginx-bind \
-v /opt/meu-app/data:/usr/share/nginx/html \
-v /opt/meu-app/config/nginx.conf:/etc/nginx/nginx.conf:ro \
-d nginx:alpine
Análise da flag -v:
/opt/meu-app/data:/usr/share/nginx/html: Monta o diretório local de dados no diretório padrão de conteúdo do Nginx./opt/meu-app/config/nginx.conf:/etc/nginx/nginx.conf:ro: Monta o arquivo de configuração com a flag:ro(read-only). Isso impede que o container tente escrever no arquivo de configuração, prevenindo erros de permissão e garantindo que as configurações sejam controladas apenas pelo host.
Se você criar um arquivo HTML em /opt/meu-app/data/index.html no host Linux, ele estará imediatamente disponível no endereço IP do container. Alterações no host são refletidas instantaneamente no container, o que é ideal para desenvolvimento rápido.
Passo 3: Orquestração com Docker Compose
Em ambientes reais, raramente usamos comandos docker run isolados. O docker compose é a ferramenta padrão para definir e executar aplicações multi-container. Ele utiliza um arquivo YAML (docker-compose.yml) para definir volumes, serviços e redes.
3.1 Estrutura do Projeto
Crie uma pasta para seu projeto e adicione o arquivo docker-compose.yml:
mkdir -p ~/projeto-webapp
cd ~/projeto-webapp
nano docker-compose.yml
3.2 Definindo Volumes no Compose
Insira o seguinte conteúdo no arquivo. Este exemplo define uma aplicação web (Node.js simulado) e um banco de dados MongoDB, ambos com persistência:
version: '3.8'
services:
webapp:
image: node:18-alpine
container_name: app-container
volumes:
# Bind mount para código fonte (desenvolvimento)
- ./src:/usr/src/app/src
# Volume gerenciado para uploads do usuário
- app-uploads:/usr/src/app/uploads
depends_on:
- database
database:
image: mongo:6
container_name: mongo-db
volumes:
# Volume gerenciado para dados do MongoDB
- mongo-data:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: senha_forte
# Definição dos volumes no escopo raiz
volumes:
app-uploads:
mongo-data:
Explicação Técnica:
- No bloco
volumesde cada serviço, especificamos as montagens. ./src:/usr/src/app/srcé um bind mount relativo ao diretório do compose. Ideal para hot-reload.app-uploads:emongo-data:referenciam volumes nomeados definidos no final do arquivo, sob a chave raizvolumes.
3.3 Iniciando a Stack
Rode o comando para iniciar os serviços em segundo plano:
docker compose up -d
O Docker criará automaticamente os volumes projeto-webapp_app-uploads e projeto-webapp_mongo-data (o prefixo é o nome da pasta do projeto). Verifique com:
docker volume ls
Passo 4: Gerenciamento e Manutenção de Volumes
Como sysadmin, você precisará gerenciar esses volumes ao longo do tempo. Isso inclui backups, limpeza de dados órfãos e migração.
4.1 Identificando Dados Órfãos
Volumes não utilizados por containers consomem espaço em disco. Para remover volumes que não estão associados a nenhum container ativo:
docker volume prune
Para remover volumes órfãos de containers já excluídos, use:
docker volume prune -f
Aviso: Sempre tenha certeza antes de rodar prune, pois isso apaga dados permanentemente.
4.2 Backup de Volumes
Uma prática essencial é backup regular dos volumes. O método mais seguro é usar um container temporário para ler o volume e compactá-lo em um arquivo tar no host.
Para fazer backup do volume mongo-data:
docker run --rm \
-v mongo-data:/volume:ro \
-v $(pwd):/backup \
alpine tar czf /backup/mongo-backup.tar.gz -C /volume .
Este comando:
- Cria um container temporário baseado na imagem
alpine. - Monta o volume
mongo-dataem leitura apenas (:ro) no caminho/volume. - Monta o diretório atual do host em
/backup. - Executa o comando
tarpara compactar todo o conteúdo de/volumee salvar como/backup/mongo-backup.tar.gz.
4.3 Restauração do Backup
Para restaurar, crie um novo volume e descompacte os dados nele:
docker volume create mongo-data-restored
docker run --rm \
-v mongo-data-restored:/volume \
-v $(pwd):/backup \
alpine tar xzf /backup/mongo-backup.tar.gz -C /volume
Agora, você pode iniciar seu banco de dados apontando para mongo-data-restored.
Passo 5: Boas Práticas e Segurança
Para garantir uma infraestrutura Docker sólida no Linux, siga estas diretrizes:
- Use Volumes Gerenciados para Dados Críticos: Nunca armazene dados de bancos de dados em bind mounts diretos do host a menos que haja uma razão específica de compatibilidade. Os volumes gerenciados oferecem melhor isolamento e performance.
- Defina Permissões Adequadas: Ao usar bind mounts, certifique-se de que o usuário dentro do container (UID/GID) tenha permissão de escrita no diretório do host. Use
chownouchmodno host antes de iniciar o container para evitar erros de "Permission denied". - Não Confie em Camadas do Container: Nunca assuma que dados salvos apenas na camada de escrita do container persistirão. Sempre use volumes explícitos.
- Mapeie Portas e Volumes Explicitamente: Em scripts de automação ou arquivos Compose, seja explícito sobre os nomes dos volumes. Isso facilita a leitura e manutenção por outros membros da equipe.
- Monitore o Uso de Disco: Volumes podem crescer rapidamente. Use comandos como
docker system dfpara monitorar quanto espaço os volumes estão consumindo no seu servidor Linux.
Conclusão
A persistência de dados em Docker não é um detalhe secundário, mas sim a base da confiabilidade de seus containers. Ao dominar a diferença entre bind mounts e volumes gerenciados, e ao utilizar ferramentas como docker compose para orquestração, você garante que suas aplicações sejam portáveis, resilientes e fáceis de manter.
Lembre-se: containers são efêmeros, mas os dados devem ser eternos. Planeje sua estratégia de volumes desde o início do desenvolvimento para evitar dores de cabeça em produção.