6月20日 11:24

Docker 数据卷怎么用?Volume、Bind Mount 和 tmpfs 有什么区别?

Docker 容器本身是“可丢弃”的:容器删了,容器可写层里的文件通常也就没了。数据卷解决的就是这个问题。它把数据放到容器生命周期之外,让数据库文件、上传文件、日志、配置等数据可以在容器重建后继续存在。

说白了,镜像负责运行环境,容器负责进程,数据卷负责把真正重要的数据留下来。

Docker 数据卷有什么用?

Docker 数据卷主要有几个作用:

  • 持久化数据:容器删除、重建、升级后,数据仍然保留。
  • 容器之间共享数据:多个容器可以挂载同一个卷,例如一个写文件、另一个读取文件。
  • 方便迁移和备份:卷可以独立备份,不必把数据混在容器可写层里。
  • 降低容器可写层压力:数据库、日志这类频繁写入的数据,不适合长期放在容器层。
  • 让运行环境和数据解耦:升级镜像时只替换应用,不动业务数据。

例如 MySQL、PostgreSQL、Redis、MinIO、Elasticsearch 这类服务,如果不把数据目录挂出来,容器一删,数据很可能也跟着消失。

Volume、Bind Mount 和 tmpfs 有什么区别?

类型数据位置是否持久化适合场景注意点
volumeDocker 管理的存储目录数据库、生产数据、需要备份的数据路径由 Docker 管理,不建议手动改内部文件
bind mount宿主机指定目录或文件本地开发、挂载配置文件、源码热更新容器可直接改宿主机文件,权限和安全要小心
tmpfs宿主机内存临时缓存、敏感临时文件容器停止后数据消失,不能用于持久数据

volume:最适合持久化业务数据

bash
docker volume create mysql-data docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=example -v mysql-data:/var/lib/mysql mysql:8

这里的 mysql-data 是一个命名卷,/var/lib/mysql 是容器里的数据库数据目录。容器删掉后,mysql-data 仍然存在。

bind mount:适合开发和明确指定宿主机路径

bash
docker run -d --name nginx -v /Users/me/site:/usr/share/nginx/html:ro nginx

这个例子把宿主机的 /Users/me/site 挂到 Nginx 的静态目录,并用 :ro 设置为只读。bind mount 很适合本地开发,但也更危险:如果挂载了 /etc/var/run/docker.sock 这类敏感路径,容器就可能影响宿主机。

tmpfs:只放临时数据

bash
docker run --tmpfs /run:rw,noexec,nosuid,size=64m nginx

它适合放运行期临时文件、缓存、敏感中间文件。容器停止后,数据就没了,所以不要把数据库、上传文件放在 tmpfs 里。

-v 和 --mount 应该用哪个?

-v 更短,适合日常快速使用:

bash
docker run -v mysql-data:/var/lib/mysql mysql:8

--mount 更清晰,适合脚本、生产环境和团队协作:

bash
docker run --mount type=volume,source=mysql-data,target=/var/lib/mysql mysql:8

新项目或生产脚本更推荐 --mount,减少误挂载的风险。

命名卷和匿名卷有什么区别?

命名卷有明确名字,方便复用、查看、备份和删除。

bash
docker volume create app-data docker run -v app-data:/data alpine docker volume ls docker volume inspect app-data

匿名卷没有手动指定名称,Docker 会生成一串 ID:

bash
docker run -v /data alpine

这种写法能持久化,但不好识别,也容易越积越多。生产环境建议优先使用命名卷,不要依赖匿名卷保存关键数据。

Docker Compose 里怎么声明数据卷?

yaml
services: db: image: postgres:16 environment: POSTGRES_PASSWORD: example volumes: - pg-data:/var/lib/postgresql/data app: image: my-app:latest depends_on: - db volumes: pg-data:

这里的 pg-data 是命名卷。执行 docker compose up -d 后,Docker 会自动创建它。

本地开发也可以用 bind mount 挂源码:

yaml
services: app: image: node:22 working_dir: /app volumes: - ./:/app command: npm run dev

这类写法适合开发环境,不建议直接照搬到生产环境。生产环境更应该把代码打进镜像,数据用命名卷或外部存储管理。

数据卷的生命周期怎么管理?

删除容器:

bash
docker rm mysql

命名卷通常不会自动删除。你需要手动删:

bash
docker volume rm mysql-data

如果创建容器时使用了匿名卷,删除容器时可以加 -v 一起删除匿名卷:

bash
docker rm -v container-name

清理所有未被容器使用的卷:

bash
docker volume prune

执行 prune 前要谨慎确认,尤其是生产服务器。只要某个卷当前没有被容器引用,就可能被清理掉。

数据卷怎么备份和恢复?

备份命名卷:

bash
docker run --rm -v mysql-data:/data:ro -v $(pwd):/backup alpine tar czf /backup/mysql-data.tar.gz -C /data .

恢复到新卷:

bash
docker volume create mysql-data-new docker run --rm -v mysql-data-new:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/mysql-data.tar.gz"

如果备份的是数据库,最好使用数据库自己的备份工具,例如 mysqldumppg_dump、物理备份工具或快照方案。直接打包数据库目录时,必须确保数据库已停止或处于一致性备份状态,否则可能得到一个不能恢复的备份。

权限问题:UID 和 GID 为什么经常出错?

挂载卷后,容器里的进程会用自己的用户身份读写文件。这个用户的 UID/GID 可能和宿主机用户不一致,于是就会出现“容器写的文件宿主机删不了”或“容器没有权限写目录”。

bash
docker exec -it app id sudo chown -R 1000:1000 ./data docker run --user 1000:1000 -v $(pwd)/data:/data my-app

数据库镜像通常有自己的用户。挂载目录时不要只看宿主机当前用户,要看镜像文档里要求的数据目录权限。

数据库使用数据卷有什么注意事项?

  • 不要多个数据库容器同时写同一个数据目录,除非数据库本身明确支持这种集群模式。
  • 不要随便跨版本复用数据目录,例如从 MySQL 5.7 直接换到 8.0,先看升级文档。
  • 不要用普通文件同步工具实时同步数据库目录,容易同步到不一致状态。
  • 不要把高频写入数据库放在性能很差的网络盘上。
  • 备份不能只靠 docker volume,还要有可验证的恢复流程。

对数据库来说,卷负责保存数据,备份策略负责保证数据能恢复。两件事不能混为一谈。

安全上要注意什么?

  • 能只读就只读,例如 :roreadonly
  • 不要把宿主机根目录、系统目录、SSH 密钥目录挂进容器。
  • 不要随便挂载 /var/run/docker.sock,这几乎等于把宿主机 Docker 控制权交给容器。
  • 生产环境避免用宽泛的 bind mount,优先使用命名卷或专门的存储方案。
  • 敏感临时数据可以考虑 tmpfs,减少落盘风险。

什么时候该选哪一种?

需求推荐方式
数据库持久化命名 volume
上传文件持久化命名 volume 或对象存储
本地开发挂源码bind mount
挂单个配置文件bind mount,尽量只读
临时缓存、运行期文件tmpfs
生产环境长期数据命名 volume、外部存储或云盘方案

Docker 数据卷的核心不是命令有多复杂,而是把数据和容器分开管理。容器可以随时删、随时重建;真正要保护的是卷里的数据。

标签:Docker