Docker: Construção Multi-estágio

Última atualização: 2026-08-26

Uma imagem encolhe de 1,2 GB para 12 MB — builds multi-estágio são a técnica mais eficaz para reduzir o tamanho da imagem.

1. O Que Você Vai Aprender



2. Uma História Real de uma Pessoa Desenvolvedora Go

(1) Ponto de Dor: Uma imagem de aplicação de 1,2 GB

A imagem da aplicação Go de Alice tem 1,2 GB — porque ela usou golang:1.22 como imagem base, que inclui o compilador Go completo, código fonte e toolchain. Mas o ambiente de produção precisa apenas de um binário compilado de 12 MB. Cada implantação leva 5 minutos para baixar a imagem, e o servidor na nuvem com 50 MB de banda fica lento a cada lançamento.

(2) Uma Abordagem de Construção Multi-estágio

Bob a ensinou a usar builds multi-estágio: "No primeiro estágio, use golang:1.22 para compilar; no segundo estágio, copie a saída compilada do Alpine."

DOCKERFILE
# Estágio 1: Build
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o server

# Estágio 2: Execução (apenas o binário, sem o compilador Go)
FROM alpine:3.19
COPY --from=builder /src/server /usr/local/bin/
CMD ["server"]

(3) Benefício: O tamanho da imagem foi reduzido de 1,2 GB para 12 MB

O tamanho da imagem foi reduzido em 100 vezes, o tempo de download e implantação caiu de 5 minutos para 5 segundos, e as vulnerabilidades de segurança foram reduzidas de mais de 200 para 10 (o compilador não está incluído na imagem de produção).



3. Princípios da Construção Multi-estágio

(1) Comparação Entre Estágio Único e Multi-estágio

100%
graph TB
    subgraph Single["Build de Estágio Único"]
        S1["golang:1.22<br/>Base 800 MB"] --> S2["go build<br/>Saída da Compilação"] --> S3["Imagem Final<br/>1,2 GB<br/>(Inclui compilador + código fonte)"]
    end
    subgraph Multi["Build Multi-estágio"]
        M1["Estágio 1: golang:1.22<br/>800 MB (descartado)"] -->|"COPY --from"| M3["Estágio 2: alpine:3.19<br/>Base 7 MB"]
        M1 --> M2["go build<br/>Saída da Compilação"]
        M2 -->|"Copiar apenas o binário"| M3
        M3 --> M4["Imagem Final<br/>12 MB<br/>(Apenas o Binário)"]
    end

(1) Mecanismo Central

Estágio Propósito Conteúdo Imagem Final
Estágio Builder Compilação/Build Compilador, código fonte, ferramentas de build ❌ Descartado
Estágio Runner Execução Copiar apenas artefatos necessários ✅ Retido
📌 Ponto-chave: A construção multi-estágio não comprime a imagem; em vez disso, impede que itens desnecessários sejam incluídos na imagem desde o início. Todas as camadas do estágio Builder são descartadas, e apenas as camadas do estágio Runner são incluídas na imagem final.



4. Prática de Builds Multi-estágio

▶ Exemplo: Builds multi-estágio em Go (Dificuldade: ⭐⭐)

DOCKERFILE
# ============================================
# Estágio 1: Compilar o binário Go
# ============================================
FROM golang:1.22 AS builder

WORKDIR /src

# Copiar arquivos de dependência primeiro (otimização de cache)
COPY go.mod go.sum ./
RUN go mod download

# Copiar código fonte e compilar
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server

# ============================================
# Estágio 2: Criar imagem de runtime mínima
# ============================================
FROM alpine:3.19

# Instalar certificados CA (para requisições HTTPS)
RUN apk --no-cache add ca-certificates

# Copiar apenas o binário compilado do builder
COPY --from=builder /src/server /usr/local/bin/server

# Executar como usuário não-root
RUN adduser -D -u 1000 appuser
USER appuser

EXPOSE 8080
CMD ["server"]
BASH
# Construir a imagem multi-estágio
docker build -t go-app:1.0 .

# Verificar o tamanho da imagem final
docker images go-app:1.0
💻 Saída:

TEXT 📖 Somente leitura
REPOSITORY   TAG   IMAGE ID       SIZE
go-app       1.0   a1b2c3d4e5f6   12.5MB

▶ Exemplo: Build com Node.js → Executar com Nginx (Dificuldade: ⭐⭐⭐)

Projetos front-end como React e Vue: Arquivos estáticos são gerados durante a fase de build, e o Nginx é usado para servir requisições HTTP durante o runtime.

DOCKERFILE
# ============================================
# Estágio 1: Compilar aplicação React
# ============================================
FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ============================================
# Estágio 2: Servir com Nginx
# ============================================
FROM nginx:1.25-alpine

# Copiar ativos compilados do estágio builder
COPY --from=builder /app/build /usr/share/nginx/html

# Configuração personalizada do Nginx para roteamento SPA
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
💡 Dica: Builds multi-estágio são mais eficazes para projetos front-end — node_modules pode ter 500+ MB, mas a saída do build (HTML/CSS/JS) geralmente tem apenas 5–20 MB.

▶ Exemplo: Clonando um venv Python (Dificuldade: ⭐⭐)

Projetos Python também podem ser construídos em múltiplos estágios: o primeiro estágio cria um ambiente virtual, e o segundo estágio simplesmente copia o ambiente virtual e o código da aplicação.

DOCKERFILE
# Estágio 1: Criar ambiente virtual
FROM python:3.12 AS builder

WORKDIR /app
COPY requirements.txt .
RUN python -m venv /opt/venv && \
    /opt/venv/bin/pip install --no-cache-dir -r requirements.txt

# Estágio 2: Executar apenas com o venv
FROM python:3.12-slim

WORKDIR /app
COPY --from=builder /opt/venv /opt/venv
COPY . .

ENV PATH="/opt/venv/bin:$PATH"
CMD ["python", "app.py"]


5. Selecionando uma Imagem Base para Execução

(1) Comparação das Imagens de Três Bases Mínimas

Imagem Base Tamanho Inclui Shell Inclui Gerenciador de Pacotes Inclui libc Casos de Uso
scratch 0 MB Binários Go/Rust compilados estaticamente
distroless 2–20 MB ✅ (glibc) Do Google, extremamente seguro
alpine 7 MB ✅ (ash) ✅ (apk) ✅ (musl) Quando a depuração é necessária

(2) Observações sobre a Imagem Scratch

DOCKERFILE
# Scratch: absolutamente nada na imagem
FROM scratch
COPY --from=builder /src/server /server
CMD ["/server"]
⚠️ Nota: A imagem scratch não inclui shell, ca-certificates ou dados de fuso horário. Programas Go devem ser compilados estaticamente CGO_ENABLED=0 para executar em scratch. Se seu programa requer requisições HTTPS ou logs com timestamp, é mais seguro usar alpine.



6. Técnicas Avançadas para Builds Multi-estágio

▶ Exemplo: Nomeação de Estágios e --target para Seleção de Build (Dificuldade: ⭐⭐)

DOCKERFILE
# Estágios nomeados para clareza
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o server

# Alternativa: estágio de desenvolvimento com ferramentas de hot-reload
FROM golang:1.22 AS dev
WORKDIR /src
RUN go install github.com/cosmtrek/air@latest
COPY . .
CMD ["air"]

# Produção: runtime mínimo
FROM alpine:3.19 AS prod
COPY --from=builder /src/server /usr/local/bin/
CMD ["server"]
BASH
# Construir apenas o estágio dev
docker build --target dev -t myapp:dev .

# Construir apenas o estágio prod
docker build --target prod -t myapp:prod .

▶ Exemplo: --target para depurar artefatos intermediários (Dificuldade: ⭐⭐⭐)

BASH
# Construir e inspecionar o estágio builder
docker build --target builder -t myapp:builder .

# Executar a imagem builder para depurar problemas de build
docker run -it myapp:builder bash

# Verificar o binário compilado
docker run --rm myapp:builder ls -la /src/server


7. Referência de Tamanhos de Saída de Compilação por Linguagem

Linguagem Imagem Base Saída do Build Imagem Final Taxa de Redução
Go golang:1.22 (780 MB) Binário estático 12 MB alpine: 12 MB 65x
Rust rust:1.75 (1,5 GB) Binário estático 8 MB alpine: 15 MB 100x
Node.js node:20 (1,1 GB) build/ 5-20 MB nginx-alpine: 25 MB 44x
Java eclipse-temurin:21 (450 MB) JAR 30 MB jre-alpine: 170 MB 2,6x
Python python:3.12 (1 GB) venv/ 50 MB slim: 200 MB 5x


8. Exemplo Completo: Build Multi-estágio para uma Aplicação Web Go

DOCKERFILE
# ============================================
# Dockerfile multi-estágio para aplicação web Go
# Estágio 1: Build com cache de dependências
# Estágio 2: Runtime Alpine mínimo
# ============================================

# ---------- Estágio 1: Builder ----------
FROM golang:1.22-alpine AS builder

# Instalar git (para go mod download com repositórios privados)
RUN apk add --no-cache git

WORKDIR /src

# Cache: copiar arquivos de dependência primeiro
COPY go.mod go.sum ./
RUN go mod download

# Copiar código fonte
COPY . .

# Compilar binário estático (CGO_ENABLED=0 para scratch/alpine)
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-w -s" -o /app/server

# ---------- Estágio 2: Runtime ----------
FROM alpine:3.19

# Instalar dependências de runtime
RUN apk --no-cache add ca-certificates tzdata

# Criar usuário não-root
RUN adduser -D -u 1000 appuser

WORKDIR /app

# Copiar apenas o binário do builder
COPY --from=builder /app/server .

# Definir propriedade
RUN chown -R appuser:appuser /app

USER appuser

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
  CMD wget -qO- http://localhost:8080/health || exit 1

CMD ["/app/server"]
BASH
# Construir
docker build -t go-web:1.0 .

# Executar
docker run -d -p 8080:8080 --name go-web go-web:1.0

# Verificar tamanho e saúde
docker images go-web:1.0
docker inspect go-web --format='Size: {{.Size}}, Health: {{.State.Health.Status}}'
💻 Saída:

TEXT 📖 Somente leitura
REPOSITORY   TAG   SIZE
go-web       1.0   15.2MB

Size: 15974400, Health: healthy

❓ Perguntas Frequentes

P: A construção multi-estágio afeta a velocidade de build? R: Não tem um impacto significativo. As camadas do estágio Builder ainda são armazenadas em cache — se o código não mudou, go build usa o cache diretamente. Apenas a imagem final não inclui as camadas do Builder. A velocidade de build é quase a mesma de um build de estágio único, mas o tamanho da imagem é significativamente reduzido.

P: Posso usar builds multi-estágio para depuração? R: Sim. Use --target builder para construir até o estágio Builder, depois use docker run -it myapp:builder bash para entrar no estágio de depuração. Você também pode definir um estágio dev no Dockerfile que inclua ferramentas de depuração (como dlv ou air).

P: Como depurar sem um shell na imagem scratch? R: Como a imagem scratch não tem shell, você não pode usar docker exec para entrar nela. Métodos de depuração: ① Construir uma imagem de depuração usando --target builder; ② Alterar temporariamente FROM scratch para FROM alpine para depuração; ③ Usar docker cp no host para copiar arquivos de log para fora.

P: Segredos podem ser passados através de builds multi-estágio? R: Não inclua segredos no estágio Builder — embora a camada Builder não seja incluída na imagem final, docker history ainda é visível. Use --mount=type=secret (um recurso do BuildKit) para passar segredos de forma segura: RUN --mount=type=secret,id=ssh_key cp /run/secrets/ssh_key /root/.ssh/id_rsa.

P: A imagem distroless é segura? R: É muito segura. A imagem distroless mantida pelo Google não inclui shell, gerenciador de pacotes ou qualquer ferramenta não essencial, resultando em uma superfície de ataque extremamente pequena. No entanto, a depuração é difícil (pois não há shell), então recomendamos usar Alpine para desenvolvimento e distroless para produção (com depuração externa via logs e monitoramento).

P: Por que imagens Go usam Alpine em vez de Scratch? R: Alpine fornece ca-certificates (necessário para requisições HTTPS) e dados de fuso horário (necessário para timestamps de log), além de um shell para depuração de emergência. Os 5 MB extras valem muito a pena. Scratch é usado apenas em cenários que exigem otimização extrema (como dispositivos embarcados).


📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Compare os tamanhos das imagens de aplicação Go construídas usando build de estágio único e multi-estágio — primeiro use FROM golang:1.22 para um build de estágio único, depois use um build multi-estágio, e registre a diferença de tamanho entre as duas imagens.
  2. Exercício Avançado (Dificuldade: ⭐⭐): Construa uma aplicação React em múltiplos estágios: node:20-alpine Build → nginx:1.25-alpine Executar. Compare os tamanhos finais da imagem base Node.js e da imagem base Nginx.
  3. Desafio (Dificuldade: ⭐⭐⭐): Use docker history para analisar o tamanho de cada camada em uma imagem construída com multi-estágio e explique por que as camadas do estágio Builder não estão incluídas na imagem final.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%