Docker for Backend & AI Engineers
Trang chủ / Module 2
🏗️ Module 2 / 12 · 1-2 tuần

Dockerfile & Image Building

Cú pháp Dockerfile, cơ chế layer & build cache, multi-stage build để giảm kích thước image, best practices.

DockerfileLayersMulti-stage
📌 Vì sao module này quan trọng

Dockerfile là "mã nguồn" định nghĩa image của bạn — viết tốt giúp build nhanh hơn nhiều lần (tận dụng cache) và image nhỏ hơn nhiều lần (ảnh hưởng trực tiếp đến tốc độ deploy và chi phí lưu trữ/băng thông registry). Đây là kỹ năng thực hành cốt lõi nhất của cả khoá học.

🎯 Mục tiêu học tập

  • Viết Dockerfile đúng cú pháp với các instruction phổ biến nhất
  • Hiểu cơ chế layer và build cache để viết Dockerfile build nhanh
  • Áp dụng multi-stage build để tách biệt môi trường build và môi trường chạy, giảm kích thước image
  • Áp dụng các best practice giảm kích thước và tăng tốc build

Cú pháp Dockerfile cơ bản

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
InstructionVai trò
FROMChọn base image làm điểm khởi đầu
WORKDIRĐặt thư mục làm việc bên trong image cho các lệnh tiếp theo
COPY / ADDSao chép file từ máy build vào image (COPY nên ưu tiên hơn ADD trừ khi cần tính năng đặc biệt như tự giải nén)
RUNChạy lệnh lúc build image (cài package, biên dịch...)
CMDLệnh mặc định chạy khi container khởi động (có thể bị ghi đè lúc docker run)
ENTRYPOINTTương tự CMD nhưng khó ghi đè hơn — thường dùng khi image chỉ nên chạy đúng 1 chương trình cố định
EXPOSEKhai báo (mang tính tài liệu) port ứng dụng lắng nghe — không tự động publish port ra ngoài
ENVĐặt biến môi trường có sẵn trong container

Layer & Build Cache — vì sao thứ tự trong Dockerfile quan trọng

Mỗi instruction (RUN, COPY...) tạo ra một layer mới, được cache lại. Khi build lại, Docker so sánh từng layer với lần build trước — nếu instruction và input không đổi, dùng lại cache thay vì chạy lại, giúp build nhanh hơn rất nhiều. Nhưng ngay khi một layer bị "cache miss" (thay đổi), mọi layer phía sau đều phải build lại — đây là lý do thứ tự trong ví dụ trên quan trọng: COPY requirements.txt + RUN pip install được đặt trước COPY . . (copy toàn bộ code) — vì code ứng dụng đổi liên tục nhưng dependency ít đổi hơn, tách riêng giúp pip install (bước chậm) được cache lại hầu hết các lần build, chỉ chạy lại khi requirements.txt thực sự thay đổi.

⚠️ Lỗi phổ biến nhất

COPY . . rồi mới RUN pip install — bất kỳ thay đổi nhỏ nào trong code cũng làm mất cache của bước cài dependency, khiến build luôn chậm dù chỉ sửa 1 dòng code.

Multi-stage Build — tách môi trường Build và Runtime

Nhiều ngôn ngữ cần công cụ build nặng (compiler, dev dependency) nhưng runtime thực tế không cần đến chúng. Multi-stage build cho phép dùng nhiều FROM trong 1 Dockerfile, mỗi stage có thể lấy kết quả từ stage trước, nhưng image cuối cùng chỉ chứa những gì thực sự cần để chạy:

# Stage 1: build — dùng image đầy đủ có công cụ biên dịch
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: runtime — chỉ lấy kết quả build, dùng image nhỏ hơn nhiều
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80

Kết quả: image cuối không hề chứa Node.js, npm, hay source code gốc — chỉ có file tĩnh đã build và nginx, thường nhỏ hơn hàng trăm MB so với cách build gộp 1 stage.

Best Practices giảm kích thước & tăng tốc build

  • Chọn base image tối giản: ưu tiên bản -slim hoặc -alpine thay vì bản đầy đủ, trừ khi cần các thư viện hệ thống mà bản tối giản thiếu.
  • Gộp RUN liên quan bằng &&: mỗi RUN tạo 1 layer — gộp các lệnh liên quan (update, install, cleanup cache) trong cùng 1 RUN để tránh layer trung gian giữ lại dữ liệu tạm không cần thiết.
  • Dùng .dockerignore: loại trừ node_modules, .git, file log... khỏi build context — giảm thời gian gửi context lên daemon và tránh copy nhầm file không cần thiết vào image.
  • Xoá cache package manager trong cùng layer cài đặt: ví dụ apt-get install ... && rm -rf /var/lib/apt/lists/* trong cùng 1 RUN, không phải ở RUN riêng (nếu tách riêng, dữ liệu vẫn tồn tại ở layer trước, không giảm kích thước tổng).

🏋️ Bài tập thực hành

Tối ưu một Dockerfile thực tế

Viết Dockerfile cho 1 ứng dụng FastAPI đơn giản (từ khoá AI Engineer) theo cách "ngây thơ" (COPY toàn bộ trước, RUN pip install sau), đo thời gian build lại sau khi sửa 1 dòng code. Sau đó viết lại theo đúng thứ tự tối ưu cache, đo lại thời gian build lại. Áp dụng multi-stage nếu phù hợp, so sánh kích thước image cuối cùng bằng docker images.

📚 Tài nguyên học tập

✅ Tự đánh giá hoàn thành

  • Viết được Dockerfile đúng cú pháp cho 1 ứng dụng thực tế
  • Giải thích được cơ chế layer/cache và sắp xếp instruction đúng thứ tự tối ưu
  • Áp dụng được multi-stage build để giảm kích thước image
  • Dùng .dockerignore đúng cách