Docker Volumes & Networking: Hiểu đúng để không mất data oan
Deep dive vào cách Docker xử lý storage và networking từ named volumes, bind mounts đến container DNS resolution.
Nguyễn Nhật Long
@nguyennhatlong1303
Nếu bạn đã đọc bài trước về Docker Compose, chắc bạn đã thấy mình dùng volumes: và networks: trong file compose mà không giải thích nhiều. Bài này mình sẽ đào sâu hơn tại sao lại có các loại storage khác nhau, khi nào dùng cái gì, và networking trong Docker thực ra hoạt động như thế nào.
Mình đã từng mất data của một PostgreSQL container chỉ vì không hiểu sự khác biệt giữa named volume và bind mount. Bài học đau lòng, nhưng đáng để chia sẻ.
Ba cách Docker lưu data và chúng không giống nhau
Nhiều người mới học Docker hay nhầm lẫn giữa các loại storage. Thực ra Docker có ba cơ chế hoàn toàn khác nhau, mỗi cái sinh ra để giải quyết một bài toán cụ thể.
| Loại | Quản lý bởi | Data persist? | Phụ thuộc host? | Dùng khi nào |
|---|---|---|---|---|
| Named Volume | Docker | ✅ Có | ❌ Không | Database, file uploads, production data |
| Bind Mount | Bạn (host path) | ✅ Có | ✅ Có | Local development, live reload |
| tmpfs Mount | Memory | ❌ Mất khi stop | ❌ Không | Sensitive data tạm thời, cache |
Named Volumes cái bạn nên dùng cho production
Named volume là Docker tự quản lý storage, bạn chỉ cần đặt tên:
1# Tạo volume2docker volume create mydata34# Gắn vào container5docker run -v mydata:/data myapp
Cái hay ở đây là data được lưu ở một chỗ do Docker quản lý (thường ở /var/lib/docker/volumes/ trên Linux), hoàn toàn độc lập với container lifecycle. Bạn stop container, xóa container, data vẫn còn nguyên. Start container mới với cùng volume name data quay lại đầy đủ.
Theo kinh nghiệm của mình, named volume là lựa chọn mặc định cho bất cứ thứ gì cần persist trong production: PostgreSQL, MySQL, Redis, file uploads của user, v.v.
Bind Mounts dev tool số một
1docker run -v /host/path:/container/path myapp2# hoặc dùng absolute path3docker run -v $(pwd)/src:/app/src myapp
Bind mount map trực tiếp một thư mục trên máy host vào trong container. Bạn edit file trên máy, container thấy ngay lập tức đây là lý do tại sao dev workflow với Docker thường dùng bind mount cho source code.
Nhưng anh em lưu ý: bind mount phụ thuộc vào cấu trúc filesystem của host. File /home/user/project trên máy mình không tồn tại trên máy người khác. Đây là lý do tại sao bind mount không phù hợp cho production.
Một gotcha nữa trên macOS/Windows: bind mount chậm hơn Linux đáng kể vì có thêm lớp virtualization ở giữa. Nếu bạn thấy container chạy chậm bất thường khi dev, đây có thể là nguyên nhân.
tmpfs khi bạn không muốn data tồn tại
1docker run --tmpfs /tmp myapp
tmpfs mount chỉ tồn tại trong memory. Container stop là data bay. Nghe có vẻ vô dụng nhưng thực ra rất hữu ích cho:
- Temporary files cần I/O nhanh
- Sensitive data như session tokens mà bạn không muốn ghi ra disk
- Cache trong quá trình build
Quản lý volumes không phải chỉ là tạo và dùng
Một thói quen mình thấy nhiều người bỏ qua là quản lý vòng đời của volumes. Sau một thời gian chạy nhiều container, bạn sẽ có đống volumes orphaned không ai dùng, chiếm disk space.
1# Xem tất cả volumes2docker volume ls34# Inspect chi tiết một volume5docker volume inspect mydata67# Xóa volume cụ thể8docker volume rm mydata910# Dọn dẹp tất cả unused volumes cẩn thận!11docker volume prune
docker volume prune là lệnh mình hay chạy định kỳ trên máy dev, nhưng tuyệt đối không chạy trên production nếu không chắc chắn 100% về những gì đang xảy ra.
Backup và restore volume đừng bỏ qua bước này
Đây là phần nhiều người không nghĩ đến cho đến khi cần. Backup một named volume:
1# Backup2docker run --rm \3 -v mydata:/data \4 -v $(pwd):/backup \5 alpine tar czf /backup/backup.tar.gz /data
Lệnh này spin up một container Alpine tạm thời, mount volume cần backup vào /data, mount thư mục hiện tại vào /backup, rồi tar toàn bộ lại. Kết quả là file backup.tar.gz ngay trên máy host của bạn.
Restore cũng tương tự:
1# Restore2docker run --rm \3 -v mydata:/data \4 -v $(pwd):/backup \5 alpine tar xzf /backup/backup.tar.gz -C /
Mình thấy cái pattern này hay ở chỗ không cần tool gì đặc biệt chỉ cần Alpine và tar là xong. Đơn giản, portable, và hoạt động trên mọi môi trường.
Networking trong Docker không phức tạp như bạn nghĩ
Phần này nhiều người hay bị confused, nhưng thực ra logic khá rõ ràng một khi bạn hiểu được mô hình.
Docker có ba network driver chính:
| Driver | Scope | Dùng khi nào | Lưu ý |
|---|---|---|---|
| Bridge | Single host | Default, local dev, production đơn giản | Cần port mapping để expose ra ngoài |
| Host | Single host | Khi cần performance tối đa, không muốn NAT | Chỉ Linux, không dùng được trên macOS/Windows |
| Overlay | Multi-host | Docker Swarm, Kubernetes | Cần orchestration setup |
Bridge network và DNS resolution tự động
Khi bạn tạo một custom bridge network và chạy containers trong đó, Docker tự động setup DNS cho bạn:
1# Tạo network2docker network create mynet34# Chạy containers trong cùng network5docker run -d --network mynet --name api myapi6docker run -d --network mynet --name db postgres
Bây giờ container api có thể gọi container db bằng hostname db không cần biết IP, không cần hardcode gì cả. Docker DNS resolver tự handle.
1# Từ bên trong container api2curl http://api:30003psql -h db -U postgres
Anh em lưu ý: DNS resolution này chỉ hoạt động với custom networks, không phải default bridge network. Đây là lý do tại sao trong Docker Compose, mọi service tự nhiên gọi được nhau bằng service name Compose tự tạo một custom network cho toàn bộ stack.
Host network khi nào thực sự cần?
1docker run --network host myapp
Với host network, container share luôn network stack với host. Không có NAT, không cần port mapping, performance tốt nhất. Nhưng mình hiếm khi dùng cái này trong thực tế vì:
- Chỉ chạy được trên Linux (macOS và Windows Docker chạy qua VM nên không có tác dụng)
- Container có thể bind bất kỳ port nào trên host security risk nếu không cẩn thận
- Mất đi tính isolation một trong những lý do chính để dùng Docker
Use case thực tế mình thấy phù hợp nhất là monitoring agents cần truy cập network metrics của host, hoặc các tool như tcpdump cần sniff traffic.
Thực hành: PostgreSQL với persistent storage
Gộp lại tất cả những gì vừa nói, đây là cách setup một PostgreSQL container đúng cách:
1# Tạo network và volume2docker network create pgnet3docker volume create pgdata45# Chạy PostgreSQL6docker run -d \7 --name postgres \8 --network pgnet \9 -v pgdata:/var/lib/postgresql/data \10 -e POSTGRES_PASSWORD=secret \11 -e POSTGRES_DB=myapp \12 postgres:151314# Chạy app kết nối đến DB15docker run -d \16 --name myapp \17 --network pgnet \18 -e DATABASE_URL=postgresql://postgres:secret@postgres/myapp \19 -p 3000:3000 \20 myapp:latest
Bây giờ thử test persistence:
1# Stop và xóa container postgres2docker stop postgres && docker rm postgres34# Start lại với cùng volume5docker run -d \6 --name postgres \7 --network pgnet \8 -v pgdata:/var/lib/postgresql/data \9 -e POSTGRES_PASSWORD=secret \10 postgres:151112# Data vẫn còn nguyên
Cái này mình hay demo cho junior dev mới join team nhìn thấy data survive qua container restart là họ hiểu ngay tại sao volumes quan trọng.
Nếu bạn dùng Docker Compose (và bạn nên dùng cho local dev), tất cả những thứ trên được express gọn hơn nhiều:
1services:2 db:3 image: postgres:154 volumes:5 - pgdata:/var/lib/postgresql/data6 environment:7 POSTGRES_PASSWORD: secret8 POSTGRES_DB: myapp910 app:11 build: .12 ports:13 - "3000:3000"14 environment:15 DATABASE_URL: postgresql://postgres:secret@db/myapp16 depends_on:17 - db1819volumes:20 pgdata:
Compose tự tạo network, tự resolve db hostname, tự manage volume lifecycle. Sạch và readable hơn nhiều so với đống lệnh docker run.
Bài tiếp theo mình sẽ nói về multi-stage builds và tối ưu Docker image size một topic mà theo kinh nghiệm của mình, phần lớn team bỏ qua và rồi tự hỏi tại sao image production lại nặng cả GB.
Nguyễn Nhật Long
@nguyennhatlong1303Nguyễn Nhật Long is a Senior Frontend Engineer and Frontend Team Leader with 7 years of experience building real-time fintech platforms. Specializing in React, Next.js, TypeScript, and React Native, shipping 10+ products across Web, Mobile, Telegram Mini-Apps, and Web3.
Thấy hay? Chia sẻ cho bạn bè!