0 XP
Módulo 4 · Docker e Compose

Dockerfile e cache de layers: builds rápidos de verdade

Intermediário 45 min+25 XPDockerDockerfile

Objetivos desta aula

  • Escrever Dockerfile ordenado para cache
  • Usar .dockerignore
  • Reduzir tamanho da imagem

Cada instrução do Dockerfile cria uma layer, e o Docker reaproveita a layer se a entrada não mudou. Daí a regra de ouro: copie primeiro os arquivos de dependência (package.json e lock), instale, e só depois copie o código. Invertendo essa ordem, qualquer alteração de uma linha de código invalida a instalação e o build volta a levar minutos.

.dockerignore é tão importante quanto o Dockerfile: sem ele, node_modules local, .git e arquivos .env vão para o contexto de build, deixando tudo lento e podendo vazar segredo dentro da imagem.

Escolha de base define tamanho e superfície de ataque. Alpine é minúscula mas usa musl (o que às vezes quebra binários nativos); as variantes slim de Debian são um bom meio; distroless entrega o mínimo para rodar, sem shell — excelente para produção.

1. Cada instrução é uma camada

FROM, RUN, COPY e ADD geram camadas. O Docker reaproveita uma camada do cache se a instrução e seus arquivos de entrada não mudaram. Quando uma camada muda, todas as seguintes são refeitas.

2. Ordem certa: do que muda pouco ao que muda muito

Copie primeiro package.json e package-lock.json, rode npm ci, e só depois copie o código. Assim, editar um arquivo .ts não reinstala dependências — o build cai de minutos para segundos.

3. .dockerignore

Sem ele, node_modules, .git e arquivos .env vão para o contexto de build: build lento, cache invalidado e segredo vazado na imagem.

4. BuildKit e cache mounts

RUN --mount=type=cache,target=/root/.npm npm ci mantém o cache do npm entre builds sem colocá-lo na imagem. No CI, --cache-from/--cache-to reaproveita camadas entre execuções.

5. CMD, ENTRYPOINT e forma exec

Use a forma JSON CMD ["node", "dist/server.js"]: o processo vira PID 1 e recebe SIGTERM. A forma shell (CMD node server.js) coloca um sh no meio que não repassa sinais.

6. Checagem final

Altere uma linha do código e rebuilde: apenas as últimas camadas devem ser refeitas.

Na prática

Dockerfile da API (ordem correta de cache)

dockerfile

FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

FROM node:22-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -S app && adduser -S app -G app
COPY --from=deps /app/node_modules ./node_modules
COPY --chown=app:app src ./src
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD node -e "fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "src/server.js"]

Nota de segurança: Nunca use ARG/ENV para segredo: valores ficam gravados no histórico da imagem e são legíveis por qualquer um com docker history.

.dockerignore

text

node_modules
npm-debug.log
.git
.gitignore
.env
.env.*
coverage
dist
Dockerfile
docker-compose*.yml
**/*.md

Dockerfile otimizado para cache

dockerfile

# syntax=docker/dockerfile:1.7
FROM node:20.15-alpine
WORKDIR /app
COPY package.json package-lock.json ./          # muda raramente
RUN --mount=type=cache,target=/root/.npm npm ci # camada reaproveitada
COPY . .                                         # muda sempre, fica por ultimo
RUN npm run build
CMD ["node", "dist/server.js"]                   # forma exec: recebe SIGTERM

.dockerignore mínimo

text

node_modules
.git
.env*
dist
coverage
*.log

Nota de segurança: Nunca copie arquivos .env para a imagem: qualquer pessoa com acesso à imagem lê os segredos.

Por que isso importa

Build de 8 minutos vira 40 segundos com ordenação correta — impacto direto no tempo de entrega do time.

Erro comum

COPY . . antes de npm ci, destruindo o cache em toda alteração de código.

Dica de produção

Fixe a versão da base (node:22.11-alpine) em vez de tag móvel para builds reprodutíveis.

Alerta de segurança

Rode scan de vulnerabilidades na imagem no CI e atualize a base periodicamente.

Pergunta de entrevista

Como você diagnostica um build de Docker que ficou lento após uma mudança pequena?

Glossário

contexto de build
Conjunto de arquivos enviados ao daemon Docker durante o build.
distroless
Imagem sem shell nem gerenciador de pacotes, contendo só o runtime necessário.
camada
Diferença de filesystem gerada por uma instrução do Dockerfile.
contexto de build
Arquivos enviados ao Docker para construir a imagem.
BuildKit
Motor de build moderno do Docker, com cache mounts e builds paralelos.

Conexão com o CloudShop

Escrever o Dockerfile definitivo da API do CloudShop com usuário não-root.

Quiz da aula

  1. 1. Por que copiar package.json antes do código-fonte?

  2. 2. Por que copiar package.json antes do código?

  3. 3. Qual forma de CMD repassa SIGTERM corretamente?

Minhas anotações

Salvo automaticamente neste navegador.

AnteriorPróxima