FNSP Web Page

Docker multi-stage: a imagem que não leva o que não usa

Uma imagem Docker de uma aplicação Node feita à pressa leva lá dentro o compilador de TypeScript, as ferramentas de teste, o node_modules de desenvolvimento inteiro e o código-fonte. Nada disso corre em produção — mas tudo isso é descarregado a cada deploy, e tudo isso é superfície de ataque.

O multi-stage build resolve isto com uma ideia simples: uma imagem para construir, outra para correr, e da primeira só passa para a segunda o que interessa.

1. O antes

FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "dist/servidor.js"]

Funciona. E leva para produção o código-fonte, os testes, o TypeScript e tudo o que o npm install trouxe para construir.

2. O depois

# --- Etapa 1: construir ---
FROM node:20-alpine AS build
WORKDIR /app

# Copiar primeiro os manifestos: enquanto eles não mudarem, o Docker
# reaproveita a camada das dependências e salta o npm ci todo.
COPY package.json package-lock.json ./
RUN npm ci

COPY . .
RUN npm run build

# --- Etapa 2: correr ---
FROM node:20-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production

COPY package.json package-lock.json ./
RUN npm ci --omit=dev

# Só o resultado da compilação atravessa
COPY --from=build /app/dist ./dist

# O node:alpine já traz um utilizador 'node'. Correr como root dentro do
# contentor é um privilégio que a aplicação não precisa.
USER node

CMD ["node", "dist/servidor.js"]

3. Onde estão mesmo os ganhos

  • Tamanho: tudo o que ficou na primeira etapa é deitado fora. A imagem final tem o Node, as dependências de produção e o dist.

  • Cache: copiar o package-lock.json antes do resto do código é o truque que mais tempo poupa. Mudar uma linha de código não volta a instalar dependências nenhumas.

  • Segurança: o que não está na imagem não tem vulnerabilidades. E o USER node é uma linha.

4. E o .dockerignore

Sem ele, o COPY . . manda para dentro do contexto de build o node_modules local, o .git inteiro e — pior — o .env:

node_modules
.git
.env
dist
*.log

Vale a pena olhar para ele antes de olhar para o Dockerfile. É o ficheiro que decide o que sequer chega a ser considerado.

Comentários

Ainda ninguém comentou este artigo.

Voltar ao blog

Gostávamos de saber quantas pessoas visitam o site, com o Google Analytics. Sem a sua autorização não corre nada. Política de Privacidade.