跳至主要內容

Dockerfile 多階段建置:打造精簡高效的容器映像檔

Dockerfile 多階段建置:打造精簡高效的容器映像檔

Docker 映像檔的大小直接影響部署速度和資源消耗。多階段建置(Multi-stage Build)是優化映像檔大小的關鍵技術,可以讓生產用的映像檔只包含真正需要的執行檔,而不是整個開發環境。

沒有多階段建置的問題

# 舊的寫法:建置環境和執行環境混在一起
FROM node:20

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

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

這個映像檔包含:Node.js 完整開發環境、npm、所有 devDependencies、原始碼……最終大小可能超過 1GB。

多階段建置基本範例(Node.js)

# 階段 1:建置
FROM node:20-alpine AS builder

WORKDIR /app

# 先複製 package.json,利用 Docker 層快取
COPY package*.json ./
RUN npm ci --include=dev

# 複製原始碼並建置
COPY . .
RUN npm run build

# 階段 2:正式執行環境
FROM node:20-alpine AS runner

WORKDIR /app

# 只安裝 production 依賴
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force

# 從建置階段複製已編譯的產物
COPY --from=builder /app/dist ./dist

# 非 root 用戶執行(安全最佳實踐)
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001
USER nextjs

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

效果:映像檔從 1.2GB 縮小到 150MB。

Go 語言多階段建置(最終映像超小)

# 建置階段
FROM golang:1.22-alpine AS builder

WORKDIR /app

# 利用模組快取
COPY go.mod go.sum ./
RUN go mod download

COPY . .

# 靜態連結,不依賴外部函式庫
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags='-s -w' -o server ./cmd/server

# 執行階段:使用最小基礎映像
FROM scratch

# 複製 TLS 憑證(scratch 沒有內建)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# 複製執行檔
COPY --from=builder /app/server /server

EXPOSE 8080
ENTRYPOINT ["/server"]

最終映像大小:約 10-15MB。

Next.js 多階段建置

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

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs
EXPOSE 3000
ENV PORT 3000

CMD ["node", "server.js"]

Python 多階段建置

# 建置階段:安裝和編譯依賴
FROM python:3.12-slim AS builder

WORKDIR /app

# 安裝建置工具
RUN pip install --upgrade pip

COPY requirements.txt .
RUN pip install --user --no-warn-script-location -r requirements.txt

# 執行階段
FROM python:3.12-slim AS runner

WORKDIR /app

# 從建置階段複製安裝好的套件
COPY --from=builder /root/.local /root/.local
COPY . .

ENV PATH=/root/.local/bin:$PATH
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

層快取最佳化

# 好的做法:先複製 lock file,再複製原始碼
# 只有依賴變化時才重新 npm install
FROM node:20-alpine

WORKDIR /app

# 先複製(這兩個檔案很少變動)
COPY package.json package-lock.json ./
RUN npm ci

# 再複製原始碼(這個常變動)
COPY . .
RUN npm run build

.dockerignore

# .dockerignore
node_modules
dist
.git
.gitignore
*.md
.env*
.DS_Store
coverage
.nyc_output
Dockerfile*
docker-compose*

建置和推送

# 建置映像
docker build -t myapp:latest .

# 多平台建置(Apple Silicon Mac 上建置 amd64)
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .

# 查看映像大小
docker images myapp

# 分析映像層
docker history myapp:latest

# 使用 dive 工具更詳細分析
dive myapp:latest

多階段建置是現代容器化的基本功,好的 Dockerfile 不只讓映像更小,也讓建置速度更快(透過層快取),並提升安全性(最終映像不含開發工具)。

分享這篇文章