Skip to content

02 - Docker 数据卷与持久化

为什么需要数据卷?

容器的可写层是临时的——容器删除后,数据就没了。 数据卷(Volume)解决了数据持久化和容器间数据共享的问题。


三种数据挂载方式

┌─────────────────────────────────────────────────┐
│  Docker Host                                    │
│                                                 │
│  ┌──────────┐  ┌───────────┐  ┌──────────────┐ │
│  │ Volume   │  │Bind Mount │  │    tmpfs      │ │
│  │          │  │           │  │   mount       │ │
│  │ Docker   │  │ 宿主机    │  │   内存中      │ │
│  │ 管理的   │  │ 任意目录  │  │   临时存储    │ │
│  │ 存储区域 │  │           │  │              │ │
│  └─────┬────┘  └─────┬─────┘  └──────┬───────┘ │
│        │             │               │          │
│        ▼             ▼               ▼          │
│  ┌─────────────────────────────────────────┐    │
│  │              Container                   │    │
│  │     /data       /app       /tmp/cache    │    │
│  └─────────────────────────────────────────┘    │
└─────────────────────────────────────────────────┘
类型管理方式位置适用场景
VolumeDocker 管理/var/lib/docker/volumes/生产环境数据持久化
Bind Mount用户指定宿主机任意路径开发环境、配置文件
tmpfs内存不写入磁盘敏感信息、临时缓存

Volume 操作

bash
# 创建命名卷
docker volume create my-data

# 列出所有卷
docker volume ls

# 查看卷详情
docker volume inspect my-data

# 使用卷运行容器
docker run -d \
  --name db \
  -v my-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8

# 删除容器后卷仍然存在
docker stop db && docker rm db
docker volume ls  # my-data 还在

# 用新容器挂载同一个卷,数据还在
docker run -d \
  --name db2 \
  -v my-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8

# 清理
docker stop db2 && docker rm db2
docker volume rm my-data

Bind Mount 操作

bash
# 挂载当前目录
docker run -d \
  -v $(pwd)/html:/usr/share/nginx/html:ro \
  -p 8080:80 \
  nginx:alpine
# :ro 表示只读

# 挂载单个文件
docker run -d \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx:alpine

Volume vs Bind Mount 对比

特性VolumeBind Mount
存储位置Docker 管理宿主机任意路径
可移植性好(不依赖宿主机路径)差(依赖宿主机结构)
备份Docker 命令管理需要自行管理
性能macOS 上更好macOS 上可能较慢
推荐场景生产环境开发环境

实操练习

bash
# 体验数据持久化
docker volume create postgres-data

docker run -d \
  --name pg \
  -v postgres-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  postgres:16-alpine

# 创建一些数据
docker exec -it pg psql -U postgres -c "CREATE TABLE test(id serial, name text);"
docker exec -it pg psql -U postgres -c "INSERT INTO test(name) VALUES ('hello docker');"

# 删除容器
docker stop pg && docker rm pg

# 用新容器挂载同一个卷
docker run -d \
  --name pg2 \
  -v postgres-data:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  postgres:16-alpine

# 数据还在!
docker exec -it pg2 psql -U postgres -c "SELECT * FROM test;"

# 清理
docker stop pg2 && docker rm pg2
docker volume rm postgres-data

下一步

03 - 多阶段构建