Trong quá trình review Dockerfile cho team dev, mình thấy một thói quen rất phổ biến là dùng ngay FROM ubuntu:latest hoặc FROM python:latest. Đúng ra là phải share cho anh em cái Dockerfile Contest 2025 vừa rồi sẽ ngộ ra nhiều thứ ngay.
Tư duy này thường xuất phát từ sự tiện lợi: “Cứ cài bản full cho đầy đủ thư viện, đỡ phải cài thêm lắt nhắt, chạy được là được.” Các bạn dev có thể hơi thiếu hơn kiến thức bảo mật và vận hành hệ thống hơn các bạn sysadmin và chắc chắn ngược lại các bạn sysadmin khả năng code sẽ không bằng các bạn dev, nên chúng ta tập trung vào đúng giá trị nha. Vì vậy, khi ra môi trường Production, cái sự “tiện lợi” đó sẽ trả giá bằng thời gian deploy, Registry cost và nghiêm trọng hơn là Attack Surface.
Bài này coi như là một chút thực tế cho các bạn cực kỳ newbie…

Bài toán
Để bài test công bằng, mình sẽ build một ứng dụng Python đơn giản. Ứng dụng này chỉ import một vài thư viện phổ biến và in ra dòng “Hello World”.
Các đối thủ tham gia benchmark bao gồm:
- Fat Image (
python:3.11): Dựa trên Debian đầy đủ. Chứa đủ thứ tool từ git, curl đến build-essential. - Slim Image (
python:3.11-slim): Phiên bản rút gọn của Debian. Đã lược bỏ các package không thiết yếu, chỉ giữ lại những gì tối thiểu để chạy Python. - Alpine Image (
python:3.11-alpine): Dựa trên Alpine Linux. Sử dụngmusl libcthay vìglibctruyền thống. Nổi tiếng là siêu nhẹ.
Mục tiêu của mình là đo đạc 3 chỉ số quan trọng: Image Size, Pull Time và Vulnerability Count.
Phân tích
Trước khi xem kết quả, mình muốn phân tích một chút về bản chất kỹ thuật của các image này.
- Fat Image: Đây là lựa chọn an toàn cho môi trường Development. Nó có sẵn compiler và header file. Nếu các bạn cài một thư viện Python cần biên dịch C extension (ví dụ
numpyhaypandasđời cũ), nó sẽ chạy mượt mà. Nhưng đổi lại, nó mang theo cả một hệ điều hành Debian cồng kềnh. - Slim Image: Đây là điểm Sweet spot. Nó vẫn dùng
glibc, nên tính tương thích binary cực tốt. Hầu hết các thư viện Python trên PyPI đều hỗ trợ tốt. Nó nhẹ hơn Fat rất nhiều nhưng thiếu các build tool. - Alpine Image: Đây là lựa chọn tối ưu cho kích thước. Nhưng Alpine dùng
musl libc. Điều này có nghĩa là nhiều binary wheel compiled choglibcsẽ không chạy được. Pip sẽ phải tự động compile lại từ source, dẫn đến việc build image lâu hơn và đôi khi gặp lỗi biên dịch rất khó chịu nhất là với các thư viện liên quan đến gRPC hay Machine Learning.
Benchmark
Mình thực hiện build 3 Dockerfile tương ứng và dùng công cụ trivy để quét lỗi bảo mật.
Kịch bản test:
- Build image.
- Kiểm tra dung lượng bằng
docker images. - Quét lỗi bảo mật bằng
trivy image. - Đo thời gian xóa image local và pull lại trên đường truyền mạng tiêu chuẩn.
Kết quả đo đạc
Đây là số liệu mình thu được, số liệu có thể dao động tùy thời điểm update của image:
1. Fat Image (python:3.11)
- Image Size: ~1.01 GB
- Vulnerability (High/Critical): ~300+ lỗi, bao gồm lỗi hệ thống Debian chưa vá.
- Pull Time: ~45 giây.
2. Slim Image (python:3.11-slim)
- Image Size: ~154 MB
- Vulnerability (High/Critical): ~40 lỗi.
- Pull Time: ~8 giây.
3. Alpine Image (python:3.11-alpine)
- Image Size: ~58 MB
- Vulnerability (High/Critical): 0 hoặc rất ít, thường < 5.
- Pull Time: ~3 giây.
Key Findings
Nhìn vào số liệu trên ta có thể thấy.
- Về dung lượng: Fat image nặng gấp 6.5 lần so với Slim và gấp 17 lần so với Alpine. Trong môi trường Kubernetes, khi Node phải thực hiện Scale-out, việc pull 1GB dữ liệu qua mạng sẽ làm chậm quá trình Pod startup đáng kể, đặc biệt khi Registry throttling.
- Về bảo mật: Fat image chứa hàng trăm công cụ thừa thãi như
netcat,curl, compiler. Nếu Hacker chiếm được quyền điều khiển Container, hắn có sẵn một kho vũ khí để tấn công các service khác trong mạng. Alpine và Slim giảm thiểu Attack Surface này đi rất nhiều. - Về tính tương thích: Mặc dù Alpine nhẹ nhất, nhưng kinh nghiệm cho thấy Alpine thường gây đau đầu với các vấn đề về DNS resolver và performance của
muslmemory allocator kém hơnglibctrong một số workload cao.
Kết luận
Từ bài benchmark này, mình rút ra vài nguyên tắc khi xây dựng Image Production:
- Tuyệt đối không dùng Fat Image cho Production: Trừ khi các bạn có lý do cực kỳ đặc biệt. Việc lãng phí băng thông và tài nguyên lưu trữ là không thể chấp nhận được ở quy mô lớn.
- Ưu tiên Slim Image: Với mình, Debian Slim là sự lựa chọn cân bằng nhất. Nó đủ nhỏ khoảng 150MB là chấp nhận được, bảo mật tốt hơn bản Fat, và quan trọng nhất là tính tương thích tuyệt vời với hệ sinh thái Python/NodeJS vốn dựa nhiều vào
glibc. - Cẩn trọng với Alpine: Chỉ dùng Alpine nếu các bạn thực sự hiểu về
musl libcvà sẵn sàng xử lý các lỗi build time phức tạp. Đừng chọn Alpine chỉ vì nó nhẹ hơn Slim 100MB nếu phải đánh đổi bằng hàng giờ debug lỗi missing shared library. - Áp dụng Multi-stage Build: Các bạn có thể dùng Fat image ở stage
builderđể biên dịch code, sau đó copy binary hoặc thư viện sang stagefinaldùng Slim hoặc Alpine. Như vậy chúng ta vừa build dễ dàng, vừa có image cuối cùng siêu nhẹ. - Luôn quét Vulnerability: Tích hợp
trivyhoặc các công cụ quét Image vào CI/CD Pipeline. Đừng để một image chứa 300 lỗi Critical lọt lên Production.








