Developer Tools

Docker para Desenvolvedores: Containers, Compose e Fluxos de Trabalho do Mundo Real

Um guia prático sobre conceitos do Docker, boas práticas de Dockerfile e uso do Docker Compose para executar ambientes de desenvolvimento com múltiplos serviços.

9 min de leitura

Sala de servidores com iluminação azul

"Funciona na minha máquina" é a frase responsável por milhares de falhas de deploy. O Docker resolve isso empacotando sua aplicação e todas as suas dependências em um container — um ambiente isolado e reproduzível que roda de forma idêntica em qualquer lugar, do seu laptop até a produção.

Containers vs. máquinas virtuais

Máquina Virtual Container
Isolamento SO completo Nível de processo
Tempo de inicialização Minutos Segundos
Tamanho GBs MBs
Overhead Alto (hypervisor) Quase zero
Caso de uso Isolamento de SO completo Empacotamento de apps

Os containers compartilham o kernel do host, mas isolam o sistema de arquivos, os processos e a rede. É por isso que são tão leves.

Conceitos fundamentais do Docker

Image — Um modelo somente leitura para um container. Pense nela como a definição de uma classe.

Container — Uma instância em execução de uma image. Pense nele como um objeto criado a partir da classe.

Registry — Um sistema de armazenamento e distribuição de images. O Docker Hub é o padrão público; GitHub Container Registry e AWS ECR são alternativas comuns.

Volume — Armazenamento persistente que sobrevive a reinicializações do container. Dados gravados dentro de um container sem um volume são perdidos quando o container para.

Escrevendo um bom Dockerfile

# Use uma versão específica — nunca "latest" em produção
FROM node:20-alpine

# Define o diretório de trabalho
WORKDIR /app

# Copia os arquivos de dependências primeiro (aproveita o cache de camadas)
COPY package.json package-lock.json ./
RUN npm ci --only=production

# Copia o código da aplicação
COPY . .

# Faz o build da aplicação
RUN npm run build

# Executa como usuário não-root por segurança
USER node

# Documenta a porta (não a publica)
EXPOSE 3000

# Usa a forma exec para tratamento correto de sinais
CMD ["node", "server.js"]

Cache de camadas: a chave para builds rápidos

Cada instrução em um Dockerfile cria uma camada. O Docker armazena camadas em cache — se nada mudou em uma camada ou em suas predecessoras, o Docker reutiliza o cache.

Copie os arquivos de dependências antes do código da aplicação. As dependências mudam muito menos do que o seu código. Se você copiar tudo de uma vez, uma alteração de uma linha no index.js invalida o cache de instalação das dependências e força um npm install completo.

Builds multi-estágio

Use builds multi-estágio para manter as images de produção enxutas:

# Estágio 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Estágio 2: Produção (sem dependências de dev, sem código-fonte)
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]

A image final contém apenas o output compilado — sem o seu código-fonte, arquivos de teste ou dependências de desenvolvimento.

Docker Compose para desenvolvimento local

O Docker Compose orquestra aplicações com múltiplos containers. Um único docker-compose.yml define toda a sua stack local:

version: "3.9"

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    depends_on:
      db:
        condition: service_healthy
    volumes:
      - .:/app
      - /app/node_modules

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

Gere o scaffolding da sua stack com o nosso Docker Compose Generator — escolha seus serviços e obtenha um docker-compose.yml pronto para produção em segundos.

Comandos essenciais do Compose

# Inicia todos os serviços em segundo plano
docker compose up -d

# Exibe logs (modo de acompanhamento)
docker compose logs -f app

# Executa um comando dentro de um container em execução
docker compose exec app sh

# Para e remove os containers (mantém os volumes)
docker compose down

# Para e remove containers E volumes
docker compose down -v

# Reconstrói as images após alterações no Dockerfile
docker compose up -d --build

Variáveis de ambiente e segredos

Nunca deixe credenciais hardcoded no seu Dockerfile ou no arquivo Compose. Use um arquivo .env:

# .env (adicione ao .gitignore!)
DATABASE_URL=postgresql://user:pass@db:5432/myapp
REDIS_URL=redis://redis:6379
JWT_SECRET=your-super-secret-key

O Compose carrega automaticamente o .env do diretório do projeto. Referencie variáveis com ${VARIABLE_NAME} no seu YAML.

Use nosso Env Generator para criar arquivos .env com valores padrão adequados e templates .env.example para o seu time.

Redes no Docker

O Compose cria uma rede padrão para a sua stack. Os serviços podem se comunicar entre si usando o nome do serviço como hostname — é por isso que a aplicação usa db como host do banco de dados, e não localhost.

app → conecta em "db:5432"
app → conecta em "redis:6379"

Apenas as portas explicitamente mapeadas com ports: ficam acessíveis a partir da máquina host.

Health checks e ordem de inicialização

O depends_on apenas aguarda um container iniciar, não estar pronto. Um container de banco de dados inicia em segundos, mas o Postgres pode levar mais alguns segundos para aceitar conexões. Use healthcheck + condition: service_healthy para aguardar até que o serviço esteja de fato pronto.

Considerações para produção

  1. Use tags de image específicasnode:20.11.1-alpine, não node:latest.
  2. Faça scan das images em busca de vulnerabilidadesdocker scout cves myapp:latest.
  3. Defina limites de recursos — evite que um único container consuma os recursos dos demais.
  4. Use sistemas de arquivos somente leitura sempre que possível — read_only: true no Compose.
  5. Nunca execute como root — adicione USER node ou equivalente.
  6. Configure o Nginx como proxy reverso na frente da sua aplicação — use nosso Nginx Config Generator para obter uma configuração testada e aprovada.

Referência rápida

# Images
docker images                    # lista images locais
docker pull nginx:alpine         # baixa uma image
docker rmi myimage               # remove uma image

# Containers
docker ps                        # containers em execução
docker ps -a                     # todos os containers
docker stop <id>                 # para graciosamente
docker rm <id>                   # remove container parado
docker logs <id> -f              # acompanha os logs

# Limpeza
docker system prune              # remove tudo que não está em uso
docker volume prune              # remove volumes não utilizados

O Docker elimina toda uma classe de bugs relacionados ao ambiente e torna o onboarding de novos desenvolvedores incrivelmente simples. Domine os fundamentos aqui e você terá uma base sólida para Kubernetes, pipelines de CI/CD e deploys em nuvem.