Giao diện
Thực hành: Multi-stage Builds
🎯 Mục tiêu
🎯 Sau bài thực hành này, bạn sẽ:
- Hiểu builder pattern và tách build stage khỏi runtime stage
- Giảm kích thước image từ hàng trăm MB xuống chục MB
- Biết khi nào nên và không nên dùng multi-stage
- Tối ưu layer caching trong multi-stage Dockerfile
Phần 1: Trắc nghiệm
🧠 Quiz
Câu 1: Multi-stage build giúp giảm kích thước image bằng cách nào?
- [ ] A) Nén toàn bộ image thành file zip
- [ ] B) Xóa tất cả layer trung gian sau khi build xong
- [x] C) Chỉ copy artifact cần thiết từ build stage sang runtime stage nhỏ gọn
- [ ] D) Tự động loại bỏ file trùng lặp giữa các layer
💡 Giải thích: Multi-stage cho phép bạn dùng image lớn (có compiler, build tools) ở stage đầu, rồi chỉ COPY binary/artifact sang image nhỏ (alpine, distroless) ở stage cuối. Build tools không tồn tại trong final image.
🧠 Quiz
Câu 2: Trong Dockerfile multi-stage, COPY --from=builder có ý nghĩa gì?
- [ ] A) Copy file từ host machine vào container
- [x] B) Copy file từ stage có tên "builder" sang stage hiện tại
- [ ] C) Copy file từ Docker Hub về local
- [ ] D) Copy và ghi đè file đã tồn tại trong image
💡 Giải thích:
COPY --from=builder /app/dist ./distlấy file từ stage được đặt tên quaFROM node:20 AS builder. Bạn cũng có thể dùng số thứ tự stage:COPY --from=0.
🧠 Quiz
Câu 3: Khi nào KHÔNG nên dùng multi-stage build?
- [ ] A) Khi build Go binary
- [ ] B) Khi build React/Vue frontend
- [x] C) Khi image chỉ cần interpreted language và dependencies nhẹ
- [ ] D) Khi cần tách dev dependencies khỏi production
💡 Giải thích: Nếu app chỉ cần Python + vài package nhỏ, multi-stage không mang lại lợi ích đáng kể — thêm phức tạp mà image không giảm nhiều. Multi-stage tỏa sáng khi có bước compile (Go, Rust, TypeScript) hoặc build tool nặng (webpack, gcc).
Phần 2: Sắp xếp Multi-stage Dockerfile
🧩 Parsons Problem
Bài 1: Multi-stage cho React App
Sắp xếp instruction đúng thứ tự cho Dockerfile 2 stage:
FROM node:20-alpine AS builderWORKDIR /appCOPY package.json package-lock.json ./RUN npm ciCOPY . .RUN npm run buildFROM nginx:alpineCOPY --from=builder /app/dist /usr/share/nginx/htmlEXPOSE 80CMD ["nginx", "-g", "daemon off;"]
🧩 Parsons Problem
Bài 2: Multi-stage cho Go API
Sắp xếp Dockerfile tối ưu cho Go binary:
FROM golang:1.22-alpine AS builderWORKDIR /srcCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 go build -o /app/server ./cmd/serverFROM scratchCOPY --from=builder /app/server /serverEXPOSE 8080ENTRYPOINT ["/server"]
Phần 3: Tìm lỗi — Image phình to
🐛 Spot-the-Bug
Dockerfile multi-stage bị sai
dockerfile
FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
FROM node:20
WORKDIR /app
COPY --from=builder /app .
RUN npm ci --only=production
EXPOSE 3000
CMD ["node", "dist/server.js"]Câu hỏi: Image cuối vẫn rất lớn (~900MB). Tại sao?
Đáp án:
- Runtime stage dùng
node:20(~900MB) — nên dùngnode:20-alpine(~130MB). - COPY toàn bộ
/appgồm cả source, dev deps. Chỉ nên COPY/app/distvàpackage.json. - Chạy
npm cilại — nên COPYnode_modulesproduction từ builder hoặc install riêng.