Docker 第七天学习指南:数据持久化 - Volume 基础
一、学习目标
-
理解容器数据持久化的必要性
-
掌握 Volume 和 Bind Mount 的概念、区别及适用场景
-
学会使用
docker volume相关命令(create、ls、inspect、rm、prune) -
能够使用
-v和--mount参数挂载数据卷到容器
二、为什么需要数据持久化?
容器存储的特点
默认情况下,容器内创建的所有文件都存储在容器的可写层,这带来几个问题:
-
数据丢失:容器删除后,容器内的数据也随之消失(如数据库数据)
-
性能开销:容器可写层的读写性能不如直接使用主机存储
-
难以共享:不同容器之间难以共享数据
核心需求:容器需要能持久化存储数据,且数据独立于容器生命周期。
Docker 提供的两种持久化方案
| 方案 | 存储位置 | 管理方式 | 适用场景 |
|---|---|---|---|
| Volume(数据卷) | Docker 管理(/var/lib/docker/volumes/) |
由 Docker 完全管理 | 生产环境,数据持久化,容器间共享 |
| Bind Mount(绑定挂载) | 主机任意目录 | 用户手动管理 | 开发环境,热重载,访问主机文件 |
三、Volume vs Bind Mount 详解
1. Volume(数据卷)
什么是 Volume?
-
由 Docker 创建和管理的一块存储空间
-
默认存储在
/var/lib/docker/volumes/目录下 -
独立于容器的生命周期
特点:
-
✅ 由 Docker 管理,操作简单
-
✅ 支持卷驱动程序(可挂载远程存储、云存储)
-
✅ 备份和迁移方便
-
✅ 在 Linux 上性能优于 Bind Mount
-
✅ 可在多个容器之间安全共享
-
✅ 适合生产环境
本质:Volume 是 Docker 管理的宿主机目录(或远程存储)。
2. Bind Mount(绑定挂载)
什么是 Bind Mount?
-
将宿主机上的任意文件或目录直接挂载到容器内部
-
不经过 Docker 管理
特点:
-
✅ 直接访问主机文件,方便开发和调试
-
✅ 无需 Docker 管理,透明
-
⚠️ 容器内进程可能修改主机文件(安全风险)
-
⚠️ 与宿主机文件系统强耦合,不利于迁移
-
⚠️ 在非 Linux 系统(如 Docker Desktop)上性能略低
适用场景:
-
开发环境中代码热重载(修改代码 → 容器自动更新)
-
将主机配置文件提供给容器使用
-
容器需要读取主机上的日志或数据
四、Volume 操作命令
1. docker volume create —— 创建数据卷
# 语法
docker volume create [选项] [卷名]
# 示例
docker volume create my-data
docker volume create redis-data
# 使用驱动(如本地文件系统、NFS等)
docker volume create --driver local --opt type=nfs --opt o=addr=192.168.1.100,rw --opt device=:/path/to/nfs nfs-volume
# 添加标签(用于元数据)
docker volume create --label environment=prod my-data
2. docker volume ls —— 列出数据卷
# 语法
docker volume ls [选项]
# 示例
docker volume ls # 列出所有卷
docker volume ls -q # 只显示卷名
docker volume ls --filter driver=local
docker volume ls --filter label=environment=prod
docker volume ls --filter dangling=true # 列出未使用的匿名卷
3. docker volume inspect —— 查看数据卷详情
# 语法
docker volume inspect [选项] 卷名
# 示例
docker volume inspect my-data
# 输出示例:
# [
# {
# "CreatedAt": "2024-01-15T10:30:00Z",
# "Driver": "local",
# "Labels": {},
# "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
# "Name": "my-data",
# "Options": {},
# "Scope": "local"
# }
# ]
# 查看挂载点路径(方便在宿主机直接访问)
docker volume inspect my-data --format '{{.Mountpoint}}'
4. docker volume rm —— 删除数据卷
# 语法
docker volume rm [选项] 卷名
# 示例
docker volume rm my-data
# 强制删除(即使有容器正在使用)
docker volume rm -f my-data
5. docker volume prune —— 清理未使用的数据卷
# 语法
docker volume prune [选项]
# 示例
docker volume prune # 删除所有未被容器使用的卷(会提示确认)
docker volume prune -f # 强制删除,不提示
docker volume prune --filter label=environment=dev # 只删除带特定标签的
注意:prune 会删除所有未被任何容器使用的卷,操作前请确认!
五、挂载 Volume 到容器
方式一:使用 -v(或 --volume)参数
语法:
# 命名卷
-v 卷名:容器内路径[:选项]
# 匿名卷(Docker 自动生成名称)
-v 容器内路径
# 示例
-v my-data:/app/data
-v redis-data:/data:ro # ro = read-only(只读)
-v /app/uploads # 匿名卷,自动生成名称
常用选项:
| 选项 | 含义 |
|---|---|
ro |
只读挂载(容器内不能修改) |
rw |
读写挂载(默认) |
z / Z |
SELinux 相关(较少用) |
方式二:使用 --mount 参数(推荐,更清晰)
语法:
--mount type=volume,source=卷名,target=容器内路径[,选项]
示例:
# 完整格式
--mount type=volume,source=my-data,target=/app/data
# 只读挂载
--mount type=volume,source=my-data,target=/app/data,readonly
# 使用卷驱动选项
--mount type=volume,source=my-data,target=/app/data,volume-driver=local
-v vs --mount 对比
| 特性 | -v |
--mount |
|---|---|---|
| 语法简洁性 | 简单 | 稍复杂 |
| 可读性 | 较差(参数容易混淆) | 更好(键值对) |
| 功能完整性 | 基础功能 | 全部功能 |
| 推荐程度 | 适合快速命令 | 生产环境推荐 |
六、综合实战:MySQL 数据持久化
场景描述
运行一个 MySQL 容器,数据存储到 Volume 中,即使容器删除,数据依然保留。
步骤详解
步骤 1:创建 MySQL 数据卷
docker volume create mysql-data
步骤 2:运行 MySQL 容器并挂载 Volume
docker run -d \
--name mysql-prod \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=mydb \
-v mysql-data:/var/lib/mysql \
mysql:8.0
解析:
-
-v mysql-data:/var/lib/mysql:将命名卷mysql-data挂载到容器内的 MySQL 数据目录 -
MySQL 的所有数据都会写入卷中
步骤 3:验证数据写入(创建测试表)
# 进入 MySQL 容器
docker exec -it mysql-prod mysql -uroot -prootpass
# 在 MySQL 中创建测试数据
CREATE TABLE mydb.test_table (id INT, name VARCHAR(50));
INSERT INTO mydb.test_table VALUES (1, 'Hello Volume');
SELECT * FROM mydb.test_table;
EXIT;
步骤 4:删除容器(模拟故障)
docker stop mysql-prod
docker rm mysql-prod
步骤 5:重新创建容器,使用同一个 Volume
docker run -d \
--name mysql-new \
-e MYSQL_ROOT_PASSWORD=rootpass \
-v mysql-data:/var/lib/mysql \
mysql:8.0
步骤 6:验证数据依然存在
docker exec -it mysql-new mysql -uroot -prootpass -e "SELECT * FROM mydb.test_table;"
# 输出:1, Hello Volume —— 数据未丢失!
步骤 7:查看 Volume 在宿主机上的实际位置
docker volume inspect mysql-data
# 输出中的 Mountpoint 指向宿主机上的实际目录
# 可以直接在宿主机访问这些文件(需要 root 权限)
七、实战:Bind Mount 开发环境(热重载)
场景:使用 Bind Mount 实现代码热重载
假设你有一个 Flask 应用,希望在修改代码后容器自动重启(开发环境)。
目录结构:
flask-dev/
├── app.py
└── requirements.txt
步骤 1:创建应用代码
mkdir flask-dev
cd flask-dev
# 创建 app.py
cat > app.py << 'EOF'
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello! Modify me to see hot reload.'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
EOF
# 创建 requirements.txt
echo "flask==2.3.2" > requirements.txt
关键参数:
-
-v "$(pwd)":/app:将当前目录(宿主机)挂载到容器的/app -
-w /app:设置工作目录为/app
步骤 3:修改代码并观察
# 在宿主机上修改 app.py
echo "Hello World" > 替换内容,或用编辑器修改
# 刷新浏览器 http://localhost:5000,代码变化自动生效(因为 Flask debug 模式)
八、匿名卷与命名卷
匿名卷
当只指定容器内路径(不指定卷名)时,Docker 会自动创建一个匿名卷。
docker run -v /var/lib/mysql mysql
# 卷名自动生成,如 e9f7a3b2c1d4...
查看匿名卷:
docker volume ls --filter dangling=true
优缺点:
-
✅ 不需要管理卷名
-
❌ 难以复用和定位
-
❌
prune时可能被误删
命名卷
docker run -v my-mysql-data:/var/lib/mysql mysql
推荐:生产环境始终使用命名卷,便于管理和备份。
九、跨容器共享数据卷
多个容器可以同时挂载同一个 Volume,实现数据共享。
# 创建一个共享卷
docker volume create shared-data
# 容器1:写入数据
docker run -d --name writer -v shared-data:/data alpine \
sh -c "while true; do echo $(date) >> /data/shared.log; sleep 5; done"
# 容器2:读取数据
docker run -it --name reader -v shared-data:/data alpine cat /data/shared.log
十、数据卷备份与恢复
备份 Volume 数据
# 使用临时容器将 Volume 数据打包
docker run --rm \
-v mysql-data:/source \
-v $(pwd):/backup \
alpine \
tar czf /backup/mysql-backup.tar.gz -C /source .
恢复 Volume 数据
# 先创建目标卷
docker volume create mysql-restored
# 解压备份文件到新卷
docker run --rm \
-v mysql-restored:/target \
-v $(pwd):/backup \
alpine \
tar xzf /backup/mysql-backup.tar.gz -C /target
十一、常见问题与最佳实践
Q1:容器删除后 Volume 数据会丢失吗?
A:不会。除非使用 docker rm -v 删除容器时同时删除关联的卷(命名卷不会被删除),或使用 docker volume prune 清理。
Q2:如何给 Volume 添加只读限制?
A:
# -v 方式
-v my-data:/app/data:ro
# --mount 方式
--mount type=volume,source=my-data,target=/app/data,readonly
Q3:Volume 在 Windows/macOS 上的存储位置?
A:Docker Desktop 将 Volume 存储在虚拟机内部(Linux VM),不能直接在宿主机文件系统中访问(除非进入虚拟机的 /var/lib/docker/volumes/)。
Q4:Bind Mount 权限问题(Permission Denied)
A:容器内的用户(如 uid=1000)可能无法访问宿主机文件。解决方法:
# 给宿主机目录添加权限
chmod 777 /host/path # 简单但不安全
# 或使用 --user 指定与宿主机相同的 UID
docker run --user $(id -u):$(id -g) -v /host/path:/container/path ...
Q5:如何迁移 Volume 到另一台主机?
A:使用备份/恢复方法(tar 打包),或使用卷驱动程序(如 NFS、云存储驱动)。
十二、最佳实践总结
| 场景 | 推荐方案 |
|---|---|
| 生产数据库(MySQL、Redis、MongoDB) | 命名卷(docker volume create) |
| 开发环境代码热重载 | Bind Mount |
| 容器间共享数据 | 命名卷 |
| 日志文件存储 | 命名卷 |
| 配置文件(需要宿主机修改) | Bind Mount |
| 云环境/集群存储 | 卷驱动(如 Rook、Portworx) |
十三、课后练习
练习 1:Redis 数据持久化
# 1. 创建 Redis 数据卷
docker volume create redis-data
# 2. 运行 Redis 容器,使用该卷
docker run -d --name redis-prod -v redis-data:/data redis:alpine
# 3. 写入数据
docker exec -it redis-prod redis-cli set mykey hello
# 4. 删除容器并重新创建,验证数据仍在
docker stop redis-prod && docker rm redis-prod
docker run -d --name redis-new -v redis-data:/data redis:alpine
docker exec -it redis-new redis-cli get mykey # 应输出 "hello"
练习 2:使用 Bind Mount 进行前端开发
# 假设有一个 React 项目,将源码目录挂载到容器
docker run -d \
--name react-dev \
-p 3000:3000 \
-v "$(pwd)/src":/app/src \
node:18-alpine \
sh -c "cd /app && npm install && npm start"
练习 3:Volume 备份与恢复
练习将 MySQL 数据卷备份,然后恢复到新的卷中,验证数据完整性。
十四、知识点总结
数据持久化:Volume 基础
│
├── 为什么需要持久化?
│ ├── 容器删除 → 数据丢失
│ └── 需要数据独立于容器生命周期
│
├── 两种方案
│ ├── Volume(数据卷)→ 生产环境
│ │ ├── Docker 管理
│ │ ├── 支持驱动(本地/云/NFS)
│ │ └── 跨容器共享
│ └── Bind Mount → 开发环境
│ ├── 宿主机目录直接挂载
│ └── 热重载、调试
│
├── Volume 命令
│ ├── create → 创建卷
│ ├── ls → 列出卷
│ ├── inspect → 查看详情(含挂载点)
│ ├── rm → 删除卷
│ └── prune → 清理未使用卷
│
├── 挂载方式
│ ├── -v 卷名:容器路径[:选项]
│ └── --mount type=volume,source=卷名,target=路径
│
└── 实战案例
├── MySQL 持久化(命名卷)
├── 开发热重载(Bind Mount)
└── 备份与恢复
十五、下一步预告
第八天将学习:
-
Docker 网络基础(bridge、host、none 网络)
-
自定义网络和容器间的通信
-
通过容器名(而非 IP)连接其他容器
今日检查清单:
-
理解 Volume 与 Bind Mount 的区别和适用场景
-
掌握
docker volume五个基本命令 -
能使用
-v和--mount挂载卷 -
完成 MySQL 或 Redis 的持久化实战
-
了解如何备份和恢复 Volume 数据
数据持久化是 Docker 生产级应用的关键,今天的内容非常重要!如果有疑问,随时交流。明天见!
更多推荐




所有评论(0)