Skip to content

Buổi 4: Docker Volumes & Networking

🎯 Mục tiêu

  • Hiểu vấn đề mất dữ liệu khi container bị xóa
  • Sử dụng bind mountnamed volume
  • Hiểu các loại Docker network
  • Kết nối các container với nhau qua network
  • Thực hành: WordPress + MySQL với volume và network

1. Vấn đề: Dữ liệu trong container

Container là ephemeral (tạm thời)

┌─────────────────────────────────────┐
│           Container (nginx)          │
│                                     │
│  /usr/share/nginx/html/             │
│    └── index.html  (sửa ở đây)     │
│                                     │
│  ⚠️ Writable Layer (tạm thời)       │
└─────────────────────────────────────┘

      docker rm  ← XÓA container


    💀 Mất hết dữ liệu!

Vấn đề: Khi xóa container, toàn bộ dữ liệu bên trong bị mất.

Giải pháp: Dùng Volumes để lưu dữ liệu bên ngoài container.


2. Các cách lưu trữ dữ liệu

┌────────────────────────────────────────────────┐
│                 Host Machine                    │
│                                                │
│  ┌──────────────┐  ┌───────────────────────┐   │
│  │ Bind Mount   │  │    Named Volume       │   │
│  │              │  │                       │   │
│  │ /home/user/  │  │  /var/lib/docker/     │   │
│  │   data/      │  │    volumes/mydata/    │   │
│  └──────┬───────┘  └───────────┬───────────┘   │
│         │                      │               │
│         ▼                      ▼               │
│  ┌─────────────────────────────────────────┐   │
│  │            Container                     │   │
│  │   /app/data  ◀── bind mount              │   │
│  │   /var/data  ◀── named volume            │   │
│  └─────────────────────────────────────────┘   │
└────────────────────────────────────────────────┘

So sánh các phương pháp

Đặc điểmBind MountNamed Volumetmpfs Mount
Vị tríĐường dẫn hostDocker quản lýRAM
Tạo bởiUser chỉ địnhDockerDocker
Persist❌ (mất khi stop)
PerformanceTốtTốt nhấtNhanh nhất
Chia sẻGiữa host & containerGiữa containersKhông
Use caseDev (live reload)Database, dataSecrets, cache

3. Bind Mount

Cú pháp

bash
# Cú pháp đầy đủ (khuyến nghị)
$ docker run -v /đường/dẫn/host:/đường/dẫn/container IMAGE

# Hoặc dùng --mount
$ docker run --mount type=bind,source=/host/path,target=/container/path IMAGE

Ví dụ: Live reload cho development

bash
# Tạo thư mục và file HTML
$ mkdir -p ~/mysite
$ echo "<h1>Hello Docker Volume!</h1>" > ~/mysite/index.html

# Chạy nginx với bind mount
$ docker run -d \
  --name web \
  -p 8080:80 \
  -v ~/mysite:/usr/share/nginx/html:ro \
  nginx

# Sửa file trên host → container tự cập nhật!
$ echo "<h1>Updated!</h1>" > ~/mysite/index.html
# → Refresh trình duyệt để thấy thay đổi
FlagÝ nghĩa
:roRead-only – container không ghi được
:rwRead-write (mặc định)

Use case cho Bind Mount

bash
# Development: mount source code
$ docker run -d -v $(pwd)/src:/app/src -p 3000:3000 node-app

# Config file
$ docker run -d -v ./nginx.conf:/etc/nginx/nginx.conf:ro nginx

# Logs ra host
$ docker run -d -v ./logs:/var/log/app my-app

4. Named Volume

Quản lý volumes

bash
# Tạo volume
$ docker volume create mydata

# Liệt kê volumes
$ docker volume ls
DRIVER    VOLUME NAME
local     mydata

# Xem chi tiết volume
$ docker volume inspect mydata
[
  {
    "CreatedAt": "2025-01-15T10:30:00Z",
    "Driver": "local",
    "Labels": {},
    "Mountpoint": "/var/lib/docker/volumes/mydata/_data",
    "Name": "mydata",
    "Options": {},
    "Scope": "local"
  }
]

# Xóa volume
$ docker volume rm mydata

# Xóa tất cả volume không dùng
$ docker volume prune

Ví dụ: PostgreSQL với Named Volume

bash
# Tạo volume cho database
$ docker volume create pgdata

# Chạy PostgreSQL với volume
$ docker run -d \
  --name postgres \
  -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=mydb \
  -v pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16

# Tạo dữ liệu
$ docker exec -it postgres psql -U postgres -d mydb \
  -c "CREATE TABLE users (id SERIAL, name TEXT); INSERT INTO users (name) VALUES ('Docker Fan');"

# Xóa container
$ docker rm -f postgres

# Chạy container mới với cùng volume → DỮ LIỆU VẪN CÒN!
$ docker run -d \
  --name postgres-new \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgres:16

# Kiểm tra dữ liệu
$ docker exec -it postgres-new psql -U postgres -d mydb \
  -c "SELECT * FROM users;"
#  id |    name
# ----+------------
#   1 | Docker Fan

💡 Key Point

Named Volume giữ dữ liệu ngay cả khi container bị xóa. Đây là cách lưu trữ database trong Docker.


5. Docker Networking

Các loại network

┌─────────────────────────────────────────────────┐
│                  Host Machine                    │
│                                                  │
│  ┌─── bridge (default) ──────────────────────┐  │
│  │  Container A ◄──────► Container B          │  │
│  │  172.17.0.2           172.17.0.3           │  │
│  └────────────────────────────────────────────┘  │
│                                                  │
│  ┌─── custom-net (user-defined) ─────────────┐  │
│  │  Container C ◄──────► Container D          │  │
│  │  "app"   (DNS)        "db"   (DNS)        │  │
│  └────────────────────────────────────────────┘  │
│                                                  │
│  ┌─── host ──────────────────────────────────┐  │
│  │  Container E  (dùng trực tiếp port host)   │  │
│  └────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

So sánh các loại network

NetworkCách lyDNSPerformanceUse case
bridge (default)❌ Chỉ IPTốtContainer đơn lẻ
bridge (custom)✅ Tên containerTốtMulti-container
hostN/ATốt nhấtCần performance
none✅✅N/ACách ly hoàn toàn
overlayTốtDocker Swarm

6. Quản lý Networks

bash
# Liệt kê networks
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
f6e5d4c3b2a1   host      host      local
1a2b3c4d5e6f   none      null      local

# Tạo custom network
$ docker network create my-network

# Xem chi tiết
$ docker network inspect my-network

# Xóa network
$ docker network rm my-network

# Dọn dẹp networks không dùng
$ docker network prune

Kết nối container vào network

bash
# Tạo custom network
$ docker network create app-network

# Chạy container trong network
$ docker run -d --name db --network app-network \
  -e POSTGRES_PASSWORD=secret postgres:16

$ docker run -d --name app --network app-network \
  -e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
  -p 3000:3000 my-app

# Container "app" có thể kết nối "db" bằng TÊN!
# → postgres://postgres:secret@db:5432/postgres

Kết nối/ngắt container khỏi network

bash
# Thêm container vào network
$ docker network connect app-network existing-container

# Ngắt container khỏi network
$ docker network disconnect app-network existing-container

7. Thực hành: WordPress + MySQL

Tạo stack hoàn chỉnh

bash
# Bước 1: Tạo network
$ docker network create wp-network

# Bước 2: Tạo volumes
$ docker volume create wp-db-data
$ docker volume create wp-content

# Bước 3: Chạy MySQL
$ docker run -d \
  --name wp-mysql \
  --network wp-network \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -e MYSQL_DATABASE=wordpress \
  -e MYSQL_USER=wpuser \
  -e MYSQL_PASSWORD=wppass \
  -v wp-db-data:/var/lib/mysql \
  mysql:8

# Bước 4: Chạy WordPress
$ docker run -d \
  --name wp-app \
  --network wp-network \
  -e WORDPRESS_DB_HOST=wp-mysql \
  -e WORDPRESS_DB_USER=wpuser \
  -e WORDPRESS_DB_PASSWORD=wppass \
  -e WORDPRESS_DB_NAME=wordpress \
  -v wp-content:/var/www/html/wp-content \
  -p 8080:80 \
  wordpress:latest

# Bước 5: Mở http://localhost:8080 → Setup WordPress!

Kiến trúc

        Browser

           ▼ :8080
  ┌─────────────────┐     ┌─────────────────┐
  │   WordPress     │────▶│     MySQL       │
  │   (wp-app)      │     │   (wp-mysql)    │
  │   Port 80       │     │   Port 3306     │
  │                 │     │                 │
  │  📁 wp-content  │     │  📁 mysql data  │
  │  (volume)       │     │  (volume)       │
  └─────────────────┘     └─────────────────┘
         wp-network (custom bridge)

🏋️ Bài tập thực hành

Bài 1: Bind Mount

  1. Tạo thư mục ~/docker-lab/site với file index.html
  2. Mount vào nginx: -v ~/docker-lab/site:/usr/share/nginx/html:ro
  3. Sửa file HTML trên host, refresh trình duyệt

Bài 2: Named Volume cho Database

  1. Tạo volume testdb-data
  2. Chạy PostgreSQL với volume
  3. Tạo bảng và thêm dữ liệu
  4. Xóa container, tạo container mới với cùng volume
  5. Kiểm tra dữ liệu còn không

Bài 3: Custom Network

  1. Tạo network lab-network
  2. Chạy 2 containers: alpine-1alpine-2 trong cùng network
  3. Từ alpine-1, ping alpine-2 bằng tên: docker exec alpine-1 ping alpine-2

Bài 4: WordPress stack

  1. Triển khai WordPress + MySQL như hướng dẫn ở mục 7
  2. Hoàn thành setup WordPress
  3. Viết một bài post
  4. Xóa container WordPress (giữ MySQL & volumes)
  5. Tạo lại container WordPress → kiểm tra bài post vẫn còn