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.
"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
- Use tags de image específicas —
node:20.11.1-alpine, nãonode:latest. - Faça scan das images em busca de vulnerabilidades —
docker scout cves myapp:latest. - Defina limites de recursos — evite que um único container consuma os recursos dos demais.
- Use sistemas de arquivos somente leitura sempre que possível —
read_only: trueno Compose. - Nunca execute como root — adicione
USER nodeou equivalente. - 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.