You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何docker-compose build生成的文件会写入挂载的数据卷?

现象成因

你之前的认知遗漏了Docker卷的默认初始化规则:

  • 镜像构建阶段确实不会挂载任何运行时定义的卷,Dockerfile里所有RUN指令写入的内容只会存储在镜像分层中,不会直接写入宿主机或卷存储空间,这部分认知是正确的。
  • 但当一个完全为空的卷(包括首次创建的命名卷、刚生成的匿名卷)第一次被挂载到容器内的某一路径时,Docker会在容器启动前,自动将当前容器镜像中对应挂载路径下的所有文件、目录结构、权限属性完整复制到这个空卷中,再完成挂载操作。
  • 上述复制逻辑仅会在卷为空、首次挂载时触发一次。只要卷内已经存在内容(无论内容是初始化复制得到的,还是后续容器运行写入的),后续再将该卷挂载到任意容器的任意路径,都不会再触发复制,也不会用镜像内的文件覆盖卷内已有数据。

对应你的测试场景,执行流程是:

  1. docker-compose build阶段仅生成包含/data/timestamp文件的my-image镜像,全程没有操作卷,符合预期。
  2. 执行docker-compose up时,Docker首先创建全新的空命名卷my-volume,再并行创建两个容器。
  3. 空卷首次挂载到my-service容器的/data路径时,触发初始化复制逻辑,把my-image镜像里/data下的timestamp文件复制到my-volume中。
  4. 随后other-service容器挂载同一个my-volume到自身/data路径时,卷内已经存在timestamp文件,不会再触发复制,因此直接使用原生alpine镜像的other-service也能读到这个时间戳文件,和你观察到的输出完全一致。
VOLUME指令的实际预期行为

Dockerfile中的VOLUME指令不会在构建阶段创建或挂载任何真实卷,它的实际作用只有两点:

  • 仅在镜像元数据中添加标记,指定若干默认挂载路径。后续使用该镜像启动容器时,如果用户没有手动给这些标记路径指定挂载的卷或绑定目录,Docker会自动为这些路径创建并挂载匿名卷。
  • 被VOLUME标记的路径,和运行时手动指定卷挂载的路径遵循完全一致的卷初始化规则:即首次挂载空卷时自动复制镜像对应路径的内容到卷中,不会因为是VOLUME指令标记的路径就产生特殊行为。这也是你把RUN mkdir /data替换为VOLUME /data后现象没有变化的原因——两种写法下,容器启动时挂载空卷的初始化复制逻辑完全相同。

你可以通过一个简单测试验证这个逻辑:先删除现有my-volume卷,调整服务启动顺序让使用原生alpine镜像的other-service先挂载空的my-volume,再启动my-service,就会发现两个容器都读不到timestamp文件。原因是首次挂载空卷的是other-service,它的镜像中/data路径没有任何文件,初始化复制到卷里的就是空目录,后续my-service挂载时卷已经有内容(空目录),不会再复制镜像里的timestamp文件。


内容的提问来源于stack exchange,提问作者bimlas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:18:45