Phần 45 dùng docker system df để đo image, volume và build cache trước khi prune. Bài đó dừng lại ở đúng con số Docker tự báo: image chiếm 2,209GB. Nhưng "Docker báo" và "đĩa thật mất" là hai câu hỏi khác nhau — docker system df chỉ nhìn vào bên trong Docker, không nhìn vào ổ đĩa vật lý. Bài này đào xuống tận đáy: trên chính máy đang viết bài (Docker Desktop 29.7.2, macOS, Apple Silicon), đĩa host thật sự mất bao nhiêu, và vì sao con số đó không khớp với những gì docker system df báo.
Lớp A: docker system df nói gì
docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 5 1 2.209GB 1.674GB (75%)
Containers 1 1 4.096kB 0B (0%)
Local Volumes 0 0 0B 0B
Build Cache 4 0 4.121MB 28.83kB
Cộng bốn dòng lại: 2,209GB + 4,096kB + 0B + 4,121MB ≈ 2,213GB. Đây là con số hầu hết tài liệu dừng lại — kể cả phần 45 của chính sê-ri này. Nó đúng, nhưng nó chỉ đúng ở phạm vi Docker tự biết: image nào tồn tại, container nào đang chạy, build cache nào còn dùng. Docker không tự hỏi "ổ đĩa vật lý mất bao nhiêu byte cho từng thứ đó" — hai câu hỏi nghe giống nhau nhưng không phải một.
Lớp B: Docker Desktop chạy Linux trong một máy ảo
Trên macOS, Docker Desktop không chạy container thẳng trên hệ điều hành host — nó dựng một máy ảo Linux (OSType: linux, kernel LinuxKit) rồi chạy toàn bộ Docker Engine bên trong đó. Docker Root Dir: /var/lib/docker mà docker info báo là đường dẫn bên trong VM, không phải đường dẫn nào trên macOS. Đứng từ macOS, không có cách nào gõ thẳng du -sh /var/lib/docker — thư mục đó không tồn tại ở tầng host.
Cách xem đĩa từ trong VM: chạy df bên trong một container.
docker run --rm alpine:3.20 df -h /
Filesystem Size Used Available Use% Mounted on
overlay 223.6G 3.1G 209.1G 1% /
3,1G đã dùng, trên tổng dung lượng khả dụng 223,6G. Đây là góc nhìn "từ bên trong hệ điều hành khách" — filesystem overlay thấy chính xác nó đang chiếm bao nhiêu khối trên ổ đĩa ảo của nó.
Lớp C: cả VM đó nằm trong đúng một file trên macOS
VM của Docker Desktop lưu trạng thái vào một file duy nhất:
ls -la ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw
-rw-r--r--@ 1 user staff 245106737152 Aug 25 22:40 Docker.raw
245.106.737.152 byte ≈ 245,1GB. Nhìn con số này mà kết luận "Docker đang chiếm 245GB đĩa" là sai — và đây chính là cái bẫy đáng nói nhất bài này. ls -la chỉ báo kích thước đã cấp phát (kích thước ảo mà Docker Desktop cấu hình cho ổ đĩa VM), không phải số byte thật sự nằm trên đĩa. Đo bằng du, thứ đếm khối thật đã ghi:
du -sh ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw
3.1G Docker.raw
Chính xác hơn bằng khối 1KB:
du -sk ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw
# 3243000
3.243.000 × 1.024 byte ≈ 3,32GB. Cùng một file, hai lệnh, chênh nhau gần 74 lần — vì Docker.raw là sparse file: hệ điều hành chỉ cấp phát khối đĩa thật khi có dữ liệu thật được ghi vào, còn phần "chưa ai đụng tới" không tốn byte nào trên đĩa dù ls vẫn báo nó tồn tại ở đó. File này chỉ thật sự chiếm khoảng 1,4% dung lượng mà nó được cấp phát tối đa.
Đây không phải hành vi riêng của Docker — mọi ổ đĩa ảo cấp phát động (VirtualBox VDI, qcow2 của QEMU, VHDX của Hyper-V) đều dùng cùng cơ chế. Bài học áp dụng rộng hơn Docker: đừng bao giờ tin ls -la khi đo dung lượng một file có thể là ổ đĩa ảo hoặc file thưa — luôn dùng du để biết số khối thật đã ghi.
Hai phép đo độc lập, một kết quả gần khớp nhau: df -h từ bên trong VM báo 3,1G đã dùng; du từ bên ngoài nhìn vào file sparse báo 3,32G khối thật. Chênh lệch nhỏ (làm tròn hiển thị, cộng thêm một chút log/metadata VM ghi thêm giữa hai lần đo) là bình thường — điều quan trọng là cả hai đều nằm quanh 3,1-3,3GB, không phải quanh 245GB hay quanh 2,2GB.
Khoảng cách 1,11GB mà docker system df không đếm
Giờ đặt hai lớp cạnh nhau:
- Lớp A (
docker system df): 2,213GB - Lớp B/C (đĩa thật, đo bằng hai cách độc lập): ~3,32GB
Chênh lệch: 1,11GB, tức đĩa thật dùng nhiều hơn khoảng 50% so với con số Docker tự báo. Đây là chỗ bài viết phải nói thẳng theo đúng tinh thần sê-ri: không đoán khi không chắc. Có một manh mối đáng ngờ nhưng chưa xác minh được đầy đủ:
docker info | grep -A1 "Storage Driver"
Storage Driver: overlayfs
driver-type: io.containerd.snapshotter.v1
Docker 29 trên máy này dùng containerd snapshotter làm nơi lưu image thay vì graph driver overlay2 cũ. Với containerd, nội dung image có thể tồn tại ở hai dạng cùng lúc: blob nén (như tải về từ registry) trong content store, và layer đã giải nén trong snapshot dùng để chạy container. docker system df báo kích thước theo layer đã giải nén — nếu content store còn giữ thêm bản nén song song, phần đó là đĩa thật bị chiếm nhưng không nằm trong con số "Images 2,209GB". Đây là giả thuyết hợp lý dựa trên kiến trúc containerd, không phải điều đã đo tách bạch được trên máy này — thử tách riêng dung lượng content store ra khỏi phần còn lại của VM đã không thành công trong lúc chuẩn bị bài (lệnh soi trực tiếp vào /var/lib/docker bên trong VM bị treo quá lâu, phải huỷ giữa chừng). Ghi lại trung thực: khoảng cách 1,11GB là số đo thật, còn nguyên nhân chính xác thì chưa xác minh được hết — số liệu future work, không phải kết luận.
Trên máy chủ Linux thì khác
Toàn bộ chuyện file VM sparse ở trên chỉ đúng với Docker Desktop (macOS, Windows). Blog này khi triển khai thật lại chạy trên máy chủ Linux, không phải Docker Desktop — ở đó Docker Engine chạy thẳng trên kernel host, không có lớp VM nào ở giữa. /var/lib/docker là một thư mục thật ngay trên ổ đĩa máy chủ, nên đo trực tiếp được:
sudo du -sh /var/lib/docker
Không cần soi qua file VM, không có khái niệm sparse ba tầng như trên macOS. Nếu đang đọc bài này để áp dụng cho server production, phần Lớp B và Lớp C ở trên có thể bỏ qua — chỉ Lớp A (docker system df) và du -sh /var/lib/docker là đủ, và trên Linux hai con số này thường khớp nhau sát hơn nhiều so với trên Docker Desktop, vì không phải cộng thêm chi phí của tầng ảo hoá.
Bảng tổng hợp
| Lớp đo | Lệnh | Kết quả | Ý nghĩa |
|---|---|---|---|
| A — bên trong Docker | docker system df |
2,213GB | Docker tự biết mình đang giữ gì |
| B — bên trong VM | docker run alpine df -h / |
3,1G đã dùng / 223,6G | Filesystem overlay của VM thấy gì |
| C — kích thước ảo trên host | ls -la Docker.raw |
245,1GB | Dung lượng đã cấp phát, không phải đã dùng |
| C — khối thật trên host | du -sh Docker.raw |
3,32GB | Đĩa macOS thật sự bị chiếm bao nhiêu |
Vậy nên tin số nào
- Muốn biết có thể prune được bao nhiêu →
docker system df, đúng mục đích thiết kế của nó (xem phần 45 để prune an toàn). - Muốn biết đĩa macOS/Windows còn trống bao nhiêu vì Docker → đừng nhìn
docker system df, đó là số thấp hơn thực tế khoảng 50% trên máy này. Nhìndu -shvào fileDocker.raw(macOS) hoặc dung lượng file đĩa ảo tương ứng trên Windows. - Muốn biết đĩa server Linux còn trống bao nhiêu vì Docker →
du -sh /var/lib/dockertrực tiếp, không cần đường vòng nào. - Đừng bao giờ đọc
ls -latrên một file đĩa ảo và coi đó là dung lượng đang dùng — luôndu.
Thử ba mươi giây
Nếu đang dùng Docker Desktop trên macOS, so ngay hai lệnh này để tự thấy khoảng cách:
docker system df | awk 'NR==2{print "Docker bao (images):", $4}'
du -sh ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw
Chênh lệch giữa hai số chính là phần đĩa thật Docker đang giữ mà docker system df không kể ra.
Bài viết liên quan
- docker image prune trả về 0 byte, volume prune xoá mất 942MB không hỏi lại: đo ba lớp rác mồ côi của Docker
- Nén chỉ giảm 3% dung lượng nhưng chậm gấp 6 lần: đo ba cách sao lưu và khôi phục volume Docker
- Bind mount ghi ngẫu nhiên 4K nhanh gấp 4 lần volume, nhưng thua khi ghi tuần tự: đo I/O theo bốn kiểu truy cập