Giới thiệu
Dockerfile đa giai đoạn (multi‑stage build) là một kỹ thuật mạnh mẽ giúp bạn tách riêng quá trình biên dịch và tạo ra môi trường chạy thực tế. Khi áp dụng đúng, kích thước image cuối cùng có thể giảm tới 80% so với cách viết truyền thống, đồng thời loại bỏ các công cụ không cần thiết, giảm bề mặt tấn công và tăng tốc độ triển khai.
Tại sao nên sử dụng multi‑stage build
Trong môi trường production, mỗi megabyte đều có giá trị. Image lớn không chỉ tốn thời gian tải xuống mà còn tiêu tốn nhiều tài nguyên lưu trữ trên registry. Multi‑stage build cho phép bạn:
- Giảm kích thước image bằng cách chỉ sao chép những file cần thiết vào stage cuối cùng.
- Tách biệt môi trường build và runtime – các công cụ biên dịch, package manager chỉ tồn tại trong stage đầu, không xuất hiện trong container chạy thực tế.
- Quản lý phụ thuộc một cách rõ ràng – mỗi stage có thể dựa trên một base image phù hợp với mục đích của nó.
Giảm kích thước image
Ví dụ, khi xây dựng một ứng dụng Node.js, bạn có thể dùng node:18-alpine làm stage build để cài đặt các package và biên dịch mã nguồn, sau đó chuyển sang node:18-alpine nhẹ hơn (hoặc scratch) để chỉ chứa file dist và node_modules đã được tối ưu.
Tách biệt môi trường build và runtime
Việc tách biệt này giúp giảm bề mặt tấn công vì các công cụ như gcc, make không còn tồn tại trong container production. Ngoài ra, khi có lỗ hổng bảo mật trong một công cụ biên dịch, bạn không cần cập nhật image runtime.
Cách viết Dockerfile đa giai đoạn chuẩn
Dưới đây là một Dockerfile mẫu cho một ứng dụng Node.js sử dụng TypeScript. Các stage được đặt tên rõ ràng và chỉ sao chép những gì thực sự cần thiết.
# Stage 1: Build FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY tsconfig.json ./ COPY src/ ./src/ RUN npm run build # Stage 2: Production runtime FROM node:18-alpine AS runtime WORKDIR /app # Chỉ sao chép file đã build và các dependency production COPY --from=builder /app/dist ./dist COPY --from=builder /app/package*.json ./ RUN npm ci --only=production EXPOSE 3000 CMD ["node", "dist/index.js"]
Trong ví dụ trên, npm ci được chạy ở cả hai stage, nhưng ở stage cuối chỉ cài đặt các dependency production, giúp giảm kích thước cuối cùng.
Kỹ thuật tối ưu thêm
Một số mẹo bổ sung để giảm dung lượng image:
- Sử dụng
--no-cachekhi cài package: Tránh lưu lại các file tạm thời trong layer cuối. - Loại bỏ file .git, docs, test bằng
.dockerignoređể chúng không được sao chép vào bất kỳ stage nào. - Chọn base image nhẹ:
alpinethường nhỏ hơndebian, nhưng cần kiểm tra tính tương thích của các binary. - Sử dụng
scratchcho các ứng dụng Go hoặc Rust đã biên dịch tĩnh, vì không có bất kỳ layer nào khác. - Minify static assets (HTML, CSS, JS) trước khi copy vào stage runtime.
Kiểm tra kích thước và layer
Sau khi build, bạn có thể dùng lệnh docker image ls để xem kích thước tổng thể, và docker history <image_name> để kiểm tra số lượng layer. Nếu có layer không cần thiết, hãy hợp nhất chúng bằng cách dùng && trong một lệnh RUN duy nhất.
Kết luận
Áp dụng multi‑stage build không chỉ giúp giảm kích thước image mà còn nâng cao bảo mật và hiệu suất triển khai. Khi đã nắm vững các nguyên tắc trên, bạn có thể tự tin tối ưu bất kỳ dự án nào, từ Node.js, Python tới Java.
Tham khảo khóa học "Làm chủ Docker từ cơ bản đến nâng cao" tại đây








