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

Docker Compose多阶段构建共享卷行为不一致问题咨询

问题:Docker Compose共享卷仅显示单个容器生成的文件

在Docker Compose多阶段构建场景下,共享同一卷并填充文件夹时存在不一致行为:并非所有文件都可见,仅显示某一个容器生成的文件(取决于容器启动顺序)。

  • 共享卷为public
  • 容器caddy将public挂载至/srv/public/
  • 容器php将public挂载至/var/www/html/public/

构建过程中,两个容器均会向各自的public目录写入内容,但最终的共享卷中仅显示caddy阶段或php阶段的文件,无法同时显示两者的文件。

某次运行中,public/build/目录缺失:

$ docker compose exec caddy ls -la public
total 12
drwxr-xr-x    3 root     root          4096 May 18 17:47 .
drwxr-xr-x    1 root     root          4096 May 18 17:40 ..
drwxr-xr-x    3 root     root          4096 May 18 17:47 bundles

php服务的输出结果一致:

$ docker compose exec php ls -la public
total 12
drwxr-xr-x    3 root     root          4096 May 18 17:47 .
drwxrwxrwt    1 www-data www-data      4096 May 18 17:47 ..
drwxr-xr-x    3 root     root          4096 May 18 17:47 bundles

Dockerfile内容

# [STAGE] php-prod
FROM php:fpm-alpine AS php
WORKDIR /var/www/html

# Create public/bundles/foo/bar.js
RUN set -eux; \
    mkdir -p public/bundles/foo; \
    touch public/bundles/foo/bar.js

# [STAGE] caddy
FROM caddy:latest as caddy
WORKDIR /srv

# Create public/build/baz.js
RUN set -eux; \
    mkdir -p public/build; \
    touch public/build/baz.js

Compose文件内容

version: "3.9"

services:
  caddy:
    build:
      context: ./
      target: caddy
    volumes:
      - public:/srv/public/

  php:
    build:
      context: ./
      target: php
    volumes:
      - public:/var/www/html/public/

volumes:
  public:

原因解释

这个问题的核心在于Docker卷挂载的覆盖机制,以及混淆了镜像构建阶段的文件和容器运行时卷挂载的文件:

  1. 镜像构建文件不会自动同步到卷
    你在多阶段构建的php和caddy镜像里,通过RUN命令创建的public目录及文件,是存储在镜像的文件系统中的,不属于共享卷的内容。

  2. 卷挂载的覆盖逻辑
    当容器启动并挂载卷到指定目录时,Docker的处理逻辑是:

    • 如果卷是全新的(首次创建),启动第一个容器时,Docker会把容器镜像中挂载点目录的内容复制到卷里。比如先启动php容器,卷public就会被填充php镜像里的public/bundles内容。
    • 启动第二个容器(比如caddy)时,因为卷已经存在内容,Docker不会再把caddy镜像里的public/build复制到卷中,而是直接用现有卷的内容覆盖容器内的/srv/public/目录,所以看不到build文件夹。
    • 反之,如果先启动caddy,卷里就只有build目录,php启动后也看不到bundles目录。
  3. 共享卷是独立存储区域
    共享卷是独立于容器镜像的存储,容器运行时对卷的修改会实时同步,但镜像构建阶段的文件只有在卷为空时才会被首次复制进去,后续容器启动不会再同步镜像里的内容。

简言之:你预期两个镜像里的public文件会合并到共享卷,但实际上只有第一个启动的容器会把自己镜像里的public内容复制到卷,后续容器启动时卷已有内容,不会再同步自身镜像的文件,所以最终只能看到其中一方的内容。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:47:51