Dockerfile chỉ có COPY . . nhưng image lại chứa cả file cấu hình local và thư mục dependency. Vấn đề thường bắt đầu từ việc hiểu nhầm dấu chấm: Docker sao chép từ build context, không tự giới hạn vào các file đã được Git theo dõi.
Bài này dùng một fixture nhỏ để bạn kiểm tra danh sách file. Đây là Dockerfile phục vụ chẩn đoán, không phải image chạy ứng dụng. Tài liệu Docker được đọc ngày 24/10/2026; môi trường biên tập không có Docker nên các lệnh build chưa được chạy. Không có số đo kích thước hoặc thời gian build trong bài.
Điều kiện cần có
Bạn cần Docker Engine đang hoạt động và Buildx hỗ trợ local exporter. Ghi lại docker version và docker buildx version khi thử. Tạo thư mục mới chỉ chứa dữ liệu giả; đừng bắt đầu bằng một repository có credential thật.
1. Xác định chính xác context
Với docker buildx build -f docker/App.Dockerfile ., đối số cuối là context của lệnh. Vị trí Dockerfile và vị trí gốc context có thể khác nhau. Tài liệu build context mô tả cách builder nhận file và tìm .dockerignore tại gốc context. File ignore riêng theo tên Dockerfile có thể được ưu tiên nếu dự án sử dụng cơ chế đó.
Để dễ quan sát, ví dụ chỉ dùng một Dockerfile ở thư mục gốc. Tạo dữ liệu thử:
mkdir docker-context-demo
cd docker-context-demo
mkdir -p src node_modules/fake-package
printf '%s\n' 'demo source' > src/app.txt
printf '%s\n' 'DEMO_ONLY=1' > .env
printf '%s\n' 'DEMO_ONLY=replace-me' > .env.example
printf '%s\n' 'not a real dependency' > node_modules/fake-package/index.txt
Các chuỗi trên là dữ liệu giả phục vụ kiểm tra. .env.example chỉ nên có tên biến và giá trị mẫu được phép chia sẻ. Đặt tên “example” không làm một file chứa bí mật trở nên an toàn.
2. Viết quy tắc có thể giải thích
Tạo .dockerignore bằng nội dung sau:
.git
**/node_modules
**/.env
**/.env.*
!**/.env.example
context-snapshot
Trong quy tắc Docker, ** hỗ trợ nhiều cấp thư mục, kể cả không có cấp nào. Dấu ! tạo ngoại lệ, và thứ tự quy tắc có ý nghĩa khi nhiều dòng cùng khớp. Ở đây, ngoại lệ .env.example được đặt sau dòng loại .env.*. Đừng đảo thứ tự mà không kiểm tra lại.
Dòng cuối ngăn output chẩn đoán trở thành input của lần build tiếp theo. .gitignore có thể có nội dung khác nhưng không thay thế .dockerignore trong một local directory context. Bạn cần review cả hai theo mục đích riêng.
3. Xuất cây file của một stage chẩn đoán
Tạo file Dockerfile:
FROM scratch AS context-check
COPY . /snapshot
Stage này không có chương trình để chạy. Nó chỉ đặt các file có thể COPY vào /snapshot. Sau đó xuất filesystem ra máy bằng local exporter của Buildx:
docker buildx build --no-cache --output type=local,dest=./context-snapshot .
test -f context-snapshot/snapshot/src/app.txt
test -f context-snapshot/snapshot/.env.example
test ! -e context-snapshot/snapshot/.env
test ! -e context-snapshot/snapshot/node_modules
Mỗi lệnh test trả mã 0 khi điều kiện đúng. Kiểm tra exit code của từng lệnh hoặc chạy chúng trong một script có set -e để dừng khi sai. Không có dòng output không đồng nghĩa mọi lệnh đều thành công nếu terminal tiếp tục chạy sau lỗi.
Dockerfile và .dockerignore là hai file đặc biệt phục vụ build; không dùng sự có mặt của chúng trong cây COPY làm tiêu chí kiểm tra fixture. Tập trung vào các file ứng dụng và các file giả mà bạn đã tạo.
4. Áp dụng cho dự án thật
Liệt kê những file build thực sự cần: manifest, lockfile, source và tài nguyên. Sau đó liệt kê các nhóm không cần gửi: lịch sử Git, dependency local, cache, output cũ và cấu hình riêng. Đặt quy tắc dựa trên cấu trúc repository thay vì sao chép một danh sách dài không rõ tác dụng.
Với monorepo, kiểm tra context được CI truyền vào. Một .dockerignore ở thư mục ứng dụng sẽ không có hiệu lực như bạn mong đợi nếu context thực tế lại là gốc monorepo và không dùng cơ chế ignore riêng cho Dockerfile.
Lỗi thường gặp và giới hạn
Nếu .env.example biến mất, đọc quy tắc cuối cùng khớp đường dẫn đó. Nếu dependency local vẫn xuất hiện, kiểm tra vị trí .dockerignore và context trước khi kết luận Docker dùng cache sai. Nếu output phình sau mỗi lần thử, đảm bảo thư mục export đã được loại khỏi input.
Không COPY bí mật rồi xóa ở layer sau để thay cho việc loại nó từ đầu. Nếu build cần credential, tìm hiểu secret mount của BuildKit và cách tool đang dùng đọc secret. Ignore file không thay thế việc thu hồi credential đã lộ hoặc rà nội dung artifact cuối.
Gợi ý tiếp theo
Giữ fixture kiểm tra file trong một nhánh thử và chạy bằng đúng lệnh CI sẽ dùng. Sau khi hiểu tập file đầu vào, quay về Dockerfile ứng dụng và kiểm tra artifact cuối theo yêu cầu dự án. Nhìn tận nơi file nào được đưa vào build giúp bạn phát hiện lỗi cấu hình sớm hơn việc chỉ nhìn dòng COPY . ..

