一、学习目标

  1. 理解容器数据持久化的必要性

  2. 掌握 Volume 和 Bind Mount 的概念、区别及适用场景

  3. 学会使用 docker volume 相关命令(create、ls、inspect、rm、prune)

  4. 能够使用 -v 和 --mount 参数挂载数据卷到容器


二、为什么需要数据持久化?

容器存储的特点

默认情况下,容器内创建的所有文件都存储在容器的可写层,这带来几个问题:

  1. 数据丢失:容器删除后,容器内的数据也随之消失(如数据库数据)

  2. 性能开销:容器可写层的读写性能不如直接使用主机存储

  3. 难以共享:不同容器之间难以共享数据

核心需求:容器需要能持久化存储数据,且数据独立于容器生命周期。

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 生产级应用的关键,今天的内容非常重要!如果有疑问,随时交流。明天见!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐