JB
_
·4 min de leitura

Docker para Desenvolvedores: Do Zero ao Deploy em Produção

Guia prático de Docker cobrindo conceitos essenciais, Dockerfile otimizado, Docker Compose para desenvolvimento local e estratégias de deploy em produção.

DockerDevOpsNode.jsInfraestrutura

Por que Docker mudou tudo?

"Na minha máquina funciona" — essa frase acabou com Docker. Containers garantem que o ambiente de desenvolvimento, staging e produção sejam idênticos. Sem surpresas no deploy.

Conceitos Fundamentais

| Conceito | Analogia | O que é | |----------|----------|---------| | Image | Receita de bolo | Template imutável com o app e suas dependências | | Container | O bolo assado | Instância rodando de uma image | | Dockerfile | Instruções da receita | Script que define como buildar a image | | Registry | Repositório de receitas | Onde images ficam armazenadas (Docker Hub, ECR) | | Compose | Kit de cozinha | Orquestra múltiplos containers juntos |

Seu Primeiro Dockerfile

Exemplo com uma API Node.js/Next.js:

# Dockerfile
 
# ── Estágio 1: dependências ──────────────────────────
FROM node:22-alpine AS deps
WORKDIR /app
 
COPY package.json package-lock.json ./
RUN npm ci --only=production
 
# ── Estágio 2: build ─────────────────────────────────
FROM node:22-alpine AS builder
WORKDIR /app
 
COPY --from=deps /app/node_modules ./node_modules
COPY . .
 
RUN npm run build
 
# ── Estágio 3: imagem final (menor possível) ─────────
FROM node:22-alpine AS runner
WORKDIR /app
 
ENV NODE_ENV=production
 
# Usuário não-root por segurança
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
 
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
 
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
 
CMD ["node", "server.js"]

O multi-stage build é crucial: a imagem final não carrega compiladores, node_modules de dev ou código-fonte — só o que é necessário para rodar.

.dockerignore — Não Esqueça

# .dockerignore
node_modules
.next
.git
.env
.env.local
*.log
README.md
Dockerfile*
docker-compose*
coverage
.nyc_output

Sem isso, o COPY . . manda 500MB de node_modules para o contexto de build.

Docker Compose para Desenvolvimento

# docker-compose.yml
version: '3.9'
 
services:
  app:
    build:
      context: .
      target: builder      # usa o estágio de builder, com devDeps
    ports:
      - "3000:3000"
    volumes:
      - .:/app             # hot reload: código local mapeado
      - /app/node_modules  # evita sobrescrever node_modules do container
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:senha@db:5432/mydb
    depends_on:
      db:
        condition: service_healthy
    command: npm run dev
 
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: senha
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
 
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
 
volumes:
  postgres_data:

Subir tudo:

docker compose up -d          # inicia em background
docker compose logs -f app    # acompanha logs
docker compose down           # para tudo
docker compose down -v        # para tudo E remove volumes

Comandos Essenciais do Dia a Dia

# Imagens
docker images                          # listar images locais
docker pull postgres:16-alpine         # baixar image
docker rmi minha-app:latest            # remover image
 
# Containers
docker ps                              # containers rodando
docker ps -a                           # todos (incluindo parados)
docker stop <id>                       # para graciosamente
docker rm <id>                         # remove container parado
docker logs -f <id>                    # acompanhar logs
 
# Executar comandos dentro do container
docker exec -it <id> sh                # abrir shell
docker exec -it <id> npm run migrate   # rodar migration
 
# Build
docker build -t minha-app:1.0.0 .
docker build --no-cache -t minha-app:latest .
 
# Limpeza
docker system prune -af               # remove tudo não usado
docker volume prune                    # remove volumes órfãos

Otimizando o Tamanho da Imagem

# ❌ Ruim: instala coisas e não limpa
RUN apt-get update
RUN apt-get install -y curl git
RUN apt-get install -y build-essential
 
# ✅ Bom: uma layer, limpa o cache
RUN apt-get update && apt-get install -y \
    curl \
    git \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

Dica: cada RUN, COPY e ADD cria uma layer. Docker cacheia layers — se uma layer muda, todas abaixo são refeitas. Copie package.json antes do código para aproveitar o cache:

# ✅ Cache inteligente
COPY package.json package-lock.json ./
RUN npm ci                             # só reexecuta se package.json mudou
 
COPY . .                               # código muda a toda hora — layer separada
RUN npm run build

Health Checks

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD curl -f http://localhost:3000/api/health || exit 1
// app/api/health/route.ts
export async function GET() {
  return Response.json({ status: 'ok', timestamp: new Date().toISOString() });
}

Variáveis de Ambiente em Produção

Nunca coloque segredos no Dockerfile ou na image. Use:

# Docker run com variáveis
docker run -e DATABASE_URL="..." -e API_KEY="..." minha-app
 
# Docker Compose com arquivo .env
# docker-compose.yml
services:
  app:
    env_file:
      - .env.production   # nunca commitado no git

Conclusão

Docker elimina a inconsistência entre ambientes e torna o deploy previsível. Comece com um docker-compose.yml simples para o desenvolvimento local — só isso já resolve 90% dos "funciona na minha máquina". Multi-stage builds e health checks vêm naturalmente à medida que o projeto amadurece.