Construções Multifases do Docker Reduza o seu Imagem Sem Quebrar a Implantação
Uma demonstração prática de builds multi-etapa do Docker — tamanhos reais antes/depois das imagens, exemplos de arquivos Docker funcionais para Node.js e Python, e os erros que afetam todas as equipes pelo menos uma vez.
Sua imagem do Node.js é de 1,1 GB. Você adicionou .dockerignore, eliminou dependências de desenvolvimento, tentou node:slim — não houve mudança significativa. A solução real é as construções multifases. Se você ainda não mudou, está enviando o compilador do TypeScript para a produção.
As construções multifases estão disponíveis no Docker desde a versão 17.05 (2017). Elas são apenas pouco utilizadas. Aqui está uma demonstração real: quais as mudanças, qual a diferença de tamanho e os três erros comuns que afetam as equipes na primeira migração.
O problema da única fase
A maioria dos Dockerfiles começa assim:
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/server.js"]
Construa isso e verifique com docker images: ~1,1 GB. Você está enviando a imagem completa do Node 20 com npm, sua cadeia de ferramentas do TypeScript, todas as dependências de desenvolvimento e sua árvore de fontes. Nada disso é executado em produção — o aplicativo precisa apenas do resultado compilado e de poucos pacotes de execução. dist/ .
Construções multifases: a solução
Cada FROM instrução inicia uma nova fase com um sistema de arquivos limpo. Nomeie as fases com AS, em seguida, use COPY --from=stagename para extrair arquivos específicos para a próxima fase. As fases intermediárias não são incluídas na imagem final — elas são artefatos de construção, descartadas após a COPY ser concluída.
Aqui está o mesmo aplicativo como uma construção multifase correta:
# ---- Build stage ----
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- Runtime stage ----
FROM node:20-alpine AS runtime
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
A linha crítica: COPY --from=builder /app/dist ./dist. Essa instrução extrai o resultado compilado da fase de construção para a imagem de execução baseada em Alpine. O compilador do TypeScript, os arquivos de origem e as dependências de desenvolvimento nunca tocam a camada final. Os nomes de cookie são sensíveis a maiúsculas e minúsculas Resultado:
~160 MB em vez de 1,1 GB. Isso representa uma redução de aproximadamente 85% para uma aplicação típica do Node — e é a mesma artefato compilado, apenas sem a estrutura ao redor. Adicionar uma fase de testes
Você pode adicionar uma fase de testes entre a construção e a execução, que executa seu conjunto de testes. Se os testes falharem, a construção é interrompida antes da criação da imagem de execução. Se os testes passarem, a fase de testes é ignorada completamente ao construir para produção.
Na CI você direciona explicitamente para a fase de testes:
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM builder AS tester
RUN npm test
FROM node:20-alpine AS runtime
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]
. Para imagens de produção, você constrói sem um alvo e o Docker executa todas as fases na ordem, parando na última docker build --target tester .. A fase de testes é executada, mas seu sistema de arquivos é descartado — os testes atuam como uma porta, não como carga. FROMPython: mesma ideia, execução ligeiramente diferente
As construções multifases do Python seguem o mesmo padrão. A diferença principal: os pacotes instalados pelo pip ficam sob
quando você usa /root/.local , então você copia esse diretório para a imagem de execução leve. --userimagem base: ~1 GB.
FROM python:3.12 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
COPY . .
FROM python:3.12-slim AS runtime
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "main.py"]
python:3.12 com apenas dependências instaladas: ~180–250 MB, dependendo do que está em requirements.txt. Os arquivos compilados vêm de graça, pois estão ao lado da origem. python:3.12-slim Três erros comuns que capturam todos pelo menos uma vez .pyc 1. Copiar arquivos errados
O erro mais comum: você
. Você simplesmente copiou tudo — arquivos de origem, fixtures de teste, node_modules, etc. — para sua imagem de execução mínima. Agora ela é maior do que a versão de uma única fase.
Seja explícito sobre o que está copiando. Copie apenas o diretório ou arquivos que o ponto de entrada de produção realmente precisa. Para a maioria dos aplicativos do Node: o resultado compilado ( COPY --from=builder /app ./ em vez de COPY --from=builder /app/dist ./dist) e opcionalmente quaisquer ativos estáticos. Para o Python: os pacotes instalados e o código do aplicativo, não requirements.txt, não testes, não notebooks.
2. Dados de construção vazando nas camadasdist/Se você passa segredos como argumentos de construção (por exemplo,
seguido por uso em um
comando), esse segredo é visível em todas as camadas subsequentes — mesmo em uma construção multifase. ARG NPM_TOKEN mostrará isso. RUN A abordagem correta é o Docker BuildKit com docker history myimage O segredo é montado apenas na execução dessa camada — nunca é gravado na história da imagem. Construa com:
A solução rápida que as pessoas usam — excluir o segredo na mesma --mount=type=secret:
RUN --mount=type=secret,id=npm_token NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci
camada — não ajuda realmente com construções multifases, mas a abordagem do BuildKit é mais limpa independentemente. docker build --secret id=npm_token,src=.npmrc .
3. Esquecer o .dockerignore RUN As construções multifases reduzem o tamanho da imagem final, mas
na fase de construção ainda envia todo o contexto para o daemon do Docker. Sem um
, isso inclui COPY . . , fixtures de teste, arquivos locais .dockerignoree quaisquer segredos armazenados em arquivos planos. A fase de construção vê tudo isso. .git/, node_modules/Mínimo .env para qualquer projeto do Node: a mesma data em que você adiciona o
. O tamanho do contexto de construção aparece na primeira linha de .dockerignore saída (
.git
node_modules
dist
.env
*.log
coverage
.nyc_output
Adicionar .dockerignore ) — se esse número for suspeito, verifique o que está sendo incluído. DockerfileFerramentas úteis para o trabalho com Dockerfile docker build Se você quiser um ponto de partida antes de escrever seu próprio, oSending build context to Docker daemon X MBno IO Tools gerará um Dockerfile multifase para pilhas comuns. Uma vez que você tiver algo escrito, execute-o pelo
para detectar erros comuns antes que eles cheguem à CI — coisas como falta de
, uso de Gerador de Dockerfile tags ou execução como root desnecessariamente. Linter e Formatador de Dockerfile A lição principal WORKDIRAs construções multifases são uma mudança em duas etapas: adicione uma fase de construção nomeada, copie o resultado compilado para uma imagem mínima fresca. A redução de tamanho é quase sempre justificável — 80–90% é típico para aplicações do Node e do Python. Os principais erros são ser muito amplo com latest , vazamento de segredos como argumentos de construção e ignorar
. Corrija esses pontos e você terá uma imagem de produção realmente adequada ao ambiente de produção.
Construções Multifases do Docker: Reduza o seu Imagem Sem Quebrar a Implantação 2 COPY --fromConstruções Multifases do Docker: Reduza o seu Imagem Sem Quebrar a Implantação 1 .dockerignore. Corrija isso e você terá uma imagem de produção realmente adequada ao tamanho de produção.
Instale nossas extensões
Adicione ferramentas de IO ao seu navegador favorito para acesso instantâneo e pesquisa mais rápida
恵 O placar chegou!
Placar é uma forma divertida de acompanhar seus jogos, todos os dados são armazenados em seu navegador. Mais recursos serão lançados em breve!
Ferramentas essenciais
Ver tudo Novas chegadas
Ver tudoAtualizar: Nosso ferramenta mais recente Texto (150 itens)
