02 - Docker 数据卷与持久化
为什么需要数据卷?
容器的可写层是临时的——容器删除后,数据就没了。 数据卷(Volume)解决了数据持久化和容器间数据共享的问题。
三种数据挂载方式
┌─────────────────────────────────────────────────┐
│ Docker Host │
│ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ Volume │ │Bind Mount │ │ tmpfs │ │
│ │ │ │ │ │ mount │ │
│ │ Docker │ │ 宿主机 │ │ 内存中 │ │
│ │ 管理的 │ │ 任意目录 │ │ 临时存储 │ │
│ │ 存储区域 │ │ │ │ │ │
│ └─────┬────┘ └─────┬─────┘ └──────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Container │ │
│ │ /data /app /tmp/cache │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘| 类型 | 管理方式 | 位置 | 适用场景 |
|---|---|---|---|
| Volume | Docker 管理 | /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-dataBind 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:alpineVolume vs Bind Mount 对比
| 特性 | Volume | Bind 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