摘要:Docker 提供三种把宿主资源挂进容器的通道:卷由 Docker 统一管理、生命周期独立;绑定挂载指向宿主机任意路径;tmpfs 把数据放进内存。本节用可复现实验验证三者的行为差异,给出数据去向的决策表与数据库备份恢复的完整手法。
docker volume create pgdata docker run -d --name pg --mount source=pgdata,target=/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret postgres:16
写入一些数据后做关键实验:
docker exec pg psql -U postgres -c "create table t1 as select 1 as v;" docker rm -f pg docker run --rm -v pgdata:/mnt alpine ls /mnt
容器删了,挂上同一个卷再看——数据分毫未动。卷的内容住在 Docker 管理区(/var/lib/docker/volumes/pgdata/_data),不依附任何容器。这就是"容器牛牛,数据长存"的实现基础。卷还有两个加分项:可被多个容器同时挂载(共享数据的正规途径),以及卷驱动机制可以把数据放到网络存储或云盘上(NFS、云厂商卷插件),让有状态服务迈出单机。
docker run -d --name dev -v /home/me/project:/app -w /app mynode npm run dev
宿主机目录直通容器。你在编辑器里改代码,容器里的进程立刻看到——热更新开发的标配。代价也来自这份"直通":容器的行为依赖宿主机的路径布局,换台机器路径就不一定存在;性能取决于宿主文件系统(在 Docker Desktop 上还隔着虚拟机,macOS 上尤其慢);权限映射直来直去,容器内 root 写出来的文件宿主机上也是 root 属主。
卷与绑定挂载的语义差异用一个细节就能记住:挂载点若容器内原本有内容,卷会把镜像里的内容先复制进空卷,绑定挂载则直接用宿主目录遮蔽原内容。这就是数据库用绑定挂载偶尔出诡异初始化问题、用卷则一帆风顺的原因。
docker run -d --name web --tmpfs /run/session:size=64m nginx:alpine
数据写在内存页上,容器一停就蒸发。两类用途:追求极致速度的临时中间产物(编译缓存、会话数据),以及不该落盘的机密——私钥、令牌,写进 tmpfs 至少不会在磁盘上留副本。
| 维度 | 卷 | 绑定挂载 | tmpfs |
|---|---|---|---|
| 存放位置 | Docker 管理区 | 宿主机任意路径 | 内存 |
| 容器删除后 | 保留 | 保留(本来就在宿主) | 消失 |
| 迁移到别机 | 方便(卷驱动可入云) | 需要搬运路径 | 不适用 |
| 性能 | 原生文件系统 | 原生(桌面版打折) | 最快 |
| 典型数据 | 数据库、消息队列 | 开发源码、配置文件 | 会话、机密 |
一句话决策:要活过容器生命周期的生产数据用卷;开发期代码同步用绑定挂载;机密与临时态用 tmpfs。唯一不要选的选项是把数据写进可写层。
卷的数据不在容器里,但备份还得借助容器。标准手法是临时起一个容器挂载卷,把内容打包到宿主目录:
# 备份 docker run --rm -v pgdata:/data -v /home/me/backup:/backup alpine \ tar czf /backup/pgdata-$(date +%F).tar.gz -C /data . # 恢复到新卷 docker volume create pgdata2 docker run --rm -v /home/me/backup:/backup -v pgdata2:/data alpine \ tar xzf /backup/pgdata-2026-08-19.tar.gz -C /data
两条命令值得细品:备份容器同时挂了卷和宿主目录,充当数据搬运工;--rm 用完即焚不留痕迹。生产上把这两条挂进定时任务,卷数据就有了最基本的保障。更成熟的路线是卷驱动对接云盘快照,思路相同、粒度更细。
卷挂了但数据丢了:检查是不是用了匿名卷却删容器时加了 -v 参数——docker rm -v 会连带删除匿名卷(命名卷不受影响)。匿名卷是 Dockerfile 里 VOLUME 指令暗地里创建的,数据库镜像常用它,用户却毫无感知。生产一律用命名卷,拒绝匿名卷。
绑定挂载后容器内文件属主不对:容器内进程 UID 与宿主文件属主不匹配。常见解法是构建镜像时创建与宿主相同 UID 的运行用户,或用 --user 运行时指定。
同挂一个卷的两个容器写坏数据:卷提供的是共享通道不是并发控制,两个数据库实例写同一个卷必然损坏。共享只适用于"一方写一方读"或应用层自带锁的场景。
存储层收尾。下一章向上到编排层——当容器多到一屏放不下时,谁来指挥。