Docker Volumes

Quando falamos em aplicações containerizadas, um ponto essencial é a persistência de dados. Por padrão, quando um container Docker é removido, todos os dados que estavam “dentro” dele se perdem. A solução? Volumes Docker. Eles permitem que os dados sobrevivam aos ciclos de “sobes e desces” dos containers, fornecendo uma camada de isolamento e escalabilidade para qualquer aplicação. Por que usar Volumes Docker? Persistência: Ao criar ou vincular volumes a containers, você evita a perda de dados quando o container é recriado ou destruído. Isolamento: Separar o armazenamento de dados da lógica do container ajuda a manter tudo mais organizado e facilita substituições ou atualizações do aplicativo. Escalabilidade: Em um ambiente onde múltiplos containers são executados, volumes fornecem meios de compartilhamento de dados de forma simples. Desenvolvimento Simplificado: Especialmente em bind mounts, você pode editar arquivos localmente e ver as mudanças refletidas no container em tempo real. Pense no container como um carro alugado — sempre que você troca de carro, perde todos os itens que estavam nele. Um volume seria a mala pessoal que você leva para qualquer lugar, independentemente de qual carro (container) esteja dirigindo. Caso de uso 1: Ligando uma pasta Local para subir arquivos no Container Imagine que você tem uma aplicação Go que recebe uploads de arquivos dos usuários. Vamos criar um mini-exemplo para demonstrar como manter esses uploads persistentes no seu computador local, em vez de perdê-los ao remover o container. Image Uploader Este exemplo simples cria um servidor HTTP que permite envio de arquivos e os salva em uma pasta uploads/. Aqui está o exemplo do nosso handler, caso você queria ver o projeto completo, ele esta disponível no meu github: func UploadHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeJSONError(w, http.StatusMethodNotAllowed, "Método não permitido") return } file, header, err := r.FormFile("file") if err != nil { writeJSONError(w, http.StatusBadRequest, "Erro ao ler arquivo do formulário") return } defer file.Close() // Salva o arquivo chamando o serviço interno err = services.SaveUploadedFile(file, header.Filename) if err != nil { writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Erro ao gravar arquivo: %v", err)) return } writeJSONSuccess(w, http.StatusOK, "Upload realizado com sucesso!", header.Filename) } Dockerfile Nosso Dockerfile compila o binário e configura o ambiente para executar a aplicação: # syntax=docker/dockerfile:1 FROM golang:1.23-alpine AS builder WORKDIR /app COPY go.mod ./ RUN go mod download COPY . . RUN go build -o server ./cmd/image-uploader FROM alpine:3.21 WORKDIR /app COPY --from=builder /app/server /app/server RUN mkdir -p /app/uploads EXPOSE 8080 CMD ["/app/server"] Criando e Executando o Container com Bind Mount 1.Construa a imagem: docker build -t go-upload-app:latest . 2.Execute o container, mapeando a pasta uploads/ do host para dentro do container: docker run -d \ --name meu_container_go \ -p 8080:8080 \ -v /caminho/no/host/uploads:/app/uploads \ go-upload-app:latest Observe o -v /caminho/no/host/uploads:/app/uploads. Esquerda: caminho no seu computador (host). Direita: caminho no container, conforme definido no Dockerfile (/app/uploads). Agora, qualquer arquivo enviado via /upload será armazenado em ambos os locais: no container e na sua pasta local (uploads/). Mesmo que você remova o container, os arquivos continuarão no seu host. Usando Volumes Nomeados Se você não precisa necessariamente da pasta local e prefere que o Docker gerencie os dados em um “volume nomeado”, eis um exemplo simples com PostgreSQL (apenas como ilustração de outro tipo de volume): docker volume create pg_dados docker run -d \ --name meu_postgres \ -e POSTGRES_PASSWORD=123456 \ -v pg_dados:/var/lib/postgresql/data \ postgres:latest Assim, o volume pg_dados sobrevive aos containers que o utilizam, preservando as informações do banco de dados. Segurança: criptografando volumes Caso você trabalhe com dados sensíveis, considere criptografar o sistema de arquivos subjacente ou usar drivers de volume com suporte a criptografia. Você pode: Armazenar volumes em partições criptografadas. Utilizar soluções de armazenamento em nuvem que suportem criptografia em repouso. Configurar drivers especializados (por exemplo, rexray, portworx etc.) que oferecem criptografia integrada. Pense nos arquivos do seu volume como documentos confidenciais; mantê-los “a céu aberto” não é seguro. Use um “cofre” (criptografia) e proteja-o com senhas (chaves de criptografia). Exemplo com Docker Compose Agora que já entendemos como os volumes podem ajudar a mante

Jan 21, 2025 - 13:00
 0
Docker Volumes

Quando falamos em aplicações containerizadas, um ponto essencial é a persistência de dados. Por padrão, quando um container Docker é removido, todos os dados que estavam “dentro” dele se perdem. A solução? Volumes Docker. Eles permitem que os dados sobrevivam aos ciclos de “sobes e desces” dos containers, fornecendo uma camada de isolamento e escalabilidade para qualquer aplicação.

Por que usar Volumes Docker?

  1. Persistência: Ao criar ou vincular volumes a containers, você evita a perda de dados quando o container é recriado ou destruído.
  2. Isolamento: Separar o armazenamento de dados da lógica do container ajuda a manter tudo mais organizado e facilita substituições ou atualizações do aplicativo.
  3. Escalabilidade: Em um ambiente onde múltiplos containers são executados, volumes fornecem meios de compartilhamento de dados de forma simples.
  4. Desenvolvimento Simplificado: Especialmente em bind mounts, você pode editar arquivos localmente e ver as mudanças refletidas no container em tempo real.

Pense no container como um carro alugado — sempre que você troca de carro, perde todos os itens que estavam nele. Um volume seria a mala pessoal que você leva para qualquer lugar, independentemente de qual carro (container) esteja dirigindo.

Docker Volumes Archtecture

Caso de uso 1: Ligando uma pasta Local para subir arquivos no Container

Imagine que você tem uma aplicação Go que recebe uploads de arquivos dos usuários. Vamos criar um mini-exemplo para demonstrar como manter esses uploads persistentes no seu computador local, em vez de perdê-los ao remover o container.

Image Uploader

Este exemplo simples cria um servidor HTTP que permite envio de arquivos e os salva em uma pasta uploads/.

Aqui está o exemplo do nosso handler, caso você queria ver o projeto completo, ele esta disponível no meu github:

func UploadHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        writeJSONError(w, http.StatusMethodNotAllowed, "Método não permitido")
        return
    }

    file, header, err := r.FormFile("file")
    if err != nil {
        writeJSONError(w, http.StatusBadRequest, "Erro ao ler arquivo do formulário")
        return
    }
    defer file.Close()

    // Salva o arquivo chamando o serviço interno
    err = services.SaveUploadedFile(file, header.Filename)
    if err != nil {
        writeJSONError(w, http.StatusInternalServerError, fmt.Sprintf("Erro ao gravar arquivo: %v", err))
        return
    }

    writeJSONSuccess(w, http.StatusOK, "Upload realizado com sucesso!", header.Filename)
}

Dockerfile

Nosso Dockerfile compila o binário e configura o ambiente para executar a aplicação:

# syntax=docker/dockerfile:1
FROM golang:1.23-alpine AS builder
WORKDIR /app

COPY go.mod ./
RUN go mod download

COPY . .
RUN go build -o server ./cmd/image-uploader

FROM alpine:3.21
WORKDIR /app

COPY --from=builder /app/server /app/server
RUN mkdir -p /app/uploads

EXPOSE 8080
CMD ["/app/server"]

Criando e Executando o Container com Bind Mount

1.Construa a imagem:

docker build -t go-upload-app:latest .

2.Execute o container, mapeando a pasta uploads/ do host para dentro do container:

docker run -d \
  --name meu_container_go \
  -p 8080:8080 \
  -v /caminho/no/host/uploads:/app/uploads \
  go-upload-app:latest

Observe o -v /caminho/no/host/uploads:/app/uploads.

  • Esquerda: caminho no seu computador (host).
  • Direita: caminho no container, conforme definido no Dockerfile (/app/uploads).

Agora, qualquer arquivo enviado via /upload será armazenado em ambos os locais: no container e na sua pasta local (uploads/). Mesmo que você remova o container, os arquivos continuarão no seu host.

Usando Volumes Nomeados

Se você não precisa necessariamente da pasta local e prefere que o Docker gerencie os dados em um “volume nomeado”, eis um exemplo simples com PostgreSQL (apenas como ilustração de outro tipo de volume):

docker volume create pg_dados

docker run -d \
  --name meu_postgres \
  -e POSTGRES_PASSWORD=123456 \
  -v pg_dados:/var/lib/postgresql/data \
  postgres:latest

Assim, o volume pg_dados sobrevive aos containers que o utilizam, preservando as informações do banco de dados.

Segurança: criptografando volumes

Caso você trabalhe com dados sensíveis, considere criptografar o sistema de arquivos subjacente ou usar drivers de volume com suporte a criptografia. Você pode:

  • Armazenar volumes em partições criptografadas.
  • Utilizar soluções de armazenamento em nuvem que suportem criptografia em repouso.
  • Configurar drivers especializados (por exemplo, rexray, portworx etc.) que oferecem criptografia integrada.

Pense nos arquivos do seu volume como documentos confidenciais; mantê-los “a céu aberto” não é seguro. Use um “cofre” (criptografia) e proteja-o com senhas (chaves de criptografia).

Exemplo com Docker Compose

Agora que já entendemos como os volumes podem ajudar a manter dados, vamos dar um passo além e orquestrar vários serviços com o Docker Compose. Essa ferramenta facilita muito as coisas ao permitir que a configuração de todos os containers seja feita em um único arquivo.

Imagine que você possui um banco de dados e não quer perder tudo o que foi salvo sempre que o container for desligado. É aqui que os volumes entram: você pode armazenar os dados no seu computador (host), garantindo que eles permaneçam intactos mesmo se o container for removido.

services:
  app:
    build: .
    container_name: go_app_container
    ports:
      - "8080:8080"
    volumes:
      # Faz bind mount: a pasta 'uploads' do host é mapeada para '/app/uploads' dentro do container
      - ./uploads:/app/uploads
    depends_on:
      - db

  db:
    image: postgres:17-alpine
    container_name: postgres_container
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb
    volumes:
      # Volume nomeado para persistir dados do PostgreSQL
      - db_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  db_data:

Executando com Docker Compose

Inicie os serviços:

docker compose up -d

O Compose vai construir a imagem da aplicação Go, baixar a imagem postgres:17-alpine, criar o volume db_data e iniciar os containers.

Verifique se tudo está rodando:

docker compose ps

Você deve ver algo como:

Name                Command             State         Ports
------------------------------------------------------------------
go_app_container    "/app/server"       Up            0.0.0.0:8080->8080/tcp
postgres_container  "docker-entrypoint…"Up            0.0.0.0:5432->5432/tcp

Teste o upload:

Você pode enviar um arquivo usando curl ou outro cliente HTTP:

curl -F "file=@/caminho/do/seu/arquivo.jpg" http://localhost:8080/upload

Verifique se o arquivo aparece na pasta local uploads/.
Parar e remover:

docker compose down

Isso remove os containers, mas o volume db_data permanece. Se você subir novamente, o PostgreSQL retoma de onde parou, pois os dados estão persistidos.

Conclusão

Volumes Docker são a chave para garantir que dados importantes não sejam descartados ao recriar containers. Quer você prefira bind mounts para desenvolvimento local (permitindo rápida iteração) ou volumes nomeados para produção, o uso correto deles traz resiliência e organização à sua aplicação em contêineres.

Ao criar um ambiente com Go e Postgres usando Docker Compose, percebe-se como é simples gerenciar múltiplos serviços e seus respectivos volumes. Isso garante não apenas persistência de dados, mas também escalabilidade e organização arquitetural.

Se você ainda não testou a persistência de dados com volumes em seus projetos, este é um excelente ponto de partida. Adapte o exemplo fornecido, faça seus próprios testes e compartilhe suas experiências — a comunidade de desenvolvedores está sempre pronta para discutir melhorias e resolver eventuais desafios.

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow