Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 22

Managing Docker Volumes for Persistent Data

Recommended resources

Olá! Bem-vindo de volta à nossa jornada pelo Docker.

Na lição anterior, estabelecemos a base teórica para o armazenamento de dados, distinguindo entre volumes, bind mounts e tmpfs mounts. A principal conclusão foi que, para dados de produção que precisam sobreviver, os volumes são a escolha certa.

Hoje, vamos colocar essa teoria em prática. O nosso objetivo é criar e gerenciar volumes Docker para persistir dados independentemente do ciclo de vida do contêiner. Vamos focar nas operações práticas que você usará no dia a dia para garantir que os dados da sua aplicação, como os de um banco de dados, estejam sempre seguros. Ao final desta lição, você saberá como usar os comandos do Docker para dar um "estado" permanente às suas aplicações "sem estado" (stateless).

O Problema Resolvido: Persistência com Volumes

Lembre-se da camada de escrita efêmera do contêiner: quando um contêiner é removido, os dados dentro dele desaparecem. Os volumes resolvem isso criando um armazenamento persistente no sistema de arquivos do host, mas de uma forma que é gerenciada inteiramente pelo Docker. Isso os torna portáteis, seguros e a melhor prática para produção.

Docker Storage Explained: Volumes vs. Bind Mounts
Esta imagem recapitula a diferença fundamental: os volumes são áreas de armazenamento gerenciadas pelo Docker, enquanto os bind mounts são um mapeamento direto para um diretório no host. Hoje, nosso foco está no lado esquerdo: os volumes.

Gerenciando o Ciclo de Vida de um Volume

Volumes são cidadãos de primeira classe no ecossistema Docker. Isso significa que eles têm seu próprio ciclo de vida e podem ser gerenciados com um conjunto dedicado de comandos, independentemente de qualquer contêiner.

Vamos explorar os comandos essenciais para criar, listar, inspecionar e remover volumes.

Day-27 | Docker Volumes and Bind Mounts|Persistent Storage for Docker| #devopstutorialsforbeginners

O vídeo a seguir, do canal Abhishek.Veeramalla, oferece uma demonstração prática e clara dos comandos de gerenciamento de volumes. Ele mostra como interagir com os volumes como recursos independentes.

Assista aos trechos de 19:56 a 26:22. Observe os seguintes comandos em ação: docker volume create para criar um novo volume nomeado. docker volume ls para listar os volumes existentes. docker volume inspect para obter detalhes sobre um volume. docker volume rm para remover um volume (e a condição para que isso funcione).

Para resumir e ter como referência, aqui estão os comandos que você acabou de ver:

  • Criar um volume:

    docker volume create meu-primeiro-volume
    

    Isso cria um novo volume nomeado. Usar nomes explícitos é uma prática recomendada, pois facilita muito o gerenciamento.

  • Listar volumes:

    docker volume ls
    

    Isso mostra todos os volumes que o Docker está gerenciando no seu host.

  • Inspecionar um volume:

    docker volume inspect meu-primeiro-volume
    

    Este comando é especialmente útil para entender "como funciona". A saída JSON mostra metadados importantes, incluindo o "Mountpoint". Este é o caminho real no sistema de arquivos do seu host onde os dados do volume são armazenados (no Linux, geralmente em /var/lib/docker/volumes/...). O Docker gerencia esse diretório para você, o que abstrai as diferenças entre sistemas operacionais e torna seus contêineres mais portáteis.

  • Remover um volume:

    docker volume rm meu-primeiro-volume
    

    Como o vídeo destaca, você só pode remover um volume se ele não estiver sendo usado por nenhum contêiner (parado ou em execução). Isso é um mecanismo de segurança para evitar a perda acidental de dados.


Mão na Massa: Persistindo Dados de um Banco de Dados PostgreSQL

Agora vem a parte mais importante: usar um volume com um contêiner. Para um desenvolvedor backend, o caso de uso mais comum e crítico é persistir os dados de um banco de dados. Vamos configurar um contêiner PostgreSQL e garantir que seus dados sobrevivam mesmo que o contêiner seja destruído.

Passo 1: Criar o Volume

Primeiro, vamos criar um volume nomeado especificamente para os dados do nosso banco de dados.

docker volume create postgres-data

Passo 2: Rodar o Contêiner PostgreSQL com o Volume

Agora, vamos iniciar um contêiner PostgreSQL. Usaremos a flag --mount para anexar nosso volume postgres-data ao diretório interno do contêiner onde o PostgreSQL armazena seus dados, que é /var/lib/postgresql/data.

A flag --mount é preferível à antiga flag -v porque sua sintaxe é mais explícita e fácil de ler, usando pares chave-valor.

docker run --name my-postgres-db -d \
  -e POSTGRES_PASSWORD=mysecretpassword \
  --mount type=volume,source=postgres-data,target=/var/lib/postgresql/data \
  postgres:15

Vamos analisar o comando:

  • --name my-postgres-db: Nomeamos nosso contêiner.
  • -d: Executamos em modo detached.
  • -e POSTGRES_PASSWORD=...: Definimos a senha do superusuário do banco, exigida pela imagem postgres.
  • --mount type=volume,source=postgres-data,target=/var/lib/postgresql/data: Esta é a parte crucial.
    • type=volume: Especificamos que estamos montando um volume.
    • source=postgres-data: O nome do nosso volume no host (o que criamos no Passo 1).
    • target=/var/lib/postgresql/data: O caminho absoluto dentro do contêiner onde o volume será montado.

Passo 3: Verificar a Persistência

Agora, vamos provar que funciona.

  1. Adicionar dados ao banco:
    Primeiro, vamos entrar no contêiner e usar o cliente psql para criar uma tabela e inserir um registro.

    # Acessa o shell interativo do psql dentro do contêiner
    docker exec -it my-postgres-db psql -U postgres
    
    # Dentro do psql, execute os seguintes comandos SQL:
    CREATE TABLE products (id serial PRIMARY KEY, name VARCHAR(50));
    INSERT INTO products (name) VALUES ('Notebook');
    SELECT * FROM products;
    \q 
    

    Você deve ver o produto "Notebook" listado. Agora saia do psql com \q.

  2. Destruir o contêiner:
    Agora, vamos simular uma falha ou uma atualização, parando e removendo completamente o contêiner.

    docker stop my-postgres-db
    docker rm my-postgres-db
    

    Neste ponto, o contêiner my-postgres-db não existe mais. Se não tivéssemos usado um volume, nossos dados teriam sido perdidos.

  3. Recriar o contêiner e verificar os dados:
    Vamos criar um novo contêiner, mas montando o mesmo volume postgres-data.

    docker run --name my-new-postgres-db -d \
      -e POSTGRES_PASSWORD=mysecretpassword \
      --mount type=volume,source=postgres-data,target=/var/lib/postgresql/data \
      postgres:15
    

    Agora, vamos nos conectar a este novo contêiner e verificar nossa tabela products.

    # Acessa o psql no novo contêiner
    docker exec -it my-new-postgres-db psql -U postgres
    
    # Dentro do psql, verifique os dados
    SELECT * FROM products;
    

    O resultado deve mostrar o produto 'Notebook' que inserimos anteriormente! Isso prova que os dados persistiram no volume postgres-data, completamente desacoplados do ciclo de vida do contêiner original.

Test your understanding!

Você executou o comando docker inspect my-new-postgres-db e viu a seguinte seção na saída JSON:

"Mounts": [
    {
        "Type": "volume",
        "Name": "postgres-data",
        "Source": "/var/lib/docker/volumes/postgres-data/_data",
        "Destination": "/var/lib/postgresql/data",
        "Driver": "local",
        "Mode": "z",
        "RW": true,
        "Propagation": ""
    }
]

Com base no que aprendeu, o que o campo "Source" representa? E por que o gerenciamento desse caminho pelo Docker é uma vantagem em relação a um bind mount?

Show answer

O campo "Source" representa o caminho absoluto no sistema de arquivos do host onde o Docker armazena fisicamente os dados do volume postgres-data.

A vantagem de o Docker gerenciar esse caminho é a portabilidade. Você não precisa se preocupar com a estrutura de diretórios específica do host. Você pode pegar seu comando docker run ou, futuramente, seu arquivo docker-compose.yml, executá-lo em qualquer outra máquina com Docker (Linux, Windows, macOS) e ele simplesmente funcionará, pois o Docker se encarregará de criar e gerenciar o caminho de origem (Source) apropriado para aquele sistema operacional. Com um bind mount, você estaria "amarrado" a um caminho específico do host, como C:\Users\MyUser\data, que provavelmente não existiria em outra máquina.


Limpeza e Próximas Etapas

Após terminar seus testes, você pode limpar seu ambiente:

# Pare e remova o contêiner
docker stop my-new-postgres-db
docker rm my-new-postgres-db

# Remova o volume (agora que não está mais em uso)
docker volume rm postgres-data

Se você tiver muitos volumes não utilizados (especialmente os "anônimos", que são criados quando você não especifica um nome), pode usar um comando de limpeza:

docker volume prune

Este comando remove todos os volumes que não estão atualmente anexados a pelo menos um contêiner.


Conclusão

Hoje você deu um passo fundamental para construir aplicações Docker robustas. Você aprendeu a gerenciar o ciclo de vida dos volumes e, o mais importante, a usá-los para garantir a persistência dos dados de um banco de dados, tornando sua aplicação com estado (stateful).

Principais Pontos:

  • Volumes são gerenciados pelo Docker: Use docker volume create, ls, inspect e rm para gerenciá-los.
  • A flag --mount é sua amiga: Use-a para anexar volumes a contêineres de forma clara e explícita (--mount type=volume,source=my-volume,target=/path/in/container).
  • Dados sobrevivem ao contêiner: Ao montar um volume, os dados persistem mesmo que o contêiner seja removido e recriado.
  • Volumes são a escolha para produção: Para qualquer dado que precise persistir (bancos de dados, uploads, logs importantes), volumes são a solução padrão.

Na próxima lição, vamos explorar o outro lado da moeda do armazenamento: os bind mounts. Veremos como montar um diretório do host diretamente em um contêiner, uma técnica extremamente poderosa e comum para ambientes de desenvolvimento, especialmente para você que trabalha com .NET e deseja um fluxo de trabalho com hot-reloading.