Docker
Tronco podado — o capítulo virou galho
Esta nota era um monólito de referência técnica de ~1300 linhas. Em 2026-08-02 ela foi podada: o conteúdo conceitual virou o galho Docker, com 18 notas em 3 fases sob a lente a imagem como artefato. O que permanece aqui é o material que não pertence ao galho: o relato de experiência do autor e o material de articulação em inglês, ambos preservados na íntegra.
Onde cada assunto foi parar
| Assunto que estava aqui | Onde está agora |
|---|---|
| O que é container, container × VM | 01 — O problema que o container resolve |
| Imagens, camadas, tags e digests | 02 — A anatomia de uma imagem |
| Rodar containers, ciclo de vida | 03 — O ciclo de vida de um container |
| Dockerfile, instruções principais | 04 — O Dockerfile como receita de camadas |
| Otimizar Dockerfile, layer caching | 05 — Build e cache |
| Volumes e persistência | 06 — Dados que sobrevivem ao container |
| Networks | 07 — Rede no Docker |
| ENTRYPOINT × CMD, exec × shell form | 08 — ENTRYPOINT, CMD e o container que não morre direito |
| Multi-stage builds, imagens mínimas | 09 — Multi-stage e imagens mínimas |
| BuildKit, buildx, mounts de cache e segredo | 10 — BuildKit por dentro |
| Docker Compose | 11 — Compose como ambiente de desenvolvimento |
| Registry | 12 — Registry |
| Segurança | 13 — Segurança da imagem e do runtime |
| Troubleshooting | 14 — Debugar um container |
| Arquitetura, conceitos core | 15 — Docker por dentro |
| Docker em desenvolvimento, Testcontainers | 17 — Docker em CI e na máquina de dev |
| Patterns de produção | 18 — Capstone + Operação: containers em produção |
| Armadilhas comuns | distribuídas na seção ## Armadilhas comuns de cada nota do galho |
Na prática (da minha experiência)
Docker é minha primeira linha de defesa contra “funciona na minha máquina”. No MedEspecialista, toda a stack local (Postgres, Redis, Kafka, MinIO, Keycloak) roda via Docker Compose. Onboarding de um novo dev é
git clone && docker compose up. 15 minutos e tudo está rodando.Patterns que padronizei:
1. Multi-stage sempre. Runtime image nunca tem build tools.
node:22-alpinepara build,distrolessounode:22-alpinepara runtime (dependendo de necessidades de debug).2. Usuário não-root em todo Dockerfile. Sem exceção. Imagens oficiais normalmente já têm um — use.
3. Layer ordering religioso.
COPY package*.json→npm ci→COPY source. Invalida cache só quando realmente precisa.4.
.dockerignoreantes do primeiro build. Incluinode_modules,.git,.env*,coverage/,dist/, IDE files.5. BuildKit cache mounts para node_modules e Maven repos. Build de CI caiu de 3 min para 40s.
6. Trivy scan no CI. Toda PR roda scan. Falhas com critical/high severities bloqueiam merge.
7. Digests em produção.
image@sha256:..., nãoimage:tag. Garantia absoluta de que a imagem não mudou silenciosamente.8. Healthchecks em todos os services. Compose usa
condition: service_healthyemdepends_on.Incidente memorável — imagem gigante:
API Node demorava 30+ min para deploy. Imagem era de 1.2 GB. Diagnóstico:
COPY . .incluíanode_moduleslocal (Linux vs Mac binaries),.git, logs,coverage/. Depois de.dockerignore+ multi-stage +npm ci --only=production, imagem caiu para 85 MB. Deploy de 30min → 3min.Outro incidente — container fica reiniciando:
Container Spring Boot reiniciava a cada 30s em produção. Logs mostravam
healthcheck failed. Causa:HEALTHCHECKchamavacurl http://localhost:8080/actuator/health, mascurlnão estava instalado na imagem alpine. Fix: instalar curl, OU mudar parawget, OU usar probe via JVM direto.Alpine pitfall com Node:
Alpine usa musl libc, não glibc. Alguns binários Node (sharp, bcrypt) precisam de builds específicas. Algumas bibliotecas falham silenciosamente. Solução: usar
node:22-slim(Debian) em vez de alpine quando tiver dependências nativas. 20 MB a mais, dor de cabeça zero.A lição principal: Docker é enganosamente simples. Um Dockerfile que “funciona” pode ter problemas de segurança, performance, manutenibilidade. Investir em multi-stage, security scanning, layer optimization e non-root paga por si mesmo rápido.
How to explain in English
“Docker fundamentally changed how I deploy applications. Containers provide the reproducibility I need without the overhead of VMs — same image runs identically in dev, staging, and production. That said, writing a Dockerfile that works is easy. Writing one that’s secure, small, and fast requires discipline.
My defaults: multi-stage builds separating build tools from runtime, non-root users, pinned base images (never
:latest),.dockerignoreto avoid polluting the build context, layer ordering from least to most frequently changed. For runtime images I prefer distroless over alpine — no shell, no package manager, tiny attack surface. For Go, I usescratchand statically linked binaries.Docker Compose is my default for local development environments. A single
compose.yamldescribes the entire stack — application, database, cache, message broker — and a new developer is productive in under 15 minutes withdocker compose up. I use healthchecks withdepends_on: condition: service_healthyto avoid race conditions between services.For security, I scan every image with Trivy in CI and fail builds on critical vulnerabilities. I never embed secrets in images — always passed at runtime via environment variables or mounted from Docker secrets, Kubernetes secrets, or Vault. I drop all capabilities and add back only what’s needed.
In production, I use image digests instead of tags to guarantee reproducibility, set resource limits, configure graceful shutdown handlers that catch SIGTERM, and log to stdout so Docker and Kubernetes can collect centrally. One process per container, stateless by default, with all persistent data in volumes or external services.
Common pitfalls I watch for: running as root,
COPY . .without.dockerignore, shell form in CMD/ENTRYPOINT that breaks signal handling, installing build tools in runtime images, and using:latestanywhere. And always multi-stage — the single biggest win for image size and security.”
Frases úteis em entrevista
- “Multi-stage builds always — build tools never touch runtime images.”
- “Non-root user in every Dockerfile, no exceptions.”
- “Distroless images for production — no shell, no package manager, minimal attack surface.”
- “Layer ordering from least to most frequently changed to maximize cache hits.”
- “
.dockerignoreis the first thing I add to any project.” - “Image digests in production, not tags. Reproducibility is non-negotiable.”
- “Secrets never in images — always at runtime via env vars or secret stores.”
- “One process per container. Logs to stdout. Stateless by default.”
- “BuildKit cache mounts cut my CI build times dramatically.”
- “Trivy in CI blocks critical CVEs from merging.”
Key vocabulary
- contêiner → container
- imagem → image
- camada → layer
- registro → registry
- construção multi-estágio → multi-stage build
- montagem → mount
- volume persistente → persistent volume
- verificação de saúde → healthcheck
- desligamento gracioso → graceful shutdown
- cache de camada → layer cache
- cadeia de suprimentos → supply chain
- varredura de vulnerabilidade → vulnerability scanning
- assinatura → digest
- privilégio mínimo → least privilege
- sem raiz → rootless
Recursos
Documentação
Livros e cursos
- Docker Deep Dive — Nigel Poulton (atualizado regularmente)
- Docker in Action — Jeff Nickoloff, Stephen Kuenzli
- Full Stack Open Part 12 — Containers (da Universidade de Helsinki, gratuito)
Blogs e artigos
Ferramentas
- Trivy — vulnerability scanning
- Dive — explorar layers de imagem
- Hadolint — Dockerfile linter
- ctop —
toppara containers - lazydocker — TUI para Docker
- Docker Scout — image analysis built-in
Base images recomendadas
- Distroless — Java, Python, Node, Go
- Chainguard Images — minimal, continuously updated
- Alpine — leve mas cuidado com musl libc
Veja também
- Docker (galho) — as 18 notas que sucedem este monólito
- Infraestrutura — o domínio
- Operação — a disciplina de rodar isso em produção
- Virtualização e containers — o mecanismo no kernel