A gestão eficiente de dados em ambientes containerizados é um dos desafios mais críticos para administradores de sistemas e desenvolvedores que operam em infraestrutura moderna. Migrar dados locais, muitas vezes persistidos incorretamente dentro do sistema de arquivos dos containers ou espalhados pelo host, para volumes docker estruturados é o primeiro passo para garantir portabilidade, segurança e facilidade na manutenção. Este tutorial guia você pela criação de uma arquitetura robusta em uma VPS, utilizando Docker Compose como orquestrador principal.
O cenário comum em ambientes domésticos ou de pequeno porte envolve a instalação direta de aplicações via scripts shell ou pacotes nativos do sistema operacional. Isso cria dependências diretas do host e dificulta atualizações não destrutivas. Ao adotar containers, isolamos as aplicações, mas precisamos resolver o problema da persistência de dados. Sem volumes adequados, qualquer reinicialização ou atualização de imagem pode resultar na perda total de configurações, bancos de dados e arquivos essenciais. Vamos corrigir isso implementando uma stack completa com gerenciamento de tráfego, certificados SSL automáticos e atualizações automatizadas.
Preparação do Ambiente na VPS
Antes de mergulharmos na configuração do Docker, é fundamental garantir que o servidor (VPS) esteja preparado para receber a carga de trabalho. A primeira etapa envolve a instalação das dependências necessárias e a criação da estrutura de diretórios que servirá como raiz para nossos projetos. Evite espalhar arquivos aleatoriamente pelo sistema; crie uma hierarquia lógica.
Comece atualizando o sistema operacional e instalando os pré-requisitos básicos:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git unzip
Agora, vamos criar a estrutura de diretórios padrão para gerenciar nossos serviços. Isso facilita o troubleshooting futuro e mantém o sistema organizado. Criaremos uma pasta base chamada docker-stack, dentro dela criaremos subpastas para cada serviço que pretendemos rodar.
sudo mkdir -p /opt/docker-stack/{traefik,whoami,app-data}
cd /opt/docker-stack
Dentro da pasta docker-stack, cada diretório representará um contexto de serviço. O diretório traefik conterá as configurações do proxy reverso, enquanto app-data simulará onde seus dados locais antigos residem ou onde os novos volumes serão montados. Essa separação física no disco é crucial para backups futuros e migrações.
Configurando o Proxy Reverso com Traefik
O Traefik atua como a porta de entrada para seus containers, gerenciando roteamento de tráfego e, crucialmente, fornecendo SSL automático através do Let's Encrypt. Sem um proxy reverso, cada container precisaria expor suas portas diretamente ao mundo externo, o que é uma prática de segurança arriscada e dificulta a gestão de certificados.
Crie o arquivo de configuração traefik.yml dentro da pasta /opt/docker-stack/traefik/. Este arquivo define as regras de acesso e os backends.
api:
dashboard: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: web
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: "[email protected]"
storage: "/etc/traefik/acme.json"
httpChallenge:
entryPoint: web
Observe a configuração exposedByDefault: false. Isso é uma prática de segurança recomendada. Significa que nenhum container será acessível publicamente a menos que você explicitamente declare isso nas labels do seu serviço no Docker Compose. Isso evita vazamentos acidentais de APIs internas ou painéis de administração.
Agora, crie o arquivo docker-compose.yml na raiz da pasta /opt/docker-stack/traefik/ para iniciar o serviço:
version: '3.8'
services:
traefik:
image: traefik:v2.10
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- acme_data:/etc/traefik/acme.json
networks:
- web
volumes:
acme_data:
networks:
web:
external: true
Note a criação de uma rede externa chamada web. Todas as aplicações que desejam ser acessíveis via internet devem estar conectadas a essa rede. O volume acme_data é essencial para persistir os certificados SSL gerados pelo Let's Encrypt, garantindo que você não precise solicitar novos certificados a cada reinicialização do container.
Migrando Dados Locais para Volumes Docker Eficientes
Aqui está o cerne da migração. Suponha que você tenha uma aplicação local (como um banco de dados SQLite, arquivos de upload ou configurações de CMS) armazenada em /home/user/meu-app/data. O objetivo é mover esses dados para um volume Docker gerenciado ou um bind mount estruturado, garantindo que os dados sobrevivam a atualizações do container.
Crie uma pasta específica para sua aplicação dentro da estrutura criada anteriormente:
mkdir -p /opt/docker-stack/app-data/myapp
Se você já possui dados locais, copie-os para esta nova localização. Isso simula a fase de migração:
# Exemplo: movendo dados existentes
cp -r /home/user/meu-app/data/* /opt/docker-stack/app-data/myapp/
Agora, vamos criar o docker-compose.yml para esta aplicação. O segredo da eficiência aqui é o uso de volumes nominais ou bind mounts explícitos que mapeiam exatamente onde os dados devem viver fora do ciclo de vida do container.
version: '3.8'
services:
myapp:
image: nginx:alpine
container_name: meu-app
restart: unless-stopped
volumes:
- ./myapp:/usr/share/nginx/html:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`meu-app.seudominio.com`)"
- "traefik.http.services.myapp.loadbalancer.server.port=80"
networks:
- web
networks:
web:
external: true
Neste exemplo, utilizamos um bind mount ./myapp:/usr/share/nginx/html:ro. O sufixo :ro (read-only) é uma medida de segurança excelente. Ele impede que o container, se comprometido, altere os arquivos de dados na raiz do host. Se sua aplicação precisar gravar dados, remova o :ro, mas certifique-se de que as permissões de usuário no host estejam corretas para evitar problemas de ownership.
Ao utilizar volumes dessa forma, você desacopla os dados da imagem. Pode atualizar a versão do Nginx ou substituir a aplicação por outra totalmente diferente (como um WordPress ou Nextcloud) simplesmente trocando a imagem e mantendo o mesmo volume de dados montado. Isso é a essência da portabilidade em containers.
Automatizando Atualizações com Watchtower
Manter containers atualizados manualmente é propenso a erros e consome tempo. O Watchtower monitora seu repositório de imagens Docker e atualiza automaticamente os containers em execução quando novas versões são detectadas. Isso garante que você esteja sempre rodando as versões mais recentes e seguras das suas aplicações.
Crie um diretório dedicado ao Watchtower:
mkdir -p /opt/docker-stack/watchtower
cd /opt/docker-stack/watchtower
O docker-compose.yml para o Watchtower é simples, mas requer configuração cuidadosa para evitar atualizações acidentais em momentos críticos.
version: '3.8'
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_POLL_INTERVAL=3600
- WATCHTOWER_INCLUDE_STOPPED=true
- WATCHTOWER_NOTIFICATIONS=email
- [email protected]
labels:
- "com.centurylinklabs.watchtower.enable=false"
networks:
default:
external: true
A variável WATCHTOWER_POLL_INTERVAL=3600 define que o Watchtower verificará atualizações a cada hora. A label com.centurylinklabs.watchtower.enable=false no próprio container do Watchtower é um truque importante para evitar que ele tente atualizar a si mesmo, o que poderia causar loops de reinicialização indesejados.
Orquestrando Tudo com Docker Compose
Agora que temos os componentes individuais, precisamos garantir que eles conversem entre si corretamente. O Docker Compose lida bem com múltiplos arquivos, mas para simplificar o gerenciamento em uma VPS de recursos limitados, muitas vezes é melhor ter um arquivo mestre ou usar docker-compose -f para iniciar os serviços separadamente.
Para este tutorial, recomendamos iniciar o Traefik primeiro, pois ele precisa estar rodando para aceitar as conexões dos outros containers. Em seguida, inicie sua aplicação e, por fim, o Watchtower.
# Iniciar Traefik
cd /opt/docker-stack/traefik
docker compose up -d
# Iniciar Aplicação (ajuste o caminho conforme necessário)
cd /opt/docker-stack/app-data
docker compose up -d
# Iniciar Watchtower
cd /opt/docker-stack/watchtower
docker compose up -d
Verifique se todos os containers estão rodando corretamente:
docker ps
Você deve ver o Traefik, sua aplicação e o Watchtower listados como Up. Acesse o painel de dashboard do Traefik em http://SEU_IP_DO_VPS:8080 para confirmar que as rotas estão sendo detectadas. Lembre-se de configurar o DNS apontando seu domínio para o IP da VPS antes de testar o acesso externo.
Troubleshooting Comum em Ambientes Docker
Apesar da robustez do ecossistema, problemas podem ocorrer. Abaixo estão os cenários mais frequentes e como resolvê-los rapidamente.
1. Problemas de Permissão (Permission Denied)
Se sua aplicação falha ao escrever arquivos no volume montado, verifique o UID/GID do usuário dentro do container e compare com o dono do arquivo no host. Containers geralmente rodam como root ou um usuário específico (como www-data). Use chown -R usuario:grupo /opt/docker-stack/app-data/myapp para corrigir.
2. Certificados SSL Não Gerados
Se o Traefik não gera os certificados, verifique se a porta 80 está acessível externamente (o Let's Encrypt precisa validar o domínio). Verifique os logs do Traefik:
docker logs traefik
Mensagens como "error: one of the entrypoints must be defined" indicam erros de configuração no arquivo YAML.
3. Watchtower Não Atualiza
Se o Watchtower não detecta atualizações, verifique se a imagem foi marcada corretamente ou se há problemas de rede entre o container do Watchtower e o Docker Hub. Use docker compose logs watchtower para inspecionar.
Conclusão e Boas Práticas
A migração de dados locais para uma estrutura de volumes docker organizada não é apenas uma mudança técnica, mas uma mudança de mentalidade operacional. Ao separar configurações, dados e lógica de aplicação, você ganha resiliência e escalabilidade.
Mantenha seus arquivos de configuração versionados no Git. Utilize segredos (secrets) do Docker para senhas sensíveis em vez de variáveis de ambiente expostas. E, acima de tudo, realize backups regulares dos diretórios /opt/docker-stack, especialmente da pasta de dados e do arquivo acme.json do Traefik.
Com essa base sólida, você está preparado para escalar sua infraestrutura, adicionar novos serviços como bancos de dados (PostgreSQL, MySQL) ou sistemas de cache (Redis), sempre mantendo o controle total sobre seus dados e a segurança da sua VPS.