Nỗi ám ảnh mang tên “Waiting for Docker Build…”
Cảnh tượng ngồi chờ Docker build hàng chục phút chỉ để sửa một dòng log chắc chẳng còn lạ gì với anh em. Cảm giác nhìn dòng npm install hay pip install chạy rùa bò, tải đi tải lại hàng trăm MB dữ liệu cũ thực sự rất gây ức chế.
Thông thường, chúng ta hay tối ưu Dockerfile bằng cách tách riêng bước copy package.json để tận dụng Layer Caching. Tuy nhiên, cách này có một điểm yếu chí mạng. Chỉ cần bạn thêm một thư viện nhỏ, Docker sẽ coi như layer đó thay đổi và xóa sạch cache. Kết quả là hệ thống lại phải tải lại cả thế giới từ đầu.
BuildKit và tính năng Mount Cache sinh ra để giải quyết triệt để vấn đề này. Nó cho phép giữ lại folder cache của các package manager (npm, pip, go mod) qua nhiều lần build, ngay cả khi file cấu hình thay đổi. Thực tế trong dự án của mình, thời gian build giảm từ 8 phút xuống còn chưa đầy 1 phút sau khi áp dụng kỹ thuật này.
Kích hoạt BuildKit – Bật chế độ hiệu năng cao
BuildKit là bộ máy build thế hệ mới được tích hợp sẵn trong Docker. Nếu bạn dùng Docker Desktop từ bản 2.4.0, nó đã được bật mặc định. Với anh em dùng Linux hoặc phiên bản cũ, chúng ta cần khai báo rõ ràng để Docker sử dụng engine này.
Để kích hoạt nhanh cho phiên làm việc hiện tại, bạn chạy lệnh:
export DOCKER_BUILDKIT=1
Nếu muốn thiết lập vĩnh viễn, hãy thêm cấu hình vào file /etc/docker/daemon.json:
{
"features": { "buildkit": true }
}
Đừng quên restart Docker sau khi sửa file. Khi build, nếu thấy giao diện log hiển thị các stage song song và trông chuyên nghiệp hơn, tức là bạn đã thành công.
Cách cấu hình Mount Cache cho từng ngôn ngữ
Sức mạnh nằm ở flag --mount=type=cache trong lệnh RUN. Nó tạo một thư mục tạm trên máy host và gắn vào container khi build. Thư mục này được bảo tồn qua các lần build khác nhau.
1. Đối với Node.js (npm/yarn)
Với Node.js, thư mục node_modules và global cache của npm là nơi ngốn thời gian nhất. Thay vì tải mới, chúng ta sẽ mount thư mục cache của npm như sau:
# Cách làm cũ: RUN npm install
# Cách tối ưu với BuildKit:
RUN --mount=type=cache,target=/root/.npm \
npm install
Nếu dự án dùng Yarn, hãy đổi đường dẫn target:
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \
yarn install
Giờ đây, khi bạn thêm một package mới vào package.json, npm sẽ chỉ tải đúng package đó. Các thư viện cũ sẽ được lấy ngay từ cache mount, tiết kiệm hàng trăm MB băng thông.
2. Đối với Python (pip)
Mỗi lần chạy pip install -r requirements.txt là một lần chờ đợi mệt mỏi. Bạn có thể giữ lại cache của pip bằng cấu hình sau:
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
Lưu ý: Nếu bạn chạy Docker với user thường (không phải root), hãy điều chỉnh đường dẫn target theo home directory của user đó để tránh lỗi phân quyền.
3. Đối với Go (Golang)
Go có cơ chế cache cực tốt nhưng mặc định sẽ bị xóa sạch sau mỗi lần build image. Go cần hai loại cache: GOCACHE (cho kết quả compile) và GOMODCACHE (cho dependencies).
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o app main.go
Ở lần build thứ hai, tốc độ compile sẽ nhanh đến mức ngỡ ngàng vì Go chỉ xử lý những phần code thực sự thay đổi.
Mẹo kiểm tra và quản lý Cache
Khi áp dụng kỹ thuật này, mình thường quan sát kỹ log build để đảm bảo mọi thứ hoạt động đúng ý. Nếu thấy dòng CACHED xuất hiện ở các bước cài đặt hoặc thời gian thực thi giảm từ vài phút xuống vài giây, bạn đã đi đúng hướng.
Đôi khi cấu hình mount point phức tạp khiến bạn bối rối. Mình thường dùng JSON Formatter tại toolcraft.app để format lại các file manifest hoặc log JSON từ Docker. Việc này giúp mình soi kỹ các mount point và layer mà không cần cài thêm extension nặng nề.
Để dọn dẹp bộ nhớ sau một thời gian build liên tục, hãy sử dụng lệnh:
docker builder prune
Lệnh này sẽ xóa các cache cũ không còn sử dụng, giúp giải phóng dung lượng ổ cứng cho server.
Lưu ý quan trọng cho môi trường CI/CD
Mount Cache cực mạnh nhưng không phải là “viên đạn bạc”. Bạn cần lưu ý ba điểm sau:
- Bảo mật: Tuyệt đối không lưu secret hay thông tin nhạy cảm vào thư mục cache vì chúng có thể tồn tại trên host build lâu dài.
- Hệ thống CI: Trên GitHub Actions, bạn phải dùng
docker/build-push-actionkèm cấu hìnhcache-fromvàcache-toloạigha. Nếu không, mỗi lần chạy CI trên một Runner mới, Mount Cache sẽ không có dữ liệu để sử dụng. - Dung lượng: Cache có thể phình to nhanh chóng lên hàng chục GB nếu project có quá nhiều dependency. Hãy thiết lập script dọn dẹp định kỳ.
Tối ưu Docker build không chỉ giúp lập trình viên bớt stress mà còn giảm đáng kể chi phí vận hành server CI/CD. Hãy thử bật BuildKit và cấu hình Mount Cache ngay hôm nay, hiệu quả mang lại chắc chắn sẽ khiến bạn hài lòng.
