Docker多阶段构建中COPY --from指令报cannot copy to non-directory错误求助
看起来你遇到的这个问题大概率是目标路径的类型冲突或者Docker BuildKit的行为变化导致的,我来帮你拆解排查一下:
先搞懂报错的本质
错误提示cannot copy to non-directory的核心意思是:你要复制的内容(源)是一个目录,但你指定的目标路径在当前构建阶段已经是一个文件,Docker没办法把目录复制到文件里,所以触发报错。
从你的错误路径/var/lib/buildkit/runc-overlayfs/cachemounts/buildkit3355209005/bin来看,问题应该出在/bin这个路径上——当前构建镜像里的/bin被识别成了文件,而非正常的目录。
具体排查步骤
检查当前构建阶段的目标路径状态
在你的COPY命令之前,加一行调试命令,看看目标路径的真实类型:# 如果你用的是 COPY --from=sqitch-build /app / RUN ls -ld /bin /lib # 如果你用的是 COPY --from=sqitch-build /app . RUN ls -ld .执行构建后看输出,如果
/bin显示是-rw-r--r--(文件标识)而不是drwxr-xr-x(目录标识),那就是问题根源——肯定是之前的构建步骤不小心把/bin改成了文件(比如误执行了RUN echo xxx > /bin这类命令)。检查sqitch-build阶段的/app结构
在sqitch-build阶段的末尾加调试命令,确认源目录的结构:# 在sqitch-build阶段的最后添加 RUN ls -la /app确保
/app/bin、/app/lib都是目录而非文件。如果/app/bin是文件,当前镜像的/bin是目录,复制整个/app时会把这个文件放到/bin目录下,这没问题;但如果/app/bin是目录,而当前镜像的/bin是文件,就会触发类型冲突报错。清理BuildKit缓存试试
有时候旧的构建缓存会残留异常状态,导致奇怪的报错。执行下面的命令清理缓存后重新构建:docker builder prune -af检查Docker版本变化
你提到之前正常最近才出问题,有可能是Docker版本更新导致BuildKit的COPY行为更严格了。比如Docker 23.x之后的版本,对源/目标类型的检查更严格,之前可能静默忽略的冲突现在直接报错。可以对比一下之前正常工作时的Docker版本,看看是不是版本更新引发的问题。
关于临时解决方案的补充
你现在用逐个复制子目录的方式能工作,是因为单独指定COPY --from=sqitch-build /app/bin /bin时,Docker会把/app/bin的内容合并到已有的/bin目录里(前提是/bin是目录);而复制整个/app到/时,Docker会尝试把/app下的每个条目直接覆盖到根目录下,如果其中某个条目和根目录下的已有文件类型冲突,就会触发报错。
备注:内容来源于stack exchange,提问作者theory

