Em ambientes de containers, a persistência de dados é um dos desafios mais críticos. Por padrão, todo dado escrito dentro de um container é efêmero: ele desaparece quando o container é removido. Isso é ótimo para manter a imutabilidade e a reprodutibilidade, mas inaceitável para bancos de dados, arquivos de upload ou qualquer estado que precise sobreviver ao ciclo de vida do container. Nesta aula, vamos explorar as duas principais estratégias do Docker para persistência: volumes e bind mounts, além de discutir quando usar cada uma e como realizar backups de forma segura.

Dominar esses conceitos é essencial para qualquer profissional de DevOps, pois garante que suas aplicações sejam resilientes, portáveis e capazes de manter dados importantes mesmo durante atualizações, migrações ou falhas. Vamos começar entendendo a natureza dos dados em containers.

Dados efêmeros vs persistentes

Todo container Docker é criado a partir de uma imagem imutável, mas durante a execução ele pode gerar e modificar dados na sua camada de escrita. Essa camada é temporária e está diretamente ligada ao ciclo de vida do container. Quando o container é removido, todos os dados escritos nessa camada são perdidos. Chamamos isso de dados efêmeros.

Para ilustrar, imagine que você execute um container com um banco de dados SQLite e insira registros. Se você parar e remover o container, o arquivo do banco será destruído. Isso é inaceitável na prática. A solução é usar mecanismos que armazenem dados fora do sistema de arquivos do container, em um local persistente no host ou em uma unidade de rede. Esses são os dados persistentes.

O Docker oferece duas formas principais de persistência: volumes e bind mounts. Ambos permitem que dados sobrevivam ao ciclo de vida do container, mas possuem características e usos distintos. Vamos analisá-los em detalhe.

Volumes e bind mounts

Volumes são a forma recomendada pelo Docker para persistir dados. Eles são gerenciados pelo próprio Docker e armazenados em uma área específica do host (geralmente /var/lib/docker/volumes/). Você pode criar um volume explicitamente com docker volume create ou deixar que o Docker crie automaticamente ao montar um volume em um container. Volumes são independentes do ciclo de vida do container e podem ser compartilhados entre múltiplos containers.

Para usar um volume, você o monta em um diretório do container usando a flag -v ou --mount. Exemplo:

docker volume create meu-volume
docker run -d --name meu-container -v meu-volume:/dados nginx

Nesse exemplo, o diretório /dados dentro do container é persistido no volume meu-volume. Qualquer dado escrito nesse diretório sobreviverá à remoção do container.

Bind mounts são uma forma mais antiga e flexível de persistência. Eles mapeiam um diretório ou arquivo do host diretamente para um caminho no container. Isso significa que você pode acessar os dados diretamente no sistema de arquivos do host, o que é útil para desenvolvimento, pois permite editar arquivos com suas ferramentas locais. No entanto, bind mounts dependem da estrutura de diretórios do host, o que pode reduzir a portabilidade.

Exemplo de bind mount:

docker run -d --name meu-container -v /home/usuario/dados:/dados nginx

Aqui, o diretório /home/usuario/dados do host é montado em /dados no container. Alterações feitas de um lado aparecem no outro.

Uma diferença importante é que volumes são totalmente gerenciados pelo Docker, enquanto bind mounts são apenas referências a caminhos do host. Isso torna os volumes mais seguros e portáveis, pois não dependem da estrutura de diretórios de uma máquina específica. Além disso, o Docker oferece comandos específicos para gerenciar volumes, como docker volume ls, docker volume inspect e docker volume rm.

Quando usar

A escolha entre volumes e bind mounts depende do contexto. Como regra geral, use volumes em produção e para dados que precisam ser persistentes e gerenciados pelo Docker. Eles são ideais para bancos de dados, arquivos de aplicação, uploads de usuários e qualquer dado que não deva ser acessado diretamente pelo host.

Use bind mounts principalmente em desenvolvimento, quando você precisa editar código ou configurações diretamente no host e vê-las refletidas no container em tempo real. Por exemplo, ao desenvolver uma aplicação Node.js, você pode montar o diretório do código-fonte no container para que as alterações sejam aplicadas sem reconstruir a imagem.

Evite usar bind mounts em produção, pois eles criam uma dependência do sistema de arquivos do host e podem causar problemas de segurança e portabilidade. Se você precisar compartilhar dados entre containers, volumes são a opção mais adequada, pois podem ser montados em múltiplos containers simultaneamente.

Outra consideração é o desempenho: volumes são geralmente mais rápidos que bind mounts, especialmente em sistemas macOS e Windows, pois o Docker pode otimizar o acesso aos dados.

Backup

Persistir dados é essencial, mas também é fundamental garantir que você possa recuperá-los em caso de falha ou perda acidental. O backup de volumes Docker é uma prática recomendada em qualquer ambiente de produção.

A estratégia mais simples é copiar o conteúdo do volume para um arquivo tar no host. Para isso, você pode usar um container temporário que monta o volume e executa um comando de compactação. Exemplo:

docker run --rm -v meu-volume:/dados -v $(pwd):/backup ubuntu tar czf /backup/backup.tar.gz -C /dados .

Esse comando monta o volume meu-volume em /dados e o diretório atual do host em /backup. Dentro do container, o comando tar compacta todo o conteúdo de /dados em /backup/backup.tar.gz. O container é removido automaticamente após a execução (--rm).

Para restaurar, basta extrair o conteúdo do backup para o volume:

docker run --rm -v meu-volume:/dados -v $(pwd):/backup ubuntu tar xzf /backup/backup.tar.gz -C /dados

Essa abordagem é simples e funciona bem para volumes pequenos. Para volumes maiores ou em ambientes com muitos dados, considere usar soluções mais robustas, como ferramentas de backup específicas ou replicação em nuvem.

Além disso, ao usar bind mounts, o backup é trivial, pois os dados já estão no host. Basta copiar o diretório com suas ferramentas habituais. No entanto, lembre-se de que bind mounts não são recomendados para produção, então o backup de volumes é mais relevante.

Outra boa prática é automatizar backups com cron jobs ou orquestradores como Kubernetes, que possuem mecanismos próprios de backup e restauração.

Referências

Exercícios

  1. Crie um volume chamado dados-app e monte-o em um container nginx no diretório /usr/share/nginx/html. Em seguida, crie um arquivo index.html dentro do volume e verifique se ele é acessível pelo container.

    ✓ Resposta:
    docker volume create dados-app
    docker run -d --name nginx-volume -v dados-app:/usr/share/nginx/html nginx
    # Para criar o arquivo, use um container temporário:
    docker run --rm -v dados-app:/dados ubuntu bash -c "echo 'Olá, persistência!' > /dados/index.html"
    # Teste com curl ou acesse o IP do container
  2. Explique a diferença entre volumes e bind mounts, citando pelo menos duas situações em que cada um é mais adequado.

    ✓ Resposta: Volumes são gerenciados pelo Docker, armazenados em área específica do host, portáveis e recomendados para produção. Bind mounts mapeiam diretórios do host, são úteis em desenvolvimento para edição em tempo real, mas dependem da estrutura do host. Use volumes para bancos de dados e dados de aplicação; use bind mounts para código-fonte em desenvolvimento.
  3. Faça backup do volume dados-app para um arquivo backup.tar.gz no diretório atual e depois restaure-o em um novo volume chamado dados-restaurado.

    ✓ Resposta:
    # Backup
    docker run --rm -v dados-app:/dados -v $(pwd):/backup ubuntu tar czf /backup/backup.tar.gz -C /dados .
    # Restauração
    docker volume create dados-restaurado
    docker run --rm -v dados-restaurado:/dados -v $(pwd):/backup ubuntu tar xzf /backup/backup.tar.gz -C /dados
  4. Monte um bind mount do diretório /tmp/meu-codigo do host no container nginx no caminho /usr/share/nginx/html. Crie um arquivo index.html no host e verifique se ele é servido pelo container.

    ✓ Resposta:
    mkdir -p /tmp/meu-codigo
    echo "<h1>Bind mount</h1>" > /tmp/meu-codigo/index.html
    docker run -d --name nginx-bind -v /tmp/meu-codigo:/usr/share/nginx/html nginx
    # Acesse http://localhost (ou IP do container) para ver a página
  5. Discuta as boas práticas para backup de volumes em produção, incluindo automação e segurança.

    ✓ Resposta: Boas práticas: automatizar backups com cron ou ferramentas de orquestração, usar criptografia para dados sensíveis, testar restaurações regularmente, armazenar backups em locais separados (ex.: nuvem), e considerar soluções como Velero para Kubernetes. Sempre verificar a integridade dos backups após a criação.

Observações finais

A persistência de dados é um pilar fundamental no mundo de containers. Dominar volumes e bind mounts permite que você construa aplicações robustas e resilientes. Lembre-se de sempre preferir volumes em produção, mas não subestime a praticidade dos bind mounts durante o desenvolvimento.

Além disso, nunca negligencie os backups. Uma estratégia de backup bem planejada pode salvar sua operação em caso de desastres. Pratique os exercícios e explore a documentação oficial para aprofundar seu conhecimento.