讲解

容器的文件系统是临时的:容器一删,写在里面的数据随之消失。需要持久化的数据(数据库文件、用户上传、日志)必须放到容器之外,Docker 提供两种挂载。命名卷(named volume)由 Docker 全权管理,存在 Docker 的存储目录里,用 -v 卷名:容器路径 挂载——数据库类应用的首选,性能最好、与宿主机目录结构解耦。绑定挂载(bind mount)把宿主机任意路径挂进容器,-v /宿主/路径:/容器/路径——开发时挂源码、挂配置文件用它,改了立即生效。

第三种 tmpfs 挂载只存在于内存,容器停即消失,适合敏感临时文件。现代语法推荐 --mount(键值对,表意清晰、不易写错),-v 是逗号分隔的简写,两者等价;团队里统一一种即可。

命名的细节决定命运:-v data:/var/lib/mysql 里的 data 是命名卷(不存在则自动创建);-v ./data:/var/lib/mysql 里的 ./data 是绑定挂载(相对路径基于当前目录)。混用的经典事故是「开发机好好的,服务器上数据没了」——服务器上 ./data 挂了个空目录进去,盖住了镜像里初始化的内容。另外 macOS/Windows 上绑定挂载要跨虚拟机文件系统,性能明显低于命名卷,大型项目的 node_modules 常用匿名卷回避。

示例

命名卷的创建、查看与使用:

docker volume create ohmydocs-verify-data
docker volume inspect ohmydocs-verify-data --format '卷 {{.Name}} 位于 {{.Mountpoint}}'

两个容器共享一个命名卷:第一个写入,第二个读到——数据的生命周期独立于容器:

docker run --rm -v ohmydocs-verify-data:/shared alpine:3.21 sh -c 'echo "来自写入者 $(date +%s)" > /shared/note.txt'
docker run --rm -v ohmydocs-verify-data:/shared alpine:3.21 cat /shared/note.txt

绑定挂载:把沙盒里的目录挂进容器,宿主机和容器看到同一份文件:

mkdir -p bind-demo && echo "宿主机写的配置" > bind-demo/app.conf
docker run --rm -v "$PWD/bind-demo:/etc/app:ro" alpine:3.21 cat /etc/app/app.conf

注意上面的 :ro(只读)后缀——配置文件挂载建议只读。清理卷(没有容器引用时才能删):

docker volume rm ohmydocs-verify-data

常见坑

  • 容器删了数据没了:没挂卷;数据库容器必须挂卷,这条没有例外。
  • 绑定挂载盖住镜像内容:宿主机空目录挂到容器的数据目录,镜像里初始化的文件全被遮蔽——用命名卷让 Docker 先做首次填充。
  • 权限问题:容器内进程以特定 uid 运行,写绑定挂载目录可能 permission denied;要么调整宿主机目录属主,要么在镜像里统一 uid。
  • -v 相对路径的歧义:./data 随执行目录变化,脚本里用 $PWD 或绝对路径,Compose 里则相对 compose 文件所在目录。

小结

命名卷(Docker 管理,生产首选)与绑定挂载(宿主机路径,开发好用);:ro 只读保护配置;数据不进卷等于没有数据。下一章讲配置注入:环境变量。