Uma imagem Docker de 1,2GB carrega compiladores, gerenciadores de pacote e devDependencies para servir uma aplicação que precisa de 90MB. Multi-stage builds resolvem isso separando compilação de execução: você constrói em um estágio e copia só o artefato para o estágio final. O app Express deste exemplo caiu de 1,1GB para 150MB com Alpine e para 90MB com distroless, sem mudar uma linha de código.

O que sobra dentro de uma imagem single-stage?

O estágio único mistura o que constrói com o que executa. node_modules completo com devDependencies, gcc, curl e cache do pip entram em produção junto. Cada pacote extra aparece no relatório do Trivy e amplia a superfície de ataque; pulls de 1GB em cada nó do cluster custam minutos por deploy.

Como funciona a anatomia de um multi-stage build?

O Dockerfile declara dois FROM. O estágio builder instala tudo e compila; o estágio runtime parte de uma base limpa e recebe só o resultado com COPY --from=builder /app/dist ./dist. Essa diretiva carrega o truque inteiro: camadas do builder jamais chegam à imagem final.

Qual base image escolher para o runtime?

Variantes Alpine encolhem a imagem, e musl libc quebra módulos nativos compilados contra glibc; teste antes de migrar. Imagens distroless shipam sem shell e sem gerenciador de pacotes, então não existe ferramenta para um atacante explorar. A tag :debug devolve busybox e salva o incident response quando você precisa executar algo dentro do pod.

Como acelerar o build com layer caching?

A ordem das instruções define o cache. Copie package.json antes do código-fonte, rode a instalação de dependências e só então copie o resto. Uma alteração de código reaproveita as camadas de dependência e transforma builds de minutos em segundos.

O que muda em cada linguagem?

  • Node: rode npm ci --omit=dev no estágio final e copie a saída do tsc do builder
  • Go: compile com CGO_ENABLED=0 e suba um binário estático de ~10MB sobre scratch
  • Python: mantenha as dependências de build do pip no builder e copie as wheels para o estágio slim

Como endurecer a segurança da imagem?

Adicione um usuário non-root com USER, fixe a base por digest (node@sha256:...) em vez de tag móvel e use COPY --chown para evitar arquivos owned pelo root. Em Kubernetes, monte o root filesystem como read-only. Nenhuma dessas medidas exige mudança no código.

Como verificar o resultado?

dive inspeciona camada por camada e aponta desperdício; docker history expõe segredo colado em ARG ou RUN. O Trivy roda no CI e bloqueia o deploy quando CVE alta aparece na imagem final.

O ganho composto aparece no pipeline: pulls dez vezes mais rápidos, scan enxuto e menos pacote para patchear na próxima emergência. Comece pelo Dockerfile mais antigo do monorepo; o de 1,2GB sempre existe.