Docker / Multi-stage build

Multi-stage build

Production-д явуулах image-ийг аль болох жижиг байлгах хэрэгтэй. Гэхдээ build хийхэд олон хэрэгсэл (compiler, test runner, dev dependency) шаардлагатай. Эдгээрийг production image-д орхивол image томорч, аюулгүй байдал буурна.

Multi-stage build нь энэ асуудлыг шийддэг — нэг Dockerfile-д олон stage тодорхойлж, эцсийн image-д зөвхөн шаардлагатай файлуудыг оруулна.

Нэг stage-тай Dockerfile-ийн асуудал

dockerfile
FROM node:20

WORKDIR /app

COPY package*.json ./
RUN npm install          # dev dependency-уудыг ч суулгана

COPY . .
RUN npm run build        # TypeScript → JavaScript

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

Энд юу болж байна вэ?

  • node_modules дотор dev dependency-ууд бий (typescript, jest, eslint...)
  • Эцсийн image-д src/ TypeScript файлууд бий ч тэдгээр нь ажиллахад шаардлагагүй
  • Image хэмжээ: ~900MB

Multi-stage build — үндсэн бүтэц

dockerfile
# ── Stage 1: builder ──────────────────────────
FROM node:20 AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# ── Stage 2: production ───────────────────────
FROM node:20-alpine AS production

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY --from=builder /app/dist ./dist

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

AS builder — stage-д нэр өгнө
COPY --from=builder — өмнөх stage-аас файл хуулна
Эцсийн image-д зөвхөн dist/ ба production dependency орно

Image хэмжээ: ~180MB → 5 дахин жижиг болно.

Go апп — жишээ

Go нь compiled хэл тул binary нэг файл болдог. Alpine ч шаардлагагүй:

dockerfile
# ── Stage 1: build ────────────────────────────
FROM golang:1.22 AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/server

# ── Stage 2: minimal runtime ──────────────────
FROM scratch

COPY --from=builder /app/server /server

EXPOSE 8080
CMD ["/server"]

FROM scratch — хоосон base image, зөвхөн binary орно
Image хэмжээ: ~10MB

Python апп — жишээ

dockerfile
# ── Stage 1: dependencies ─────────────────────
FROM python:3.12 AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# ── Stage 2: production ───────────────────────
FROM python:3.12-slim

WORKDIR /app

COPY --from=builder /root/.local /root/.local
COPY . .

ENV PATH=/root/.local/bin:$PATH

CMD ["python", "main.py"]

Development vs Production — нэг Dockerfile

--target flag-аар stage сонгоно:

dockerfile
FROM node:20 AS base
WORKDIR /app
COPY package*.json ./

# ── Development ───────────────────────────────
FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]

# ── Builder ───────────────────────────────────
FROM base AS builder
RUN npm ci
COPY . .
RUN npm run build

# ── Production ────────────────────────────────
FROM node:20-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]
bash
# Development container ажиллуулах
docker build --target development -t myapp:dev .

# Production image build хийх
docker build --target production -t myapp:prod .

Image хэмжээ харьцуулалт

| Арга | Node.js апп | | ------------------------ | ----------- | | node:20 нэг stage | ~950 MB | | node:20-alpine нэг stage | ~250 MB | | Multi-stage + alpine | ~120 MB | | Multi-stage + distroless | ~80 MB |

Distroless base image

Google-ийн distroless image нь shell, package manager зэрэг бүх зүйлийг хасаж, зөвхөн runtime орчин агуулна:

dockerfile
FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

FROM gcr.io/distroless/nodejs20-debian12
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["dist/index.js"]

Shell байхгүй тул docker exec -it ... bash ажиллахгүй — debugging хийхэд хэцүү, гэхдээ production-д аюулгүй.

Дараагийн хичээлд:

Node.js Express аппликейшнийг бүрэн containerize хийх — development болон production Dockerfile бичих.