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_outputSem 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 volumesComandos 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ãosOtimizando 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 buildHealth 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 gitConclusã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.