Trong container, khi bạn ls / bạn thấy một cây thư mục hoàn chỉnh — /bin, /etc, /usr... — trông y như một hệ Linux đầy đủ, và không hề thấy file của host hay của container khác. Nhưng container dùng chung kernel host, chung ổ đĩa vật lý. Vậy làm sao mỗi container lại có một / riêng? Câu trả lời là hai cơ chế kernel ghép lại: mount namespace (cô lập bảng mount) và pivot_root/chroot (đổi thư mục gốc). Bài này (phần 3 loạt Docker) chạy thật để dựng lại từng tầng — kể cả tự tay tạo một rootfs tối giản bằng chroot.
Container thấy rootfs riêng, không phải / của host
Điều đầu tiên: / trong container không phải / của host. Nó là rootfs lấy từ image (alpine, ubuntu...). Đo thật, so / của môi trường host với / của container alpine:
# / của host: bin boot dev etc go home ... work (có thư mục go, work của mình)
docker run alpine ls / # bin dev etc home lib ... var (rootfs alpine, KHÔNG có go/work)
Hai cây thư mục khác hẳn nhau, dù chạy trên cùng một kernel và cùng một máy. Container không "nhìn thấy" file của host — nó có hệ thống file của riêng mình.

Hình 1: / container là rootfs từ image, khác / host; mount namespace cô lập bảng mount; chroot/pivot_root đổi thư mục gốc; ghép lại thành hệ thống file hoàn chỉnh, cô lập của container.
Đo thật: ba tầng của filesystem container

Hình 2: Chạy thật — / host có go/work, / container không (rootfs riêng); trong mount namespace mount tmpfs /tmp/rieng chỉ ns đó thấy (f.txt), ngoài ns thư mục rỗng; chroot /newroot đổi / thành rootfs tối giản chỉ có bin/lib.
- Rootfs riêng:
/host chứago,work(thư mục của mình);/container alpine chỉ có rootfs của image, không hề có chúng. Đây là cây thư mục độc lập. - Mount namespace cô lập bảng mount:
unshare --mounttạo một bảng mount riêng. Bên trong,mount -t tmpfs none /tmp/riengvà tạof.txt—lsthấy file,mountthấy tmpfs. Nhưng ngoài namespace,/tmp/riengrỗng — mount đó vô hình với tiến trình khác. Đây là cách mỗi container có bảng mount (và các mount/proc,/sys, volume) của riêng nó. - chroot đổi thư mục gốc: mình dựng một rootfs tối giản (
/newrootvớibusybox+ musl loader), rồichroot /newroot /bin/sh. Sau đóls /chỉ thấybinvàlib— không còn/etc,/proc,/usrcủa rootfs cũ./của tiến trình đã bị đổi. Đây chính là tiền thân của cách container "chuyển" vào rootfs của image.
Ghép lại thành hệ thống file container
Một container thật ghép cả ba: tạo mount namespace (để mọi mount tiếp theo cô lập), rồi pivot_root (Docker dùng cái này thay chroot) sang rootfs của image, rồi mount /proc, /sys, /dev riêng và bind mount các volume. Kết quả: container thấy một cây / hoàn chỉnh, độc lập, dù thực chất chỉ là vài lời gọi kernel trên chính hệ thống file của host.
Đánh đổi cần cân nhắc
chroot không phải ranh giới bảo mật; pivot_root an toàn hơn. chroot chỉ đổi / nhưng tiến trình có quyền (root) thoát ra được bằng vài kỹ thuật kinh điển. Docker dùng pivot_root kết hợp mount namespace để bỏ hẳn rootfs cũ khỏi tầm với. Đừng coi chroot là cô lập bảo mật — nó là công cụ tổ chức file, không phải hàng rào chống thoát.
Mount namespace giải thích vì sao volume/bind mount hoạt động. Vì container có bảng mount riêng, Docker "chèn" một thư mục host vào container bằng một bind mount trong namespace của container — host và container thấy cùng dữ liệu ở hai đường dẫn khác nhau. Hiểu mount namespace là hiểu vì sao thay đổi trong bind mount phản ánh ngay hai phía (chủ đề bài volume).
Rootfs của image là chỉ đọc + lớp ghi riêng. Bạn không sửa trực tiếp rootfs của image; container thêm một lớp ghi (writable layer) chồng lên bằng overlayfs (chủ đề bài sau). Vì thế nhiều container chạy cùng image chia sẻ rootfs chỉ-đọc mà vẫn ghi riêng — tiết kiệm đĩa lớn. Mount namespace và overlayfs phối hợp để làm điều đó.
Ba ý mang về
- Container có rootfs riêng, không phải / của host: đo thật,
/host cógo/workcòn/container alpine chỉ có rootfs của image — chung kernel nhưng khác hoàn toàn cây thư mục. - Mount namespace cô lập bảng mount: đo thật, tmpfs mount trong
unshare --mountchỉ ns đó thấy (f.txt), ngoài ns thư mục rỗng — nền tảng để mỗi container có mount/proc/volume riêng. - chroot/pivot_root đổi thư mục gốc: đo thật
chrootsang rootfs tối giản khiến/chỉ cònbin/lib; Docker dùngpivot_root(an toàn hơn chroot) sang rootfs của image — nhưng chroot không phải hàng rào bảo mật.
Nguồn
- man7.org — mount_namespaces(7): https://man7.org/linux/man-pages/man7/mount_namespaces.7.html
- man7.org — pivot_root(2): https://man7.org/linux/man-pages/man2/pivot_root.2.html
- man7.org — chroot(2): https://man7.org/linux/man-pages/man2/chroot.2.html
Phần sau ta xem container có mạng riêng thế nào: network namespace, cặp veth nối container với host, và vì sao mỗi container có địa chỉ IP của riêng nó.