如何让docker-compose在多阶段构建时等待子模块构建完成再构建主应用
项目结构
my-app/ ├─ my-submodule/ │ ├─ .git │ ├─ Dockerfile │ ├─ ... ├─ .git/ ├─ .gitmodules ├─ docker-compose.yml ├─ Dockerfile ├─ .dockerignore ├─ ...
需求与当前配置
my-app包含子模块my-submodule,需先编译子模块再构建主应用。采用多阶段构建:
子模块Dockerfile
FROM ubuntu AS my-submodule WORKDIR /my-submodule COPY . # 子模块编译逻辑...
主应用Dockerfile
FROM my-submodule AS my-app WORKDIR /my-app COPY . . # 主应用构建逻辑...
docker-compose.yml
version: "3.9" services: my-submodule: image: my-submodule build: context: ./my-submodule target: my-submodule my-app: build: context: . dockerfile: Dockerfile target: my-app image: my-app depends_on: - my-submodule
.dockerignore已忽略my-submodule目录,避免主应用构建时重复复制子模块内容。
报错信息
首次运行docker-compose build时出现以下错误:
=> ERROR [my-app internal] load metadata for docker.io/library/my-submodule:latest 0.4s
...
failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to create LLB definition: pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
再次运行构建正常,因为my-submodule镜像已存在本地。
问题原因
depends_on指令仅控制容器启动顺序,对docker-compose build的镜像构建顺序没有约束。首次构建时,Compose会并行尝试构建所有服务的镜像,主应用构建时本地还没有my-submodule镜像,就会尝试从Docker Hub拉取,导致拉取失败报错。
解决方案
方案1:显式指定构建顺序
每次构建时手动指定先构建子模块,再构建主应用:
docker-compose build my-submodule my-app
缺点:需要手动指定顺序,无法自动固化到配置中。
方案2:使用构建阶段的依赖(推荐)
在Compose文件的my-app服务的build配置中添加depends_on,明确指定依赖子模块的构建任务。该特性支持Compose文件版本3.7及以上:
version: "3.9" services: my-submodule: image: my-submodule build: context: ./my-submodule target: my-submodule my-app: build: context: . dockerfile: Dockerfile target: my-app # 新增:指定构建依赖 depends_on: - my-submodule image: my-app # 容器启动依赖可保留(若需要) depends_on: - my-submodule
这样docker-compose build会先完成my-submodule的镜像构建,再开始构建my-app。
方案3:合并到单个Dockerfile
将子模块的构建逻辑整合到主应用的Dockerfile中,无需依赖外部镜像:
# 第一阶段:构建子模块 FROM ubuntu AS my-submodule WORKDIR /my-submodule # 复制子模块代码(上下文为主目录,路径为./my-submodule) COPY ./my-submodule . # 子模块编译逻辑... # 第二阶段:构建主应用 FROM my-submodule AS my-app WORKDIR /my-app COPY . . # 主应用构建逻辑...
此时可以移除Compose文件中的my-submodule服务,简化配置:
version: "3.9" services: my-app: build: context: . dockerfile: Dockerfile target: my-app image: my-app
缺点:若子模块内容频繁变动,每次主应用构建都会重新触发子模块构建,可能影响构建效率。
内容的提问来源于stack exchange,提问作者Simon Tran

