Docker COPY 从当前阶段复制,构建报 circular dependency
检查 COPY 所属的 FROM 与阶段依赖,分开资源来源和最终运行镜像,避免阶段自引用造成构建环。
构建在执行应用命令之前就停止,报 circular dependency detected on stage: assets。如果当前阶段的名字是 assets,其中又写了 COPY --from=assets,这条复制就在要求阶段先构建完自己,才能继续构建自己。
这里处理 BuildKit 检出的阶段自引用。错误也可能来自多个阶段组成的环,不能只看到 circular 就删掉某一条 COPY;先沿阶段名字确定真实依赖。
找到 COPY 所属的 FROM
下面的结构有问题:
FROM nginx:stable AS web
FROM registry.example.com/team/assets:release AS assets
WORKDIR /usr/src/app
COPY --from=assets /usr/src/app/public .
每个 FROM 都开始一个新阶段。最后两条指令属于 assets,并不属于上面的 web。--from=assets 指向这个阶段本身,而不是“另一台正在运行的容器”。名字是合法的,也仍然可能构成依赖环。
先从错误给出的阶段名往上找到对应 FROM,再检查它下面、下一个 FROM 之前的所有 COPY 来源。对多阶段环,还要继续跟踪来源阶段的 COPY:例如甲依赖乙、乙又依赖甲,同样无法形成可构建的顺序。
这类错误不靠清理镜像、删除缓存或重新登录仓库解决。仓库认证成功也不能使一个阶段先于自身完成。
把来源与最终运行阶段分开
若目标是让 Nginx 镜像包含另一个镜像里的静态文件,可以把来源阶段放在前面,再以 Nginx 开始最终阶段:
# syntax=docker/dockerfile:1
FROM registry.example.com/team/assets:release AS assets
FROM nginx:stable AS web
COPY --from=assets /usr/src/app/public/ /usr/share/nginx/html/
这是完整的两阶段结构示例。仓库、标签及源路径都要替换为项目实际值,来源镜像中必须确有构建好的静态文件。目标基础镜像沿用项目批准的版本;原有 Nginx 配置、用户和其他必要设置应放回 web 阶段,不要只为消除错误而丢失运行配置。
此时 web 从 assets 复制,assets 没有反向依赖 web,依赖关系才能成立。COPY 的源路径从来源阶段文件系统根目录解析,不取决于当前阶段的 WORKDIR。
不要只把别名改成一个不存在的名字。COPY --from 还支持外部镜像来源;错误别名可能让构建器转去寻找同名镜像,变成拉取失败,却没有修好原来的阶段设计。
如果真正想构建的就是 assets 镜像的派生版本,所需文件本来已经在该阶段中,可能根本不需要从自身复制。先明确产物要基于哪个镜像,再决定删除冗余 COPY 还是增加独立运行阶段,不能机械交换所有 FROM。
验证最终产物,而不只是错误消失
保留原 Dockerfile、构建参数和已验收镜像,先在隔离环境使用新标签构建。例如:
docker build --target web -t example-web:stage-check .
web 是上述最终阶段名,. 是构建上下文。真实流程若指定了 Dockerfile、平台、构建参数或远程 builder,要保留这些约束。若通过 Compose 构建,也要检查 build.target 是否仍指向旧阶段;构建默认最后阶段与显式选择 target 是两种情况。
成功标准包括:依赖图不再成环;文件来自预期版本的来源镜像;最终产物确实基于运行镜像;静态资源和原代理配置正常。COPY 成功不证明资源路径符合应用,也不证明服务启动正常。
新构建若缺文件,按实际来源路径与镜像版本继续定位,不要把宿主机同名目录换进去掩盖来源变化。候选不合格时恢复构建定义并继续使用先前镜像;本次修复不要求清空缓存、删除数据卷或变更正在运行的服务。


