Hiểu Docker Build Cache và cách sắp xếp lệnh để tăng tốc build Image
Hướng dẫn Docker Build Cache: cache key, invalidation, thứ tự COPY/RUN, .dockerignore, BuildKit cache mounts, external cache trong CI và cách debug cache miss.
ShanverseCập nhật 9 phút đọc— lượt xemĐang tải lượt xem
Docker
Caching
Build Cache là gì?
Mỗi instruction trong Dockerfile tạo ra một kết quả mà builder có thể tái sử dụng ở lần build sau. Khi cache hit, Docker không chạy lại bước đó.
Nếu source code đổi nhưng lockfile không đổi, một Dockerfile được sắp xếp tốt có thể tái sử dụng bước cài dependencies vốn tốn nhiều thời gian.
Mermaid
Loading diagram…
Cache key được tính từ đâu?
Builder đánh giá instruction cùng trạng thái đầu vào của nó. Tùy loại lệnh, cache key có thể phụ thuộc vào:
Instruction và arguments.
Kết quả/layer của bước trước.
Base Image hoặc stage nguồn.
Nội dung và metadata của file trong
Bình luận
COPY
/
ADD
.
Build arguments và environment liên quan.
Mount configuration của BuildKit.
Platform và target stage.
Điều quan trọng: cache tạo thành một chuỗi. Khi một bước cache miss, các bước phía sau thường phải được đánh giá hoặc chạy lại.
Quy tắc vàng: từ ít đổi đến hay đổi
Một Dockerfile chưa tối ưu:
Chỉ cần sửa một file README hoặc source, COPY . . thay đổi và npm ci phải chạy lại.
Phiên bản tốt hơn:
Thứ tự:
Base Image.
Cấu hình hệ thống ít đổi.
Dependency manifests/lockfile.
Cài dependencies.
Configuration build.
Source code.
Build artifact.
COPY là ranh giới cache quan trọng
Docker tính checksum cho file được copy. Vì vậy hãy chia COPY theo mức độ thay đổi:
Không nên copy toàn repository quá sớm:
Các file không liên quan như .git, log, coverage hoặc local build output có thể làm context lớn và gây cache miss.
.dockerignore giúp cache ổn định
Lợi ích:
Build context nhỏ hơn.
Ít file không liên quan tác động đến COPY.
Giảm nguy cơ gửi secret vào builder.
Tránh copy node_modules từ host.
Chỉ ignore README nếu Image/build thật sự không dùng nó. Một số tool tạo metadata hoặc package từ README.
Cache của RUN không biết package ngoài Internet đã đổi
Với instruction:
Builder chủ yếu dựa vào instruction và input cache, không tự truy vấn repository để quyết định package đã có version mới hay chưa. Một cache hit có thể giữ package cũ.
Để refresh có chủ đích:
Hoặc thay đổi version/package input theo quy trình cập nhật dependency.
Đừng vô hiệu toàn bộ cache ở mọi build chỉ vì một package. Cache cần đi cùng chiến lược update định kỳ và vulnerability scanning.
Cài OS packages trong cùng một layer
Tốt:
Không nên:
Tách update và install có thể tạo cache không mong muốn. Cleanup trong cùng RUN cũng tránh giữ package index ở layer trước.
BuildKit cache mount
Cache mount giữ package cache giữa các build nhưng không copy cache đó vào Image cuối.
npm
pnpm
pip
apt
sharing=locked hữu ích khi package manager không an toàn với nhiều writer đồng thời.
Bind mount trong bước build
BuildKit có thể mount source tạm thời để chạy một bước mà không đưa toàn bộ input vào layer:
Kỹ thuật này hữu ích cho workflow nâng cao, nhưng COPY rõ ràng thường dễ đọc hơn. Ưu tiên khả năng maintain trước khi tối ưu cực đoan.
Secret mount và cache
Không dùng:
Token có thể xuất hiện trong metadata, layer hoặc history. Dùng:
Thay đổi nội dung secret không nên được xem là cơ chế cache-busting đáng tin cậy. Nếu output phải được tạo lại khi credential/source thay đổi, dùng input cache-bust không nhạy cảm hoặc version dependency rõ ràng.
Multi-stage Build và cache
Các stage giúp:
Cache dependency độc lập.
Chỉ rebuild stage bị ảnh hưởng.
Không đưa build cache/dev tools vào final Image.
Tạo target riêng cho test, development và production.
Monorepo: tránh copy cả workspace
Trong monorepo, thay đổi package không liên quan có thể làm mất cache. Hãy copy manifests tối thiểu cần cho workspace mục tiêu:
Cấu trúc thực tế phụ thuộc npm, pnpm, Yarn và build tool. Lockfile root vẫn thường là input quan trọng.
Cache trong CI: vì sao local nhanh nhưng pipeline chậm?
Runner CI thường là máy mới nên không có cache local. Cần export/import cache.
Ví dụ registry cache với Buildx:
Nguyên tắc:
Cache ref tách khỏi production tag.
Chỉ đọc cache từ nguồn tin cậy.
Thiết lập quyền Registry phù hợp.
Cân bằng thời gian upload cache với thời gian tiết kiệm.
Cache multi-platform có thể khác theo architecture.
Inline cache
Một số pipeline nhúng cache metadata vào Image và dùng Image trước làm nguồn cache. Registry cache riêng với mode=max thường giữ được nhiều intermediate stage hơn. Chọn theo builder, Registry và chi phí lưu trữ.
Khi nào dùng --no-cache?
Dùng khi:
Chẩn đoán cache có che lỗi hay không.
Thực hiện clean rebuild định kỳ.
Cần refresh dependency không được pin đầy đủ.
Điều tra supply-chain hoặc cache bất thường.
Không nên dùng mặc định cho mọi build vì bỏ toàn bộ lợi ích cache.
Vẫn có thể kéo base mới:
--pull và --no-cache giải quyết hai vấn đề khác nhau: base Image freshness và instruction cache.
Debug cache hit/miss
Hiển thị log rõ:
Không dùng cache để so sánh:
Kiểm tra disk/cache:
Dọn builder cache:
Prune là thao tác xóa. Đọc dung lượng và xác nhận trước, đặc biệt trên shared runner hoặc máy production.
Tại sao cache miss ngoài dự kiến?
Checklist:
Base Image/digest có đổi không?
Build context có thêm file generated không?
.dockerignore có thiếu .git, log hoặc output không?
Lockfile có bị format/rewrite không?
Build ARG/ENV có thay đổi không?
CI có dùng builder mới mà không import cache không?
Target platform có đổi không?
Stage name/source của COPY --from có đổi không?
Có timestamp hoặc version động trong bước sớm không?
Cache source có quyền đọc và tồn tại không?
Những anti-pattern phổ biến
COPY . . ngay đầu Dockerfile
Mọi thay đổi làm mất cache các bước sau.
Dùng timestamp để “đảm bảo build mới”
Timestamp động phá cache toàn bộ downstream và làm build khó tái hiện.
Không pin dependency
Build nhanh nhờ cache nhưng clean build lại cho kết quả khác.
Chia quá nhiều RUN không có mục đích
Nhiều layer không tự động làm cache tốt hơn. Tách theo ranh giới thay đổi hợp lý.
Gộp mọi thứ vào một RUN
Một thay đổi nhỏ khiến toàn bộ chuỗi dài phải chạy lại. Cân bằng atomicity và cache boundaries.
Tin cache từ nguồn không kiểm soát
External cache là input của supply chain. Giới hạn quyền và nguồn cache.
Dockerfile mẫu tối ưu cache
Checklist tăng tốc build
Có .dockerignore phù hợp.
Dependency manifests được copy trước source.
Lockfile được dùng ở chế độ frozen.
Instruction xếp từ ổn định đến hay thay đổi.
Package cache dùng BuildKit cache mount.
OS package update/install nằm cùng bước hợp lý.
Multi-stage Build tách deps, test, builder và runner.
CI import/export external cache.
Cache đến từ Registry/nguồn tin cậy.
Build vẫn đúng khi cache rỗng.
Có clean rebuild định kỳ để kiểm tra tính tái hiện.
Cache và disk usage được theo dõi/prune có kiểm soát.
Tổng kết
Docker Build Cache hiệu quả nhất khi Dockerfile phản ánh tần suất thay đổi của input:
Mục lục(31)
Codeplain text
1FROM → WORKDIR → COPY lockfile → RUN install → COPY source → RUN build
Codeplain text
1Step 1: HIT2Step 2: HIT3Step 3: MISS ← đầu vào thay đổi4Step 4: rebuild5Step 5: rebuild
Hướng dẫn Docker Hardening thực tế với non-root user, multi-stage builds, minimal image, read-only filesystem, Linux capabilities, seccomp, scanning và supply-chain security.
Docker Compose khi nào không còn đủ? So sánh Kubernetes và Docker Swarm về multi-host, self-healing, scaling, rolling update, networking, secrets và lộ trình migration production.
Hướng dẫn tự động deploy ứng dụng Docker lên VPS Ubuntu và cloud providers bằng GitHub Actions: AWS ECS/App Runner/EC2, Vercel, Render, secrets, healthcheck, rollback và zero-downtime strategy.