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.

Ba lớp đo dung lượng Docker — docker system df bên trong Docker, df -h bên trong VM, và file Docker.raw sparse trên đĩa host macOS — kèm khoảng cách 1,11GB giữa số Docker báo và đĩa thật

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/dockerdocker 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.rawsparse 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êudocker 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ìn du -sh vào file Docker.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ì Dockerdu -sh /var/lib/docker trực tiếp, không cần đường vòng nào.
  • Đừng bao giờ đọc ls -la trên một file đĩa ảo và coi đó là dung lượng đang dùng — luôn du.

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