跳到主要内容

Docker 数据持久化与挂载

Docker 新手最容易踩的一类坑,不是容器起不来,而是“容器删了数据也没了”或者“挂载后文件怎么和我想的不一样”。

要先记住一个基本事实:

容器自身的可写层适合临时运行,不适合保存你真正重要的数据。

3 种常见存储方式

方式适合什么典型命令
容器可写层临时调试、一次性运行默认行为
named volume数据库存储、长期持久化--mount type=volume,...
bind mount本地代码、配置文件、宿主机目录共享--mount type=bind,...

我一般怎么选

选 named volume

适合:

  • 数据库数据目录
  • 缓存目录
  • 想让 Docker 接管数据路径,但不想自己管理宿主机具体目录

例如:

docker volume create postgres-data

docker run -d \
--name postgres \
-e POSTGRES_PASSWORD=secret \
--mount type=volume,src=postgres-data,target=/var/lib/postgresql/data \
postgres:16

查看数据卷:

docker volume ls
docker volume inspect postgres-data

选 bind mount

适合:

  • 把本地源码挂进容器
  • 把宿主机已有配置文件映射进去
  • 你明确希望直接看到宿主机路径

例如把当前目录挂到容器里:

docker run --rm -it \
--mount type=bind,src="$PWD",target=/workspace \
-w /workspace \
python:3.12 \
bash

如果只想读不想写:

docker run --rm -it \
--mount type=bind,src="$PWD",target=/workspace,readonly \
python:3.12 \
bash

-v--mount 怎么选

两种都能用,但我自己的默认偏好是:

  • 简单命令:-v
  • 稍复杂或需要长期维护的命令:--mount

原因不是功能差很多,而是 --mount 的字段更显式,后面回看时更容易知道自己到底挂了什么。

例如这两条本质相近:

docker run -v redis-data:/data redis:7
docker run --mount type=volume,src=redis-data,target=/data redis:7

一个常见误区:挂载会覆盖容器内同路径内容

如果你把宿主机目录挂到容器的 /app,那么容器镜像原本 /app 里的内容会被“遮住”。

例如:

docker run --rm -it \
--mount type=bind,src="$PWD",target=/app \
my-image:latest

这时容器里看到的 /app,主要就是宿主机当前目录,而不是镜像构建时写进去的那份内容。

怎么判断自己该用哪种

你可以直接按这个顺序判断:

  1. 数据是不是要长期保留
  2. 我是不是需要直接操作宿主机上的真实文件
  3. 这个路径是不是数据库、缓存或应用运行时状态目录

大多数情况下:

  • 数据目录:优先 volume
  • 代码目录、配置目录:优先 bind mount

备份和迁移 volume

最简单的思路,是临时起一个容器把数据卷打包出来:

docker run --rm \
-v postgres-data:/source \
-v "$PWD":/backup \
busybox \
tar czf /backup/postgres-data.tar.gz -C /source .

恢复时同理,把压缩包解回去。

排查挂载问题时我会先看什么

  1. 宿主机路径是否真的存在
  2. 路径类型是不是写反了,例如把文件当目录挂
  3. 挂载后是不是把镜像内原内容遮住了
  4. 容器内进程用户有没有读写权限
  5. 我到底用的是 volume 还是 bind mount

看容器实际挂载信息:

docker inspect my-container

关联阅读