Skip to content

Thực hành: Viết Dockerfile

🎯 Mục tiêu

🎯 Sau bài thực hành này, bạn sẽ:

  • Viết Dockerfile production-ready cho Node.js và Python
  • Phân biệt COPY vs ADD, RUN chain vs RUN riêng lẻ
  • Hiểu thứ tự instruction tối ưu để tận dụng build cache
  • Sử dụng đúng EXPOSE, CMD, và ENTRYPOINT

Phần 1: Trắc nghiệm

🧠 Quiz

Câu 1: Instruction FROM trong Dockerfile có vai trò gì?

  • [ ] A) Copy file từ host vào container
  • [x] B) Chỉ định base image để build image mới
  • [ ] C) Chạy lệnh bên trong container khi build
  • [ ] D) Mở port cho container giao tiếp với bên ngoài

💡 Giải thích: FROM luôn là instruction đầu tiên trong Dockerfile. Nó xác định base image (ví dụ node:20-alpine, python:3.12-slim) — nền tảng mà image của bạn được xây dựng lên.

🧠 Quiz

Câu 2: Sự khác biệt chính giữa COPY và ADD là gì?

  • [ ] A) COPY nhanh hơn ADD trong mọi trường hợp
  • [ ] B) ADD chỉ hoạt động với file local, COPY hỗ trợ URL
  • [x] C) ADD hỗ trợ tự động giải nén tar và tải URL, COPY chỉ copy đơn thuần
  • [ ] D) Không có khác biệt — hai instruction hoàn toàn giống nhau

💡 Giải thích: COPY chỉ copy file/folder từ build context vào image. ADD có thêm tính năng tự giải nén .tar và tải file từ URL. Best practice: luôn dùng COPY trừ khi cần giải nén tar — vì COPY rõ ràng và dễ dự đoán hơn.

🧠 Quiz

Câu 3: Tại sao nên gộp nhiều lệnh RUN bằng &&?

  • [ ] A) Vì Docker không cho phép nhiều instruction RUN
  • [ ] B) Vì && giúp lệnh chạy song song, tăng tốc build
  • [x] C) Vì mỗi RUN tạo một layer mới — gộp lại giảm số layer và kích thước image
  • [ ] D) Vì && bắt buộc khi dùng Alpine Linux

💡 Giải thích: Mỗi instruction RUN tạo một layer trong image. Gộp apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* thành một RUN giúp giảm layer, giảm kích thước image, và tránh cache cũ của package manager.

Phần 2: Sắp xếp thứ tự Dockerfile

🧩 Parsons Problem

Bài 1: Dockerfile cho Node.js API

Sắp xếp các instruction theo thứ tự tối ưu cho build cache:

  1. FROM node:20-alpine
  2. WORKDIR /app
  3. COPY package.json package-lock.json ./
  4. RUN npm ci --only=production
  5. COPY . .
  6. EXPOSE 3000
  7. CMD ["node", "server.js"]

🧩 Parsons Problem

Bài 2: Dockerfile cho Python Flask App

Sắp xếp các instruction đúng thứ tự:

  1. FROM python:3.12-slim
  2. WORKDIR /app
  3. COPY requirements.txt .
  4. RUN pip install --no-cache-dir -r requirements.txt
  5. COPY . .
  6. EXPOSE 5000
  7. CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]

Phần 3: Tìm lỗi trong Dockerfile

🐛 Spot-the-Bug

Dockerfile bị lỗi — Node.js App

dockerfile
FROM node:20-alpine
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "server.js"]

Câu hỏi: Dockerfile trên có 2 vấn đề. Bạn tìm được không?

Đáp án:

  1. Thiếu WORKDIR — file nằm ở root /, gây xung đột. Thêm WORKDIR /app sau FROM.
  2. COPY all trước install — mỗi thay đổi file buộc Docker chạy lại npm install. Nên COPY package.json trước, install, rồi COPY source.

🐛 Spot-the-Bug

Dockerfile bị lỗi — Python App

dockerfile
FROM python:3.12
WORKDIR /app
COPY ../src ./src
COPY requirements.txt .
RUN pip install -r requirements.txt
CMD ["python", "src/main.py"]

Câu hỏi: Tìm lỗi trong Dockerfile trên.

Đáp án: COPY ../src ./src — Docker không cho phép COPY file nằm ngoài build context. Path ../src sẽ gây lỗi build. Cần điều chỉnh build context hoặc đặt lại vị trí Dockerfile.