Volumes: persistindo dados em containers
1. Introdução à persistência de dados em containers
Containers são, por natureza, efêmeros. Quando um container é removido, todos os dados escritos em seu sistema de arquivos são perdidos. Essa característica, embora desejável para garantir imutabilidade e escalabilidade, torna-se um problema crítico quando precisamos armazenar dados de banco de dados, logs de aplicação, uploads de usuários ou qualquer informação que precise sobreviver ao ciclo de vida do container.
Para resolver esse problema, o Docker introduziu o conceito de volumes: mecanismos que permitem persistir dados fora do sistema de arquivos do container, mantendo-os disponíveis mesmo após a remoção e recriação do container.
Existem três tipos principais de montagem no Docker:
- Volumes gerenciados pelo Docker: armazenados em área gerenciada pelo daemon (
/var/lib/docker/volumes/) - Bind mounts: mapeamento direto de um diretório do host para dentro do container
- tmpfs mounts: dados armazenados exclusivamente em memória RAM (voláteis)
2. Tipos de montagem no Docker
Volumes gerenciados pelo Docker
São a forma recomendada para produção. O Docker gerencia todo o ciclo de vida, incluindo backup e migração.
docker volume create meu-volume
docker volume ls
docker volume inspect meu-volume
Bind mounts
Mapeiam diretamente um caminho do host para o container. Úteis em desenvolvimento para compartilhar código-fonte.
docker run -v /host/caminho:/container/caminho nginx
tmpfs mounts
Dados em RAM, perdidos ao parar o container. Ideais para dados sensíveis que não devem ser persistidos em disco.
docker run --tmpfs /app/temp:rw,noexec,nosuid,size=64m nginx
3. Trabalhando com volumes no Docker
Comandos essenciais
# Criar volume
docker volume create dados-mysql
# Listar volumes
docker volume ls
# Inspecionar volume
docker volume inspect dados-mysql
# Remover volume
docker volume rm dados-mysql
Montando volumes: flag -v vs --mount
A flag -v é mais concisa, enquanto --mount é mais explícita e recomendada para scripts de produção.
# Usando -v (modo antigo)
docker run -v dados-mysql:/var/lib/mysql mysql:8.0
# Usando --mount (modo explícito)
docker run --mount source=dados-mysql,target=/var/lib/mysql mysql:8.0
Exemplo prático: persistindo dados do MySQL
# Criar volume para dados do MySQL
docker volume create mysql-data
# Executar container MySQL com volume persistente
docker run -d \
--name mysql-db \
--mount source=mysql-data,target=/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=minhasenha \
mysql:8.0
# Parar e remover o container (dados persistem no volume)
docker stop mysql-db
docker rm mysql-db
# Recriar o container montando o mesmo volume
docker run -d \
--name mysql-db-novo \
--mount source=mysql-data,target=/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=minhasenha \
mysql:8.0
4. Gerenciamento de permissões e ownership
Um problema comum ao usar volumes é o conflito de UID/GID entre o container e o host. O container pode executar processos como mysql (UID 999) enquanto o host espera outro UID.
Boas práticas
# No Dockerfile, definir usuário explícito
FROM alpine:3.18
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
Exemplo: volume compartilhado entre containers
# Criar volume compartilhado
docker volume create shared-data
# Container escritor (UID 1000)
docker run -d --name escritor \
--mount source=shared-data,target=/dados \
alpine sh -c "while true; do echo 'dado' >> /dados/arquivo.txt; sleep 5; done"
# Container leitor (UID 1001)
docker run -d --name leitor \
--mount source=shared-data,target=/dados \
alpine sh -c "while true; do cat /dados/arquivo.txt; sleep 10; done"
5. Backup e restauração de volumes
Backup de volume para arquivo tar
# Backup do volume mysql-data para arquivo tar
docker run --rm \
-v mysql-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/mysql-backup-$(date +%Y%m%d).tar.gz -C /data .
Restauração de backup
# Criar volume vazio
docker volume create mysql-data-restaurado
# Restaurar backup para o volume
docker run --rm \
-v mysql-data-restaurado:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/mysql-backup-20240101.tar.gz -C /data
Automação com cron
# Script de backup automático (backup.sh)
#!/bin/bash
docker run --rm \
-v mysql-data:/data \
-v /backup:/backup \
alpine tar czf /backup/mysql-$(date +%Y%m%d-%H%M).tar.gz -C /data .
# Agendar no crontab (executar diariamente às 2h)
0 2 * * * /caminho/backup.sh
6. Volumes no Docker Compose
O Docker Compose simplifica o gerenciamento de volumes em ambientes multi-container.
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- web-data:/usr/share/nginx/html
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: appdb
volumes:
- db-data:/var/lib/mysql
volumes:
web-data:
db-data:
7. Volumes no Kubernetes (Persistent Volumes)
No Kubernetes, o gerenciamento de armazenamento persistente é feito através de três objetos principais:
- PersistentVolume (PV): recurso de armazenamento no cluster
- PersistentVolumeClaim (PVC): solicitação de armazenamento pelo usuário
- StorageClass: provisionamento dinâmico de PVs
Exemplo: PVC e Pod
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
# deployment-mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpass"
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
persistentVolumeClaim:
claimName: mysql-pvc
StatefulSet com volume persistente
Para bancos de dados, recomenda-se usar StatefulSet, que garante identidade única para cada pod e volumes estáveis.
# statefulset-mysql.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
volumeMounts:
- name: data
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
8. Boas práticas e considerações finais
- Produção: prefira volumes gerenciados pelo Docker ou PVs no Kubernetes
- Desenvolvimento: bind mounts são úteis para hot-reload de código
- Limpeza: use
docker volume pruneregularmente para remover volumes órfãos - Monitoramento: configure alertas para uso de disco nos volumes
- Segurança: evite tmpfs para dados que precisam ser recuperados após crash
- Backup: automatize backups com scripts e ferramentas como Velero no Kubernetes
Lembre-se: dados são o ativo mais valioso de qualquer aplicação. Trate volumes com o mesmo cuidado que trata seus bancos de dados em servidores tradicionais.
Referências
- Docker Documentation: Manage data in Docker — Documentação oficial sobre todos os tipos de montagem no Docker
- Docker Volumes: Guia Completo — Tutorial prático da DigitalOcean sobre compartilhamento de dados entre containers
- Kubernetes: Persistent Volumes — Documentação oficial do Kubernetes sobre PVs e PVCs
- Kubernetes: StatefulSet — Guia oficial sobre StatefulSets para aplicações stateful
- Backup e Restore de Volumes Docker — Artigo técnico da Baeldung sobre estratégias de backup de volumes
- Storage Classes no Kubernetes — Documentação oficial sobre provisionamento dinâmico de armazenamento