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卷挂载的覆盖机制,以及混淆了镜像构建阶段的文件和容器运行时卷挂载的文件:
镜像构建文件不会自动同步到卷
你在多阶段构建的php和caddy镜像里,通过RUN命令创建的public目录及文件,是存储在镜像的文件系统中的,不属于共享卷的内容。卷挂载的覆盖逻辑
当容器启动并挂载卷到指定目录时,Docker的处理逻辑是:- 如果卷是全新的(首次创建),启动第一个容器时,Docker会把容器镜像中挂载点目录的内容复制到卷里。比如先启动
php容器,卷public就会被填充php镜像里的public/bundles内容。 - 启动第二个容器(比如
caddy)时,因为卷已经存在内容,Docker不会再把caddy镜像里的public/build复制到卷中,而是直接用现有卷的内容覆盖容器内的/srv/public/目录,所以看不到build文件夹。 - 反之,如果先启动
caddy,卷里就只有build目录,php启动后也看不到bundles目录。
- 如果卷是全新的(首次创建),启动第一个容器时,Docker会把容器镜像中挂载点目录的内容复制到卷里。比如先启动
共享卷是独立存储区域
共享卷是独立于容器镜像的存储,容器运行时对卷的修改会实时同步,但镜像构建阶段的文件只有在卷为空时才会被首次复制进去,后续容器启动不会再同步镜像里的内容。
简言之:你预期两个镜像里的public文件会合并到共享卷,但实际上只有第一个启动的容器会把自己镜像里的public内容复制到卷,后续容器启动时卷已有内容,不会再同步自身镜像的文件,所以最终只能看到其中一方的内容。
内容的提问来源于stack exchange,提问作者gremo
相关产品推荐
相关产品推荐

