Skip to content

Layer Caching & UnionFS — Tại Sao Thứ Tự Dòng Lệnh Quan Trọng

🎓 Instructor Profile

Bài giảng được biên soạn bởi sự hợp tác giữa Giáo sư Tom (MIT) — chuyên gia về kiến trúc hệ thống phân tán, và Kỹ sư Raizo (Phó CTO HPN) — người đã tối ưu pipeline CI/CD cho hàng trăm dự án production.

Chào mừng các kỹ sư quay lại với Docker Masterclass Phase 1. Ở bài trước, chúng ta đã mổ xẻ cấu trúc Dockerfile. Hôm nay, chúng ta sẽ trả lời một câu hỏi mà 90% người mới bỏ qua: Tại sao chỉ đổi thứ tự hai dòng lệnh mà thời gian build giảm từ 12 phút xuống 2 phút?

Câu trả lời nằm ở Layer CachingUnionFS — hai cơ chế nền tảng biến Docker thành công cụ build nhanh nhất thế giới container.

🎯 Mục tiêu

Sau bài này, bạn sẽ:

  1. Hiểu mô hình UnionFS/OverlayFS ở mức trực giác — không cần đào sâu kernel
  2. Nắm vững quy tắc cache invalidation của Docker — tại sao một thay đổi nhỏ phá hủy toàn bộ cache phía dưới
  3. Biết cách sắp xếp Dockerfile để tận dụng cache tối đa — giảm thời gian build từ phút xuống giây
  4. Sử dụng docker history để kiểm tra layer và kích thước từng lớp
  5. Áp dụng BuildKit mount cache cho package manager — kỹ thuật nâng cao mà ít người biết

📚 Phần 1: UnionFS/OverlayFS — Image Là Chồng Giấy Trong

1.1 Mô hình trực giác: Chồng giấy trong suốt

Hãy tưởng tượng bạn là một họa sĩ hoạt hình. Để tạo một khung hình, bạn không vẽ lại toàn bộ — bạn dùng nhiều tấm giấy trong suốt xếp chồng lên nhau. Tấm dưới cùng là phong cảnh nền (không đổi), tấm giữa là đồ vật cố định, tấm trên cùng là nhân vật di chuyển (đổi mỗi khung hình).

Docker Image hoạt động y hệt. Mỗi instruction trong Dockerfile tạo ra một layer, xếp chồng lên nhau thành image hoàn chỉnh.

1.2 Sơ đồ Layer Stack

┌─────────────────────────────────────────────────┐
│          🔓 WRITABLE CONTAINER LAYER            │  ← Container thêm vào
│   (Logs, temp files, runtime data)              │     khi chạy (read-write)
├─────────────────────────────────────────────────┤
│  📄 Layer 4: COPY . .                           │  ← App source code
│   (index.js, routes/, controllers/)             │     ~2 MB
├─────────────────────────────────────────────────┤
│  📦 Layer 3: RUN npm ci                         │  ← Dependencies
│   (node_modules/ — 150MB+)                      │     ~180 MB
├─────────────────────────────────────────────────┤
│  📋 Layer 2: COPY package*.json ./              │  ← Manifest files
│   (package.json, package-lock.json)             │     ~5 KB
├─────────────────────────────────────────────────┤
│  🐧 Layer 1: FROM node:20-alpine               │  ← Base OS + Node.js
│   (Alpine Linux, Node.js runtime)               │     ~180 MB
├─────────────────────────────────────────────────┤
│  ░░░░░░░░░░ BOOTFS (kernel) ░░░░░░░░░░░░░░░░░░ │  ← Shared với host
└─────────────────────────────────────────────────┘

     ▲ Đọc từ trên xuống: Layer trên "che" layer dưới
     ▲ Tất cả layer (trừ container layer) là READ-ONLY

1.3 Quy tắc vàng của UnionFS

Quy tắcGiải thích
🔒 Layer = Read-OnlySau khi build xong, không layer nào bị thay đổi
📝 Container Layer = Read-WriteKhi docker run, Docker thêm một layer ghi được lên trên cùng
📋 Copy-on-Write (CoW)Muốn sửa file ở layer dưới? File được sao chép lên container layer rồi mới sửa
🔍 Tìm từ trên xuốngKhi đọc file, Docker tìm từ layer trên cùng xuống — gặp đầu tiên thì dùng
♻️ Chia sẻ layerNhiều container cùng image dùng chung các read-only layer — tiết kiệm disk

1.4 Copy-on-Write trong thực tế

Khi container cần sửa file /etc/nginx/nginx.conf ở Layer 1, Docker không sửa Layer 1. Thay vào đó, file được copy lên Container Layer rồi sửa ở đó. Layer gốc giữ nguyên — các container khác dùng cùng image vẫn thấy bản gốc.

1.5 Xem layer bằng docker history

bash
# Xem tất cả layer của một image
docker history node:20-alpine

# Output chi tiết hơn (không cắt ngắn)
docker history --no-trunc --format "table {{.CreatedBy}}\t{{.Size}}" myapp:latest

Output mẫu:

CREATED BY                                      SIZE
COPY . .                                        2.1MB
RUN /bin/sh -c npm ci --omit=dev                178MB
COPY package*.json ./                           5.12kB
/bin/sh -c #(nop)  CMD ["node"]                 0B
/bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
...
/bin/sh -c addgroup -g 1000 node...             181MB

💡 Mẹo đọc docker history

  • Layer 0B = metadata (CMD, ENV, EXPOSE) — không tạo filesystem layer thực sự
  • Layer lớn nhất thường là RUN npm ci hoặc RUN apt-get install — đây là nơi cần tối ưu
  • Đọc từ dưới lên để thấy thứ tự build thực tế

Phần 2: Tại Sao Thứ Tự Dòng Lệnh Quan Trọng

2.1 Quy tắc cache của Docker

Đây là quy tắc quan trọng nhất trong toàn bộ bài học:

Nếu một layer thay đổi, TẤT CẢ các layer phía sau nó bị invalidate (xóa cache).

Hãy hình dung cache như một chuỗi domino:

Layer 1 ✅ → Layer 2 ✅ → Layer 3 ❌ thay đổi! → Layer 4 💥 → Layer 5 💥

                          Từ đây trở đi,
                          tất cả phải build lại

Docker kiểm tra cache theo từng bước: FROM (base image đổi?), COPY/ADD (checksum file nguồn khác?), RUN (chuỗi lệnh giống hệt?). Nếu bước N cache miss → bước N+1, N+2, ... tất cả phải chạy lại.

2.2 Ví dụ SAI — Dockerfile "phá cache"

dockerfile
# ❌ BAD: Mỗi khi sửa một dòng code → npm ci chạy lại!
FROM node:20-alpine
WORKDIR /app

COPY . .                    # ← Bất kỳ file nào thay đổi = cache miss
RUN npm ci --omit=dev       # ← Phải cài lại 180MB dependencies mỗi lần!
RUN npm run build           # ← Phải build lại!

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

Điều gì xảy ra khi bạn chỉ sửa src/index.js?

bash
$ docker build -t myapp:bad .
# Step 1/7 : FROM node:20-alpine  → Using cache ✅       0.0s
# Step 2/7 : WORKDIR /app         → Using cache ✅       0.0s
# Step 3/7 : COPY . .             → ❌ CACHE MISS!       0.8s  (src/index.js thay đổi)
# Step 4/7 : RUN npm ci           → ❌ CHẠY LẠI!       38.2s  (tải lại 180MB deps)
# Step 5/7 : RUN npm run build    → ❌ CHẠY LẠI!        6.1s
# Total: ~45s 🐌

Kết quả: 45 giây mỗi lần build — chỉ vì sửa một dòng code!

2.3 Ví dụ ĐÚNG — Dockerfile "thân thiện với cache"

dockerfile
# ✅ GOOD: Tách dependency layer khỏi source code layer
FROM node:20-alpine
WORKDIR /app

# Bước 1: Copy CHỈ file manifest (thay đổi rất hiếm)
COPY package.json package-lock.json ./

# Bước 2: Cài dependencies (cached khi package.json không đổi!)
RUN npm ci --omit=dev

# Bước 3: Copy source code (thay đổi thường xuyên nhất → đặt cuối)
COPY . .

# Bước 4: Build
RUN npm run build

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

Cùng tình huống: sửa src/index.js, build lại:

bash
$ docker build -t myapp:good .
# Step 1/8 : FROM node:20-alpine             → Using cache ✅    0.0s
# Step 2/8 : WORKDIR /app                    → Using cache ✅    0.0s
# Step 3/8 : COPY package.json package-lock.json ./
#                                             → Using cache ✅    0.0s  (package.json không đổi!)
# Step 4/8 : RUN npm ci --omit=dev           → Using cache ✅    0.0s  (🎉 SKIP 180MB!)
# Step 5/8 : COPY . .                        → ❌ CACHE MISS     0.8s  (source thay đổi)
# Step 6/8 : RUN npm run build               → ❌ CHẠY LẠI      4.2s
# Total: ~5s 🚀

Kết quả: 5 giây! Nhanh hơn 9 lần so với cách sai.

2.4 Nguyên tắc vàng: Sắp xếp theo tần suất thay đổi

  Ít thay đổi  ────────────────────────────►  Thay đổi nhiều

  FROM base-image       (vài tháng/lần)
  RUN apt-get install   (khi thêm system deps)
  COPY package*.json    (khi thêm/xóa npm package)
  RUN npm ci            (khi package.json đổi)
  COPY . .              (MỖI LẦN sửa code)
  RUN npm run build     (khi source code đổi)
  CMD / ENTRYPOINT      (gần như không bao giờ)

🧪 Phần 3: Lab — Trải Nghiệm Cache Thực Tế

Lab 3.1: Chứng minh bad order phá cache

Bước 1: Tạo project mẫu

bash
mkdir cache-lab && cd cache-lab

cat > package.json << 'EOF'
{
  "name": "cache-lab",
  "version": "1.0.0",
  "scripts": { "start": "node index.js" },
  "dependencies": { "express": "^4.18.2" }
}
EOF

cat > index.js << 'EOF'
const express = require("express");
const app = express();
app.get("/", (req, res) => res.send("Hello Docker Cache!"));
app.listen(3000, () => console.log("Server running on :3000"));
EOF

Bước 2: Tạo 2 Dockerfile

dockerfile
# Dockerfile.bad
FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install --omit=dev
EXPOSE 3000
CMD ["node", "index.js"]
dockerfile
# Dockerfile.good
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]

Bước 3: Build và so sánh

bash
# Build lần 1 cho cả 2 (cold build — thời gian tương đương)
time docker build -f Dockerfile.bad -t lab:bad .
time docker build -f Dockerfile.good -t lab:good .

# Sửa một dòng code
sed -i 's/Hello Docker Cache!/Hello v2!/' index.js

# Build lần 2 — quan sát sự khác biệt
time docker build -f Dockerfile.bad -t lab:bad .
# → npm install chạy LẠI dù dependencies không đổi → ~30-45s 🐌

time docker build -f Dockerfile.good -t lab:good .
# → "Using cache" cho npm install → ~3-5s 🚀

Lab 3.2: Phân tích layer bằng docker history

bash
# So sánh layer giữa 2 images
echo "=== BAD ORDER ==="
docker history lab:bad --format "table {{.CreatedBy}}\t{{.Size}}" --no-trunc

echo ""
echo "=== GOOD ORDER ==="
docker history lab:good --format "table {{.CreatedBy}}\t{{.Size}}" --no-trunc

Quan sát kết quả:good order, layer COPY . . chỉ chứa source code (~850B) vì package.json đã nằm ở layer riêng phía dưới. Khi sửa code, chỉ layer 850B cần rebuild — không phải layer 12.3MB dependencies.

💡 Mẹo

Đọc docker history từ dưới lên để thấy thứ tự build thực tế. Layer 0B = metadata (CMD, ENV), không tạo filesystem layer thực sự.


🏋️ Phần 4: Bài Tập Nhanh — Sắp Xếp Dockerfile

Bài tập: Sắp xếp lại các instruction sau để tối ưu cache

Cho Dockerfile sau (thứ tự ngẫu nhiên), hãy sắp xếp lại để tận dụng cache tối đa:

dockerfile
# Các instruction cần sắp xếp (CHƯA đúng thứ tự):
RUN pip install -r requirements.txt
COPY . /app
CMD ["python", "main.py"]
FROM python:3.12-slim
COPY requirements.txt /app/
WORKDIR /app
RUN python -m pytest tests/ --tb=short
🔑 Xem đáp án
dockerfile
# ✅ Đáp án tối ưu:
FROM python:3.12-slim              # 1. Base image (ít thay đổi nhất)
WORKDIR /app                       # 2. Working directory (gần như không đổi)
COPY requirements.txt /app/        # 3. CHỈ file manifest (đổi khi thêm deps)
RUN pip install -r requirements.txt # 4. Cài deps (cached khi requirements.txt không đổi!)
COPY . /app                        # 5. Source code (thay đổi thường xuyên → cuối)
RUN python -m pytest tests/ --tb=short  # 6. Test (chạy mỗi lần code đổi)
CMD ["python", "main.py"]          # 7. Startup command (metadata, 0B)

Giải thích: requirements.txt tách riêng trước COPY . /app để khi sửa code Python, bước pip install được cache — tương tự chiến lược package.json ở Node.js.

Thử thách thêm: Go project

Sắp xếp lại các instruction sau:

dockerfile
RUN go build -o /app/server .
COPY . .
CMD ["/app/server"]
FROM golang:1.22-alpine
COPY go.mod go.sum ./
RUN go mod download
WORKDIR /app
🔑 Xem đáp án
dockerfile
FROM golang:1.22-alpine
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o /app/server .
CMD ["/app/server"]

⚠️ Phần 5: Gotcha — Những Cái Bẫy Thực Tế

⚠️ Cạm bẫy

dockerfile
# ❌ NGUY HIỂM: Layer update bị cache, install dùng index cũ!
RUN apt-get update
RUN apt-get install -y curl wget

# 6 tháng sau, bạn thêm package mới:
RUN apt-get install -y nginx    # ← Dùng apt index CŨ từ 6 tháng trước!
                                 #    Package version cũ, có thể không tồn tại!
dockerfile
# ✅ ĐÚNG: Gộp update + install cùng một layer
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
      curl \
      wget \
      nginx && \
    rm -rf /var/lib/apt/lists/*   # Dọn dẹp cache apt để giảm size

Tại sao? Khi Docker cache layer apt-get update, nó giữ nguyên package index cũ. Lần build sau, apt-get install ở layer mới sẽ dùng index đã lỗi thời — dẫn đến lỗi "package not found" hoặc cài version có lỗ hổng bảo mật.

⚠️ Cạm bẫy

Không có .dockerignore → Docker gửi toàn bộ vào build context (bao gồm node_modules, .git, logs). Điều này khiến COPY . . luôn cache miss vì .git thay đổi mỗi commit.

# .dockerignore tối thiểu cho Node.js
node_modules
.git
*.log
.env
dist
coverage

⚠️ Cạm bẫy

bash
docker build --no-cache -t myapp .   # ❌ Tự phá vũ khí mạnh nhất!
docker build -t myapp .              # ✅ Để Docker tự quyết định cache

Chỉ dùng --no-cache khi thực sự cần (ví dụ: base image bị compromised).


🚀 Phần 6: Performance Nâng Cao — BuildKit Mount Cache

🚀 Performance: Sử dụng --mount=type=cache cho Package Manager

BuildKit (Docker 18.09+) hỗ trợ mount cache — giữ lại cache package manager giữa các lần build, kể cả khi layer bị invalidate.

dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./

# Mount cache npm — giữ ~/.npm cache giữa các lần build
RUN --mount=type=cache,target=/root/.npm \
    npm ci --omit=dev

COPY . .
CMD ["node", "index.js"]

Python tương tự: --mount=type=cache,target=/root/.cache/pipGo tương tự: --mount=type=cache,target=/go/pkg/mod

bash
# Bật BuildKit
export DOCKER_BUILDKIT=1
# Hoặc dùng buildx (mặc định đã bật)
docker buildx build -t myapp .

Kỹ thuật này hiệu quả khi package.json thay đổi — npm chỉ tải packages mới thêm thay vì tải lại toàn bộ.


🛑 Phần 7: Anti-Pattern Nghiêm Trọng

🛑 Anti-pattern: COPY . . là instruction đầu tiên

dockerfile
# ❌ ANTI-PATTERN NGHIÊM TRỌNG NHẤT
FROM node:20-alpine
WORKDIR /app
COPY . .                    # ← MỌI thay đổi = MỌI layer sau bị invalidate
RUN npm ci                  # ← Cài lại 180MB mỗi lần
RUN npm run build           # ← Compile lại mỗi lần

Hậu quả CI/CD:

MetricCOPY . . đầu tiênĐúng thứ tự
Build time~3-5 phút~15-30 giây
50 builds/ngày250 phút25 phút
Chi phí CI/tháng~$150-300~$15-30

Câu chuyện thực tế: Một team 8 người dùng COPY . . đầu tiên — pipeline CI tốn 12 phút mỗi push. Sau khi chỉ sắp xếp lại Dockerfile (không thay đổi gì khác), pipeline giảm xuống 2 phút. Tiết kiệm $200/tháng và hàng giờ chờ đợi.


🔬 Phần 8: Under The Hood — OverlayFS Bên Trong

8.1 Nơi lưu trữ layer

Trên Linux, OverlayFS lưu layer tại /var/lib/docker/overlay2/:

/var/lib/docker/overlay2/
├── abc123.../
│   ├── diff/           # ← File MỚI/THAY ĐỔI trong layer này
│   ├── link            # ID rút gọn
│   ├── lower           # Danh sách layer phía dưới
│   └── work/           # Thư mục tạm cho atomic operations
├── def456.../
│   ├── diff/           # File mới của layer này
│   └── merged/         # ← UNIFIED VIEW — filesystem mà container "nhìn thấy"

Cách hoạt động: OverlayFS gộp upperdir (writable) + các lowerdir (read-only) thành merged/ — đây là filesystem thống nhất mà container sử dụng.

8.2 Content-Addressable Storage

Mỗi layer được định danh bằng SHA256 hash của nội dung:

bash
docker inspect myapp:latest --format '{{json .RootFS.Layers}}' | python3 -m json.tool
# [
#     "sha256:a1b2c3d4e5f6...",   # Base layer
#     "sha256:f6e5d4c3b2a1...",   # COPY package.json
#     "sha256:1a2b3c4d5e6f...",   # RUN npm ci
#     "sha256:6f5e4d3c2b1a..."    # COPY . .
# ]

Ý nghĩa thực tế:

  • Hai image có cùng layer → Docker chỉ lưu 1 bản trên disk
  • Push/pull registry: chỉ truyền layer chưa tồn tại → tiết kiệm bandwidth

8.3 Kiểm tra dung lượng

bash
docker system df              # Tổng quan dung lượng Docker
docker system df -v           # Chi tiết từng image
docker builder prune          # Xóa build cache
docker system prune -a        # Xóa TẤT CẢ unused (cẩn thận!)

🐛 Phần 9: Spot the Bug

Tìm 3 lỗi trong Dockerfile sau:

dockerfile
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update
RUN apt-get install -y gcc libpq-dev
COPY . .
RUN pip install -r requirements.txt
COPY config/ /app/config/
EXPOSE 8000
CMD ["gunicorn", "app:create_app()", "--bind", "0.0.0.0:8000"]
🔍 Xem đáp án

Lỗi 1: apt-get updateapt-get install tách 2 layer → layer update bị cache, install dùng index cũ. Lỗi 2: COPY . . trước pip install → mọi thay đổi code khiến pip install chạy lại. Lỗi 3: COPY config/ thừa — COPY . . đã copy rồi, tạo layer không cần thiết.

Dockerfile đã sửa:

dockerfile
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && \
    apt-get install -y --no-install-recommends gcc libpq-dev && \
    rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "app:create_app()", "--bind", "0.0.0.0:8000"]

🎯 Phần 10: Scenario — Quyết Định Kiến Trúc

Tình huống: Monorepo với nhiều services

Team bạn có monorepo chứa 3 microservices (api/ Node.js, worker/ Python, gateway/ Go). Mỗi lần sửa code ở services/api/, cả 3 Dockerfiles đều rebuild vì dùng COPY . . từ root. Bạn chọn giải pháp nào?

A) Dùng .dockerignore riêng cho mỗi service B) Dùng COPY có chọn lọc thay vì COPY . .C) Tách thành 3 repo riêng

🔑 Phân tích đáp án

Đáp án tốt nhất: BCOPY có chọn lọc

dockerfile
# Dockerfile.api — chỉ copy những gì cần
FROM node:20-alpine
WORKDIR /app
COPY shared/proto/ ./shared/proto/       # Shared code (ít thay đổi)
COPY services/api/package*.json ./       # Dependencies
RUN npm ci --omit=dev
COPY services/api/ ./                    # Source code
CMD ["node", "index.js"]

Tại sao không A? Docker chỉ hỗ trợ một .dockerignore tại root (trừ BuildKit). Tại sao không C? Tách repo tạo overhead quản lý — overkill cho vấn đề này.


📝 Phần 11: Quiz Kiểm Tra

🧠 Quiz

Câu 1: Điều gì xảy ra khi Layer 3 trong Dockerfile thay đổi?

  • [ ] Chỉ Layer 3 bị rebuild, các layer khác giữ nguyên
  • [x] Layer 3 và TẤT CẢ các layer sau nó (4, 5, 6...) bị rebuild
  • [ ] Tất cả layer (1, 2, 3, 4, 5...) đều bị rebuild
  • [ ] Docker tự động tối ưu, chỉ rebuild layer nào cần

💡 Giải thích: Đây là quy tắc cache invalidation cốt lõi: khi một layer thay đổi, tất cả layer phía sau (downstream) bị invalidate. Layer 1 và 2 vẫn giữ cache vì chúng nằm trước layer thay đổi.

🧠 Quiz

Câu 2: Tại sao COPY package.json nên đặt TRƯỚC COPY . .?

  • [ ] Vì Docker yêu cầu copy file nhỏ trước file lớn
  • [ ] Vì package.json cần có trước khi copy source code
  • [x] Vì package.json ít thay đổi hơn source code, giúp cache layer npm ci phía sau
  • [ ] Không có khác biệt, chỉ là convention

💡 Giải thích: Khi đặt COPY package.json trước, layer RUN npm ci chỉ bị invalidate khi package.json thay đổi (hiếm khi). Nếu dùng COPY . . trước, bất kỳ file nào thay đổi đều khiến npm ci phải chạy lại.

🧠 Quiz

Câu 3: Container layer có đặc điểm gì khác so với image layer?

  • [ ] Container layer lớn hơn image layer
  • [ ] Container layer được chia sẻ giữa các container
  • [x] Container layer là read-write, trong khi image layer là read-only
  • [ ] Container layer được lưu trữ ở vị trí khác trên disk

💡 Giải thích: Tất cả image layer đều read-only sau khi build. Khi docker run tạo container, Docker thêm một writable layer (container layer) lên trên cùng. Mọi thay đổi runtime (log, temp files, database writes) đều ghi vào layer này qua cơ chế Copy-on-Write.

🧠 Quiz

Câu 4: --mount=type=cache trong BuildKit giải quyết vấn đề gì?

  • [ ] Cache toàn bộ Dockerfile để không cần build lại
  • [ ] Cache base image trên local machine
  • [x] Giữ lại cache của package manager (npm, pip) giữa các lần build, kể cả khi layer bị invalidate
  • [ ] Mount volume từ host vào container khi build

💡 Giải thích: Khi package.json thay đổi, layer npm ci phải chạy lại. Nhưng với --mount=type=cache, thư mục ~/.npm được giữ lại — npm chỉ tải packages mới thêm thay vì tải lại toàn bộ. Đây là tính năng BuildKit, không có trong builder cũ.


Phần 12: Checklist Tối Ưu Cache

✅ Checklist triển khai

  • [ ] Sắp xếp Dockerfile theo tần suất thay đổi (ít → nhiều)
  • [ ] Tách file manifest (package.json, requirements.txt, go.mod) trước COPY . .
  • [ ] Gộp apt-get update + install cùng một RUN instruction
  • [ ] Tạo .dockerignore loại bỏ node_modules, .git, logs, test coverage
  • [ ] Dọn dẹp trong cùng layer: rm -rf /var/lib/apt/lists/* sau apt-get install
  • [ ] Bật BuildKit (DOCKER_BUILDKIT=1) cho mount cache và parallel builds
  • [ ] Kiểm tra layer bằng docker history — layer nào lớn nhất? Có thể tối ưu?
  • [ ] Đo thời gian build trước/sau khi tối ưu — ghi nhận kết quả
  • [ ] CI/CD pipeline dùng --cache-from để share cache giữa các lần build trên CI

🔗 Phần 13: Tài Nguyên Liên Quan


🎓 Lời Kết

Hôm nay bạn đã học kỹ năng có ROI cao nhất trong Docker: tối ưu layer caching.

📌 Instruction ít thay đổi → đặt trước. Thay đổi nhiều → đặt sau.

📌 Tách file manifest ra khỏi COPY . . — chiến lược cache quan trọng nhất.

📌 Mỗi RUN tạo một layer. Gộp lệnh liên quan bằng &&.

Bài tiếp theo, chúng ta sẽ học Multi-stage Build — kỹ thuật giảm image size từ 1GB xuống dưới 100MB. Hẹn gặp lại! 🚀