【Docker】P3 别再让你的数据“随用随弃”!Docker 存储核心指南:目录挂载与卷详解
目录

导言:那个“一次性”的 Nginx 首页
在 Docker 的世界里,容器被设计为“简洁且高效的”。它们可以被快速创建、启动、停止、分享和销毁,这带来了极大的灵活性。然而,在上一篇博文中,我们想要修改 Ngxin 的 index.html 页面文件,我们:
- 通过
docker exec命令进入 Nginx 容器内部 - 大刀阔斧地修改了
/usr/share/nginx/html/index.html,打造了一个全新的首页 - 通过
-p端口映射将 Nginx 默认的容器内部首页端口映射到外部的88端口
至此,我们成功修改了 Nginx 首页内容。但是这种方式存在一个严重的问题:如果由于任何原因(比如配置错误、版本更新)这个容器被销毁并重建了,当你再次访问时,你会发现,之前的所有修改均已丢失,这是为什么?
- 原因其实在第一篇博文中,就已隐藏了答案。“一个镜像可以开启多个容器,每个容器互相独立”,每一个容器都是一个单独的个体,销毁容器,就如同销毁了整个应用,包含应用中修改的数据。
为了解决这个数据持久化的问题,Docker 提供了强大的存储机制,允许我们将数据 持久化(persist) 在容器之外。今天,我们将深入探讨两个最核心的概念:目录挂载 和 卷存储。
目录挂载 —— “所见即所得”的开发利器
目录挂载是一种非常直接的机制。它允许你将宿主机(Host)上的一个明确路径(目录或文件)“挂载”到容器内的指定路径上。
- 核心理念: 宿主机和容器共享同一个目录。你在宿主机上的修改会立即反映在容器中,反之亦然。
如何使用目录挂载?
通过在 docker run 命令中使用 -v (volume) 标志或更现代、更清晰的 --mount 标志。
语法(两种方案皆可,没有性能差别):
docker run -v
docker run -d \
--name my-nginx \
-p 8080:80 \
-v /path/on/your/host:/path/in/container \
nginx
docker run --mount
docker run -d \
--name my-nginx \
-p 8080:80 \
--mount type=bind,source=/path/on/your/host,target=/path/in/container \
nginx
需要注意的是: 不管是内部路径(容器内部)还是外部路径(宿主机),都必须是绝对路径地址。
实例:实时编辑 Nginx 网站
让我们来解决文章开头的问题。
- 在宿主机上创建你的网站内容:
# 在你的 home 目录下创建一个目录
mkdir ~/my-nginx-content
# 创建一个首页
echo "<h1>Hello from my Host Machine!</h1>" > ~/my-nginx-content/index.html
- 使用目录挂载启动 Nginx 容器:
我们将宿主机的~/my-nginx-content目录挂载到 Nginx 容器存放网页的usr/share/nginx/html目录。
docker run -d \
--name my-dev-nginx \
-p 8080:80 \
--mount type=bind,source=$HOME/my-nginx-content,target=/usr/share/nginx/html \
nginx
$HOME变量来动态获取 home 目录的绝对路径
- 验证效果:
- 访问
http://localhost:8080,你将看到 “Hello from my Host Machine!”。 - 测试实时同步:
echo "<h1>Docker 目录挂载太棒了!</h1>" > ~/my-nginx-content/index.html - 刷新你的浏览器(无需重启容器)。页面内容立即更新了!
- 清理:
docker stop my-dev-nginx
docker rm my-dev-nginx
即使容器被删除了,你的 ~/my-nginx-content 目录和 index.html 文件仍然安全地保留在你的宿主机上。
目录挂载的优缺点
-
优点:
- 操作简单: 只需要简单的在
run命令中增加挂载命令即可实现,操作简单且易于理解。 - 共享配置: 通过在宿主机中存储,我们不仅可以实现在一个容器中挂载,甚至可以在多个容器中同时通过挂载方式共享我们的配置。
- 实时同步: 宿主机与容器内部双端实时同步,不管是从哪一端更改,双端都能迅速的自动同步。
- 操作简单: 只需要简单的在
-
缺点:
- 紧密耦合: 需要注意的是,如果还需拷贝给同事,此时需要将宿主机中的文件同
.tar文件一起传送,其与容器紧急耦合。 - 权限问题: 可能会遇到宿主机和容器内部用户的 UID/GID (用户 ID/组 ID) 不匹配导致的“Permission Denied”问题。
- 安全性: 将容器内部文件暴露在宿主机中,可能会导致一些安全性问题。
- 紧密耦合: 需要注意的是,如果还需拷贝给同事,此时需要将宿主机中的文件同
卷映射 —— “专业”的数据持久化管理员
宿主机存在一个问题,他会直接覆盖容器内指定的原始文件。如果我们只是需要在原始文件上新增更多内容,这该怎么处理?
- 核心理念: 卷映射 是由 Docker 创建和管理的存储区域。你告诉 Docker:“我需要一块叫
mysql-data的持久化空间”,然后将它附加到容器的/var/lib/mysql目录。
你不需要关心这块数据在宿主机上的具体位置。Docker 会在宿主机的一个特定目录(如 /var/lib/docker/volumes/mysql-data)中处理所有细节。
如何使用卷映射?
语法(推荐使用 --mount):
docker run -d \
--name my-database \
--mount type=volume,source=my-volume-name,target=/path/in/container \
mysql
语法(传统 -v):
# 冒号:前面是 卷 的名字,而不是宿主机路径
docker run -d \
--name my-database \
-v my-volume-name:/path/in/container \
mysql
卷映射与目录挂载的区别非常明显:
- 目录挂载:
/path/on/host(包含/,是绝对路径) - 卷映射:
my-volume-name(不包含/,只是一个名字)
实例:为 MySQL 数据库持久化数据
数据库是卷映射的完美应用场景。我们绝不希望数据库文件随容器的销毁而丢失。
- (可选)创建一个命名卷 (Named Volume): 虽然 Docker 会在需要时自动创建卷,但我们推荐先“显式”创建,更清晰。
docker volume create mysql-persistent-data - 使用卷启动 MySQL 容器: MySQL 默认将其数据存储在 /var/lib/mysql 目录。
MySQL 默认将其数据存储在 /var/lib/mysql 目录。docker run -d \ --name my-production-db \ -e MYSQL_ROOT_PASSWORD=mysecretpassword \ --mount type=volume,source=mysql-persistent-data,target=/var/lib/mysql \ mysql:8 - 卷的神奇特性:自动数据初始化
在你对比卷映射与目录挂载的时候,不仅会思考这个关键的问题:- 当我挂载一个空的 mysql-persistent-data 卷到一个非空的 /var/lib/mysql 目录(镜像中自带了默认文件)时,发生了什么?
答案是:Docker 会自动将容器镜像中 /var/lib/mysql 目录的默认内容,复制到空的卷中。
这太重要了!这意味着你的 MySQL 容器启动时,会发现 mysql-persistent-data 卷 是空的,于是它会把自己的默认数据库结构“初始化”到这个卷中。下一次启动时,它发现卷不再是空的,就会跳过初始化,直接使用现有的数据。这点是卷映射与目录挂载的本质区别。
管理你的卷
Docker 提供了一整套命令来管理这些“卷映射”:
docker volume create <name>: 创建一个卷docker volume ls: 列出所有卷docker volume inspect <name>: 查看卷的详细信息 (包括它在宿主机上的真实路径 Mountpoint)docker volume rm <name>: 删除一个卷 (前提是没有容器在使用它)docker volume prune: 删除所有“孤儿”卷 (即未被任何容器使用的卷),非常适合清理环境
卷的优缺点
优点:
- 解耦与可移植: 完美解耦。你的
docker-compose.yml只需要定义卷的名字,它在任何安装了 Docker 的机器上都能运行。 - Docker 托管: 由 Docker 管理生命周期,更安全,更易于备份、迁移和管理。
- 高性能: 在某些系统(如 Docker Desktop for Mac/Windows)上,卷映射 的 I/O 性能优于目录挂载。
- 自动初始化: 如上所述,非常适合有状态应用。
- 驱动支持: 卷不一定只存在本地,你可以使用插件驱动(Drivers)将卷存储在云上(如 AWS S3, Azure Blob)或 NFS。
缺点:
- 不易宿主机访问: 数据存储在 Docker 管理的“黑盒”路径中,从宿主机直接编辑相对麻烦(虽然可以通过
docker volume inspect找到路径)。
终极对比:目录挂载 vs. 卷映射,我该用哪个?
一张图表胜过千言万语。
| 特性 | 目录挂载 | 卷映射 |
|---|---|---|
| 存储位置 | 宿主机的任意地址,由用户指定 | 由 Docker 管理的专属路径 |
| 核心目的 | 代码同步、配置共享、开发调试 | 数据持久化、数据共享、数据备份 |
| 生命周期 | 与宿主机路径绑定,删除容器不影响主机文件 | 由 Docker 管理,删除容器后卷默认保留,需手动删除 |
| 性能 | 在非 Linux 操作系统下通过虚拟机转发,性能损失 | 所有平台上都稳定且高效、接近原生 |
| 数据初始化 | 如果挂载到空目录,会清空容器内原有目录 | 如果挂载到空卷,会复制容器内原有目录的内容到卷中 |
| 可移植性 | 差,严重依赖宿主机的目录结构,难以迁移 | 好,不依赖宿主机,可以轻松备份和迁移到其他机器 |
所以,在这么多对比下,我们可以说:如果用户在一个应用的开发阶段,建议使用目录挂载。而当这个应用已然成型,准备上生产环境,那么建议用户使用卷来统一管理。
结论:黄金法则
恭喜你,现在你已经掌握了 Docker 数据持久化的两大支柱!
记住这个简单的“黄金法则”:
- 目录挂载: 开发时,把宿主机的文件夹“借”给容器用,双向同步,方便调试。
- 卷: 生产时,让 Docker 帮你管理一个专门存数据的“仓库”,容器死了数据还在,安全可靠。
别再让你的数据“随用随弃”了。从今天起,为你的容器数据找一个安全的、持久的家!
2025.10.31 金融街
更多推荐


所有评论(0)