Dockerfile: Multi-stage builds în Docker

Dockerfile Docker 17.05+

Construiește aplicația într-o etapă cu compilatoare și copiază doar rezultatul într-o imagine finală mică.

Sintaxă comandă
FROM imagine_build AS builder
...
FROM imagine_runtime
COPY --from=builder /cale/artefact /cale/destinație

Ce face Dockerfile: Multi-stage builds

Pentru a compila o aplicație (Go, Rust, Java, TypeScript, frontend React/Vite) ai nevoie de compilatoare, SDK-uri și devDependencies care ocupă sute de megabiți, dar la rulare ai nevoie doar de binarul compilat sau de fișierele statice. Build-urile multi-stage îți permit să folosești mai multe instrucțiuni FROM în același Dockerfile.

Fiecare FROM începe o etapă nouă și curată. Cu COPY --from=builder iei din etapa anterioară strict fișierele rezultate, iar tot restul (compilatorul, codul sursă, cache-ul de build) este aruncat. Imaginea rezultată este ultima etapă din fișier, dacă nu specifici alta cu docker build --target.

O imagine Go construită clasic cu golang:1.22 are peste 800 MB; cu un build multi-stage care copiază binarul în alpine sau gcr.io/distroless/static, imaginea finală scade la 10–25 MB și are o suprafață de atac mult mai mică.

Opțiuni și argumente

Opțiune / Parametru Descriere
FROM ... AS nume Denumește etapa curentă pentru a te referi la ea mai jos.
COPY --from=nume Copiază fișiere din etapa nume (sau după index, de ex. --from=0) în etapa curentă.
COPY --from=imagine:tag Copiază un binar direct dintr-o imagine externă (de ex. COPY --from=composer:2 /usr/bin/composer /usr/bin/composer).
docker build --target nume Oprește construcția la etapa indicată (util când ai o etapă dev sau test și una prod în același Dockerfile).

Exemple

Exemplul 1: Aplicație Go: compilare în golang, rulare în alpine (15 MB)

În imaginea finală ajunge doar binarul compilat /usr/local/bin/api, fără compilatorul Go și fără codul sursă.

# Etapa 1: compilare
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /out/api ./cmd/api

# Etapa 2: imaginea finală de producție
FROM alpine:3.20
RUN apk add --no-cache ca-certificates
COPY --from=builder /out/api /usr/local/bin/api
USER nobody
EXPOSE 8080
ENTRYPOINT ["api"]

Exemplul 2: Frontend React/Vite compilat cu Node și servit cu Nginx

Node.js este folosit doar pentru npm run build; imaginea finală conține doar Nginx și fișierele statice din dist/.

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html

Greșeli frecvente

  • Compilarea unui binar Go fără CGO_ENABLED=0 pe o imagine Debian și copierea lui în alpine sau scratch: la pornire apare eroarea confuză exec /usr/local/bin/api: no such file or directory (deoarece lipsește linkerul dinamic glibc).
  • Uitarea faptului că variabilele ENV și pachetele instalate în prima etapă nu se transmit în etapa a doua: în ultima etapă există doar ce instalezi sau copiezi în ea.

Comenzi înrudite

  • docker build: Construiește o imagine pe baza unui Dockerfile și a unui context de build.
  • Dockerfile: FROM și WORKDIR: Alege imaginea de bază a construcției și setează directorul curent de lucru în imagine.
  • Dockerfile: COPY și ADD: Copiază fișiere și directoare din contextul de build (sau din altă etapă) în imagine.
  • docker images: Listează imaginile descărcate sau construite local, cu etichetă, ID și dimensiune.