自在学

我们与你共同进步

  • 分类课程
  • 文章
  • 工作台
  • 订阅

  • 关于我们
  • 隐私政策
  • 使用条款

探索

  • 分类课程
  • 文章
  • 工作台
  • 订阅

网站信息

  • 关于我们
  • 隐私政策
  • 使用条款

加入社区

自在学学习社区微信二维码

微信扫码,交流学习

株洲市自在学教育科技有限公司© 2025 - 2026 版权所有

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

湘公网安备43020302000292号|湘ICP备2025148919号-1
分类课程工作台文章订阅
分类课程工作台文章价格

Docker 从入门到项目交付

  1. 01容器、Docker 与课程项目
  2. 02安装 Docker 并完成体检
  3. 03运行第一个容器
  4. 04管理镜像与容器生命周期
  5. 05用 Dockerfile 容器化任务 API
  6. 06理解构建缓存与镜像分层
  7. 07用数据卷保存状态
  8. 08让容器通过网络协作
  9. 09用 Compose 编排完整应用
  10. 10配置、健康检查与启动顺序
  11. 11用日志和检查命令排错
  12. 12标记、导出与多平台发布镜像
  13. 13建立容器安全基线
  14. 14从构建到交付的完整演练
正在加载课程章节内容
课程编程Docker 从入门到项目交付用数据卷保存状态

用数据卷保存状态

容器适合被替换。把重要数据只写在容器可写层中,会让“删除旧容器、启动新容器”变成一次数据事故。数据卷把数据生命周期从容器生命周期中分离出来。

数据卷持久化与容器网络协作


知识点:三种常见存储方式

方式由谁管理路径适合内容
容器可写层Docker临时文件、可丢弃缓存
命名数据卷Docker数据库、需要跨重建保存的数据
绑定挂载使用者源码、需要直接编辑或读取的文件

命名卷最适合 Redis 数据,因为应用只关心容器内的 /data,不需要知道数据在存储层的具体路径。

推荐使用 --mount,因为参数含义比 -v 更清楚:

text
--mount type=volume,source=course-task-data,target=/data

为什么容器可写层不适合重要数据

容器可写层的身份属于某个容器。停止再启动同一个容器时它还存在,但删除并重建后,新容器得到的是新的可写层。数据库如果把唯一数据写在这里,就会把“替换应用实例”与“删除业务数据”绑在一起。

可写层还使用镜像存储驱动的 copy-on-write 机制,适合容器运行产生的小量临时变化,不是为高写入数据库提供的最佳持久化接口。卷把 I/O 放到独立挂载中,生命周期和容器分开,也更容易备份、迁移或交给专门驱动管理。

三种挂载在容器内看起来为什么一样

无论 volume、bind 还是 tmpfs,应用看到的都是某个普通路径。差异在路径背后的所有权与生命周期:

  • volume:Docker 管理存储位置,适合持久数据与跨容器共享。
  • bind mount:直接映射指定文件系统路径,双方都可能修改,耦合路径结构。
  • tmpfs:数据放在内存中,不写入持久磁盘,容器停止后消失,适合敏感临时文件或可丢弃缓存。

选择依据应是数据语义,不是命令哪个更短。需要长期保留的数据库数据用 volume;需要实时编辑的源码用 bind;明确不该落盘的短期数据才考虑 tmpfs。

挂载会“遮住”目标目录原内容

如果镜像在 /data 中本来有文件,把卷挂到 /data 后,进程看到的是卷内容,原目录内容会被挂载遮住。删除挂载并不是删除镜像里的原文件,它们只是暂时不可见。这个行为能解释很多“镜像里明明有文件,挂载后却不见了”的问题。


实操:创建并检查数据卷

shell
$ docker volume create course-task-data
course-task-data
$ docker volume inspect course-task-data \
    --format 'name={{.Name}} driver={{.Driver}}'
name=course-task-data driver=local

结果展示

卷已经存在,但还没有被任何容器使用。local 是卷驱动名,不代表课程要依赖某个固定目录。

docker volume inspect 可能显示 Mountpoint,但不应让业务代码直接依赖这个内部路径。在 Docker Desktop 中,引擎存储还位于它管理的 Linux 环境中,外部文件管理器看到的路径并不等同于普通宿主目录。正确访问方式是把卷挂载给容器。


实操:让两个不同容器读取同一份数据

先用一个一次性容器写入文件:

shell
$ docker run --rm \
    --mount type=volume,source=course-task-data,target=/data \
    alpine:3.22 sh -c 'echo "第一条任务" > /data/tasks.txt'

这个容器已经被 --rm 删除。现在用另一个新容器读取:

shell
$ docker run --rm \
    --mount type=volume,source=course-task-data,target=/data,readonly \
    alpine:3.22 cat /data/tasks.txt
第一条任务

结果展示

写入容器和读取容器不是同一个对象,但文件仍然存在,证明数据属于卷。第二次挂载增加 readonly,读取容器无法修改内容。


知识点:卷不会自动随容器删除

这是数据持久化的优点,也是清理时必须注意的地方。查看哪些容器引用某个卷:

shell
$ docker ps -a --filter volume=course-task-data

确认不再需要数据后,才能执行 docker volume rm course-task-data。本章后面还要用它演示备份,所以现在只检查引用,不删除。这个顺序也对应真实操作:先确认使用者和备份,再做不可逆删除。

如果卷仍被容器使用,Docker 会拒绝删除。应先确认并删除相关容器,再处理卷,而不是用强制选项绕过引用关系。

docker compose down 默认保留命名卷;docker compose down --volumes 会删除该 Compose 项目的卷和其中的数据。生产数据删除前必须有备份和明确确认。

匿名卷与命名卷的差异

如果只声明目标路径而没有 source,Docker 会创建随机名称的匿名卷。它也能独立于容器存在,但难以从名字判断用途,长期使用容易留下孤立卷。课程显式命名为 task-data 或由 Compose 项目命名,是为了让归属、备份和清理都可追踪。

--rm 对匿名卷和命名卷的处理也不能混为一谈。课程采用的原则更简单:重要数据只用明确命名的卷,删除动作始终写出卷名并先确认内容。


知识点:持久化不等于已经备份

卷能让数据跨容器重建保留,但如果卷本身损坏、误删或所在设备故障,数据仍会丢失。备份必须把数据复制到独立位置,并且能验证恢复过程。

对数据库而言,优先使用数据库自身的一致性备份工具;简单文件或学习数据可以在服务停止或保证一致性的前提下,用临时工具容器打包。下面只演示卷的文件级备份机制。


实操:备份并恢复一个卷

先确保上一段的 course-task-data 仍存在并包含 tasks.txt。创建备份目录,然后用 Alpine 同时挂载数据卷和当前目录:

shell
$ mkdir -p backup
$ docker run --rm \
    --mount type=volume,source=course-task-data,target=/data,readonly \
    --mount type=bind,source="$PWD/backup",target=/backup \
    alpine:3.22 tar -czf /backup/course-task-data.tar.gz -C /data .
$ ls -lh backup/course-task-data.tar.gz

知识点在参数里得到落实:源卷以只读方式挂载,工具容器不能误改原数据;备份目录用 bind mount 映射,所以容器退出后压缩包仍在项目目录。

创建一个新卷并恢复:

shell
$ docker volume create course-task-data-restored
$ docker run --rm \
    --mount type=volume,source=course-task-data-restored,target=/data \
    --mount type=bind,source="$PWD/backup",target=/backup,readonly \
    alpine:3.22 tar -xzf /backup/course-task-data.tar.gz -C /data
$ docker run --rm \
    --mount type=volume,source=course-task-data-restored,target=/data,readonly \
    alpine:3.22 cat /data/tasks.txt
第一条任务

结果展示

恢复到新卷而不是直接覆盖原卷,能先验证备份是否可读。看到相同内容只证明这个简单文件恢复成功;真实数据库还要执行逻辑校验、版本兼容检查和业务抽样。

验证后清理恢复副本和原始练习卷:

shell
$ docker volume rm course-task-data-restored
course-task-data-restored
$ docker volume rm course-task-data
course-task-data

绑定挂载什么时候更合适

如果要把当前目录的配置文件只读挂进容器,可以这样写:

shell
$ docker run --rm \
    --mount type=bind,source="$PWD/config",target=/app/config,readonly \
    alpine:3.22 ls -la /app/config

绑定挂载依赖源路径实际存在,也会把设备目录结构暴露给容器。开发时挂载源码很方便;发布镜像时,应优先把应用文件构建进镜像,让交付物保持完整。

绑定挂载的两个常见陷阱

第一是路径耦合。同一份命令换到不同目录或另一台设备,source 可能不存在。使用 --mount 时源路径不存在会明确报错,这比 -v 在部分场景自动创建目录更容易发现拼写问题。

第二是权限映射。容器进程仍以自己的 UID/GID 访问挂载文件;“容器里用户名叫 app”不意味着外部文件系统认识这个名字。遇到 permission denied 时,应比较数字 UID/GID、文件所有者和挂载只读属性,而不是直接改成 root。


存储决策练习

为下面四种数据选择位置,并解释生命周期:

  1. 编译好的 task-api 二进制:应进入镜像,因为它属于可重建交付物。
  2. Redis 数据文件:应进入命名卷,因为它必须跨容器替换保留。
  3. 开发中实时修改的源码:适合绑定挂载,因为编辑器和容器都要访问。
  4. 只在一次请求中使用的敏感临时文件:可考虑 tmpfs,并确保应用不要求跨重启恢复。

能用生命周期解释选择,比记住“数据库用 volume”更重要。换一种数据类型时,这套判断仍然有效。


小测

1
删除一个使用命名卷的容器,会同时删除该命名卷。
上一章理解构建缓存与镜像分层下一章让容器通过网络协作