Docker Volume是唯一能可靠实现容器重建后数据不丢的机制;它由Docker全权管理、路径独立、权限自动适配、支持多容器共享且跨平台可移植,而Bind Mount路径依赖强、tmpfs内存易失。

docker volume 是唯一能可靠实现容器重建后数据不丢的机制。绑定挂载(bind mount)虽然也能保留数据,但路径依赖强、权限易出错、跨平台迁移困难;tmpfs 完全在内存里,一停就清空。别信“删容器前先 docker commit”这种伪持久化——它只保存文件系统快照,不包含运行时状态,且无法还原数据库一致性。
为什么 docker volume 是默认首选
命名卷由 Docker 全权管理,路径藏在 /var/lib/docker/volumes/ 下(WSL2 内部或 HyperKit VM 中),与宿主机用户目录解耦。这意味着:
- 你不用操心路径是否存在、权限是否够(Docker 自动
chown到容器用户 UID) - 卷生命周期独立于容器:删容器不删卷,
docker volume prune才会清理未被引用的卷 - 多个服务(如 MySQL + PHP-FPM)可同时挂载同一卷,共享配置或缓存目录
-
docker compose up -d重建时,只要volumes:块里定义了同名卷,Docker 就复用已有卷,不会新建
docker-compose.yml 里怎么写才真正生效
常见错误是只取消注释 volumes 行,却漏掉顶层 volumes: 块定义。这两部分必须同时存在,否则 Compose 会创建匿名卷(删容器即丢)。
正确写法(以 Redis 为例):
services:
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
# 其他配置...
<p>volumes:
redis-data: # ← 这行不能少!没有它,redis-data 就是匿名卷
</p>
关键点:
- 服务内
volumes:的redis-data:/data是“挂载声明” - 顶层
volumes:下的redis-data:是“卷资源定义”,Docker 由此知道该创建/复用命名卷 - 如果想指定卷驱动(比如存到 NFS),可写成
redis-data: { driver: local, driver_opts: { o: bind, type: none, device: /mnt/nfs/redis } },但开发环境极少需要
重建容器时数据还在,但应用报错连不上?检查三件事
数据卷没丢 ≠ 应用能正常读写。以下问题高频出现:
-
Permission denied:Alpine 镜像默认以redis用户(UID 999)运行,但 WSL2 中卷目录可能属 root。解决方案:启动容器时加--user 999:999或改镜像USER指令 - Redis 启动失败并提示
Can't open the log file: Permission denied:因为/data下已有日志文件,但属主不是当前运行用户。临时解法:docker run --rm -v redis-data:/data alpine chown -R 999:999 /data - MySQL 报
Table 'mysql.plugin' doesn't exist:说明卷里混了旧版本数据(如从 MySQL 5.7 卷直接挂给 8.0 容器)。必须清空卷或手动升级 schema,不能硬扛
别把 bind mount 当 volume 用
有人图省事写 - ./db:/var/lib/mysql,以为这样更“直观”。实际埋雷:
- 第一次运行时,
./db是空目录,MySQL 初始化失败(因非空目录不初始化) - Windows/macOS 上,
./db路径在 WSL2 或 HyperKit 外,I/O 性能差,尤其 SQLite 或小文件多的场景 - Git 误提交
./db目录,团队成员拉代码后直接覆盖本地数据 - Compose 重命名服务名(如
mysql→db),绑定路径不变,但语义已混乱
真要调试配置文件,用 bind mount 挂单个 redis.conf 即可;数据目录一律交给 volume。
卷本身不解决数据一致性,它只是把“删容器不丢数据”这件事做到底。应用层是否能安全重启、是否支持热加载、是否要求文件锁 —— 这些得看具体服务的设计。别指望一个 docker volume create 命令能绕过 MySQL 的 mysqldump 或 Redis 的 SAVE 逻辑。











