Bạn chạy một database trong container, dùng vài ngày, rồi docker rm container để nâng cấp — và toàn bộ dữ liệu biến mất. Đây là bài học đau đớn mà nhiều người học một lần rồi nhớ mãi: dữ liệu ghi vào lớp container là tạm thời. Container được thiết kế để bỏ đi và tạo lại, nên bất cứ thứ gì bạn cần giữ lại phải nằm ở nơi khác — volume hoặc bind mount. Bài này (phần 11 loạt Docker) chạy thật để thấy dữ liệu mất thế nào, và hai cách giữ nó bền.

Lớp container là tạm — xoá là mất

Bài overlayfs đã cho thấy: container ghi vào một lớp ghi riêng chồng trên image. Lớp đó gắn với vòng đời container — docker rm xoá container cũng xoá luôn lớp ghi. Đo thật:

docker run --name vtest1 alpine sh -c 'echo "du lieu quan trong" > /data.txt'
docker rm vtest1                    # xoá container
docker run --rm alpine cat /data.txt   # -> No such file: MẤT

File /data.txt biến mất cùng container. Với dữ liệu thật (database, file người dùng tải lên), đây là mất mát không thể chấp nhận. Nguyên tắc: không bao giờ để dữ liệu quan trọng trong lớp container.

Ảnh chụp đoạn mã nền tối minh hoạ volume vs bind mount giữ dữ liệu sống sót khi container bị xoá, dữ liệu ghi vào lớp container là tạm xoá container là mất container ghi vào lớp overlay riêng của nó xoá container lớp đó bị xoá dữ liệu biến mất không bao giờ để dữ liệu quan trọng DB upload trong lớp này, named volume docker quản lý sống độc lập với container docker volume create mydata docker run -v mydata data ghi vào data vào volume xoá container chạy container mới gắn cùng volume dữ liệu còn volume nằm ở var lib docker volumes do docker lo, bind mount gắn thẳng một thư mục host vào container docker run -v duong dan host mnt host và container thấy cùng file sửa bên nào bên kia thấy ngay hợp cho dev sửa code trên host container chạy live, chọn cái nào named volume dữ liệu production DB upload docker quản lý di động backup dễ không phụ thuộc đường dẫn host bind mount dev mã nguồn hoặc cần đúng file host cụ thể nhưng phụ thuộc đường dẫn cộng quyền của host

Hình 1: Lớp container là tạm (xoá container là mất dữ liệu); named volume do Docker quản lý sống độc lập với container; bind mount gắn thẳng thư mục host vào container (hai chiều); chọn volume cho production, bind mount cho dev.

Named volume: dữ liệu sống độc lập với container

Volume là vùng lưu trữ do Docker quản lý, tồn tại độc lập với container. Gắn nó vào một đường dẫn, mọi ghi vào đó đi vào volume chứ không vào lớp container:

docker volume create mydata
docker run -v mydata:/data alpine sh -c 'echo "..." > /data/f.txt'
docker rm <container>                 # xoá container
docker run -v mydata:/data alpine cat /data/f.txt   # -> dữ liệu VẪN CÒN

Đo thật: ghi qua volume, xoá container vtest2, rồi chạy container mới gắn cùng volume — nó đọc được du lieu ben qua volume. Dữ liệu sống sót vì nó thuộc về volume, không thuộc container. Volume nằm ở /var/lib/docker/volumes/mydata/_data (Docker quản lý; bạn không cần biết đường dẫn để dùng).

Bind mount: host và container chia sẻ cùng thư mục

Bind mount gắn thẳng một thư mục có sẵn trên host vào container — cả hai thấy cùng file, sửa bên nào bên kia thấy ngay:

docker run -v /duong/dan/host:/mnt alpine ...

Đo thật hai chiều: host tạo shared.txt = "tao tren HOST", container cat đọc được ngay; container ghi thêm "sua trong CONTAINER", host đọc lại thấy cả hai dòng. Đồng bộ tức thì vì thực chất đó là cùng một file trên đĩa host, chỉ được gắn vào hai chỗ. Đây là lý do bind mount tuyệt cho phát triển: sửa mã nguồn trên host bằng editor quen thuộc, container chạy phản ánh ngay mà không cần build lại image.

Ảnh chụp bảng kết quả chạy thật volume và bind mount output thật, một ghi vào lớp container rồi xoá thì mất vtest1 ghi data.txt bằng du lieu quan trong xoá container container mới đọc data.txt ra cat can not open data.txt No such file mat cung container, hai named volume dữ liệu sống sót vtest2 ghi qua -v mydata data xoá container vtest2 container mới gắn cùng volume đọc du lieu ben qua volume còn dù container cũ đã xoá, ba bind mount host và container thấy cùng file host tạo shared.txt bằng tao tren HOST container đọc ra tao tren HOST container ghi thêm sua trong CONTAINER host đọc lại tao tren HOST sua trong CONTAINER hai chiều tức thì, bốn volume nằm đâu docker quản lý docker volume inspect Mountpoint var lib docker volumes myvol_demo _data

Hình 2: Chạy thật — ghi /data.txt rồi xoá container thì mất (No such file); named volume giữ dữ liệu qua vòng đời container (du lieu ben qua volume); bind mount cho host và container thấy cùng file hai chiều; volume nằm ở /var/lib/docker/volumes/myvol_demo/_data.

Chọn volume hay bind mount

  • Named volume cho dữ liệu production (database, file upload): Docker quản lý nên di động (không phụ thuộc đường dẫn máy cụ thể), dễ backup, dễ chia sẻ giữa container, và tách khỏi hệ thống file host.
  • Bind mount cho phát triển (mã nguồn) hoặc khi cần đúng một file/thư mục host cụ thể (file cấu hình, socket): nhưng phụ thuộc đường dẫn và quyền của host, kém di động hơn.

Đánh đổi cần cân nhắc

Bind mount trên macOS/Windows chậm hơn nhiều. Như bài "container không phải VM" đã nói, Docker Desktop chạy một VM Linux ẩn; bind mount phải đồng bộ file qua ranh giới VM ↔ host, nên I/O chậm hơn hẳn Linux gốc (đặc biệt với dự án nhiều file nhỏ như node_modules). Với dev nặng I/O trên Mac/Windows, cân nhắc dùng named volume cho node_modules và chỉ bind mount mã nguồn, hoặc dùng các chế độ đồng bộ tối ưu.

Volume ẩn mà tồn tại — dọn dẹp không tự động. docker rm container không xoá volume nó dùng (đúng thiết kế — để dữ liệu an toàn). Volume "mồ côi" tích tụ theo thời gian, chiếm đĩa. Dùng docker volume ls để xem, docker volume prune để dọn volume không container nào dùng — nhưng cẩn thận, prune xoá dữ liệu vĩnh viễn. docker rm -v xoá container kèm volume ẩn danh của nó.

Mount đè lên nội dung có sẵn của image. Nếu bạn mount một volume/thư mục vào một đường dẫn mà image đã có file, nội dung image ở đó bị che bởi mount (với bind mount rỗng thì thư mục trông trống). Đây là nguồn bối rối phổ biến ("sao file trong image biến mất?"). Named volume rỗng lần đầu thì được điền từ nội dung image ở đường dẫn đó; bind mount thì không. Biết hành vi này để không mount nhầm.

Ba ý mang về

  1. Dữ liệu trong lớp container là tạm: đo thật, ghi /data.txt rồi docker rm container thì file biến mất — không bao giờ để database/upload trong lớp container.
  2. Named volume giữ dữ liệu qua vòng đời container: đo thật, ghi qua -v mydata:/data, xoá container, container mới gắn cùng volume vẫn đọc được — volume do Docker quản lý, sống độc lập, hợp cho production.
  3. Bind mount chia sẻ thư mục host hai chiều: đo thật host và container thấy cùng file, sửa bên nào bên kia thấy ngay — tuyệt cho dev (mã nguồn), nhưng chậm trên Mac/Windows và phụ thuộc đường dẫn host.

Nguồn

Phần sau khép lại loạt với bảo mật container: chạy non-root, --cap-drop bỏ bớt quyền, --read-only khoá hệ thống file — đo thật những gì mỗi biện pháp chặn được.