容器默认的文件系统是临时的------容器删除后,其中所有数据都会丢失。对于数据库、日志、配置等需要持久化的数据,Docker 提供了三种存储方式:Volume、Bind Mount 和 tmpfs。本文将详细讲解三者的原理、适用场景以及具体操作方法,帮助你正确管理容器数据。
一、容器存储的临时性问题
Docker 容器的可写层与容器生命周期绑定:
容器运行时,所有对文件系统的修改都写入容器的可写层(位于宿主机 /var/lib/docker/overlay2/...)。
容器被删除时,可写层随之删除,数据永久丢失。
即使使用 docker commit 将容器保存为新镜像,也只能保留当时的状态,不适合频繁变化的数据。
因此,需要独立于容器生命周期的存储方案。
二、Docker 的三种存储挂载类型

三、Volume(卷)
Volume 是 Docker 推荐的持久化方式,由 Docker 完全管理,独立于容器。
3.1 基本操作
bash
# 创建卷
docker volume create mydata
# 列出卷
docker volume ls
# 查看卷详情
docker volume inspect mydata
# 删除卷(只有未被任何容器使用才能删除)
docker volume rm mydata
# 删除所有未使用的卷
docker volume prune
3.2 使用卷挂载容器
bash
# 运行容器时挂载卷(如果卷不存在,会自动创建)
docker run -d --name db -v mydata:/var/lib/mysql mysql
# 使用 --mount 语法(更显式)
docker run -d --name db --mount source=mydata,target=/var/lib/mysql mysql
# 只读挂载
docker run -d --name web --mount source=config,target=/etc/nginx/conf.d,readonly nginx
3.3 卷的备份与恢复
bash
# 备份:启动一个临时容器,挂载源卷和目标目录,打包
docker run --rm -v mydata:/source -v $(pwd):/backup alpine tar czf /backup/mydata-backup.tar.gz -C /source .
# 恢复:创建一个新卷,解压备份文件
docker volume create newdata
docker run --rm -v newdata:/target -v $(pwd):/backup alpine tar xzf /backup/mydata-backup.tar.gz -C /target
3.4 适用场景
数据库(MySQL、PostgreSQL、MongoDB)的数据目录。
应用需要持久化的用户上传文件、日志文件。
需要在多个容器之间共享数据(例如一个卷挂载到多个容器)。
四、Bind Mount(绑定挂载)
Bind Mount 将宿主机上的任意目录或文件挂载到容器中。它依赖宿主机文件系统结构,因此可移植性较差,但在开发环境中非常方便。
4.1 基本用法
bash
# 将当前目录挂载到容器的 /app
docker run -d --name dev -v $(pwd):/app node:18
# 使用 --mount 语法(推荐)
docker run -d --name dev --mount type=bind,source="$(pwd)",target=/app node:18
# 只读绑定
docker run -d --name dev --mount type=bind,source="$(pwd)",target=/app,readonly node:18
4.2 开发环境典型用法
在开发 Web 应用时,将代码目录挂载到容器内,修改代码后容器内立即生效(无需重建镜像):
bash
# 项目结构
./myapp/
├── index.html
├── app.js
└── Dockerfile
# 启动容器,挂载代码目录
docker run -d -p 3000:3000 -v $(pwd):/app node:18 npm start
4.3 注意事项
路径必须使用绝对路径:$(pwd) 或完整路径。
文件权限:容器内进程的用户 ID 可能与宿主机不同,可能导致权限错误。可以通过 --user 或调整宿主机文件权限解决。
性能:Bind Mount 在 macOS/Windows 下通过文件共享机制(如 VirtioFS、gRPC FUSE)可能比 Volume 慢,但对开发来说影响不大。
五、tmpfs 挂载
tmpfs 将数据存储在内存中,不写入磁盘。容器停止或删除后,tmpfs 中的数据会丢失。
5.1 基本用法
bash
# 挂载 tmpfs 到容器的 /tmp
docker run -d --name temp --tmpfs /tmp:rw,noexec,nosuid,size=100m alpine
# 使用 --mount 语法(仅 Linux)
docker run -d --name temp --mount type=tmpfs,destination=/tmp,tmpfs-size=100m alpine
5.2 适用场景
存放临时敏感数据(如 session、密码),避免写入磁盘。
提高频繁读写临时文件的性能(如编译缓存)。
测试环境中需要隔离文件系统但又不想持久化。
六、三种方式对比与选择

选择建议:
生产环境持久化数据 → Volume
开发环境代码热加载 → Bind Mount
临时敏感数据或缓存 → tmpfs
七、实战:MySQL 数据持久化与备份恢复
bash
`# 1. 创建数据卷
docker volume create mysql-data
# 2. 启动 MySQL 容器
docker run -d --name mysql \
-v mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
mysql:8.0
# 3. 插入测试数据
docker exec -i mysql mysql -uroot -proot <<EOF
CREATE DATABASE test;
USE test;
CREATE TABLE t(id INT);
INSERT INTO t VALUES(1);
EOF
# 4. 备份卷数据
docker run --rm -v mysql-data:/source -v $(pwd):/backup alpine \
tar czf /backup/mysql-backup.tar.gz -C /source .
# 5. 删除原容器和卷(模拟灾难)
docker stop mysql && docker rm mysql
docker volume rm mysql-data
# 6. 创建新卷并恢复
docker volume create mysql-data-new
docker run --rm -v mysql-data-new:/target -v $(pwd):/backup alpine \
tar xzf /backup/mysql-backup.tar.gz -C /target
# 7. 启动新容器验证数据
docker run -d --name mysql-new -v mysql-data-new:/var/lib/mysql mysql:8.0
docker exec -it mysql-new mysql -uroot -proot -e "SELECT * FROM test.t`"
八、常见问题与解决

九、小结
本文详细介绍了 Docker 的三种存储方式。Volume 是生产环境持久化的首选;Bind Mount 适合开发调试;tmpfs 用于临时内存数据。理解它们的特性和适用场景,能够帮助你设计出数据可靠、易于管理的容器化应用。