DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

RUN 已生成目录,Docker COPY 仍报 file not found:源文件选错了位置

RUN 生成的目录不是普通 COPY 的上下文来源。按同阶段和跨阶段选择 RUN cp 或 COPY --from,核对最终产物路径与秘密处理。

Dockerfile 在 RUN 中生成或下载了一个目录,紧接着 RUN ls 能看到它,COPY ./site /usr/share/nginx/html 却报 file not found in build context or excluded by .dockerignore。先分清目录是在构建上下文中,还是在当前构建阶段的文件系统里。

普通 COPY 的源端读取构建上下文,不读取前一个 RUN 的文件系统。同一阶段搬运文件用 RUN 中的复制命令;从其他阶段取产物用 COPY --from。这与“文件在宿主机构建上下文之外”是不同场景。

为什么 ls 看得到,COPY 却看不到

以下为错误示例:

FROM alpine:3.22
RUN mkdir -p /work/site && printf '%s\n' 'example page' > /work/site/index.html
RUN ls -l /work/site
COPY /work/site /srv/site

这里的 /work/site 是构建阶段中的路径。普通 COPY 会把源路径当作构建上下文中的路径解释;加上 / 不能让它转向当前阶段。更换 WORKDIR、关闭缓存或删除 .dockerignore,都不会改变 COPY 的源端类型。

先对照 Dockerfile 确认目录的产生位置:由前面的 RUN 产生,就沿着当前阶段检查;本来应该由宿主机或 CI 提供,则检查 build 命令最后的上下文参数、文件生成时机和忽略规则。不要仅凭错误里出现 .dockerignore 就把忽略文件全部删掉,那可能将凭据一并送进构建。

同一阶段复制目录,要包含目录内容

上面的示例可改为以下完整 Dockerfile:

FROM alpine:3.22
RUN mkdir -p /work/site && printf '%s\n' 'example page' > /work/site/index.html
RUN mkdir -p /srv/site && cp -R /work/site/. /srv/site/

它展示文件位置的关系,不是完整的网站部署镜像。实际基础镜像须有 mkdir 和 cp,应用也应具有所需读取权限。

cp -R 才能递归复制目录;源端 /work/site/. 表示目录中的内容,避免把整个 site 目录再嵌入目标。复制会覆盖同名文件,构建阶段的目标目录应是可替换的发布资源,不应混入需要保留的用户数据。

如果生成工具能够直接指定输出目录,也可以让它写到最终路径,省掉中间复制。但修改输出目录前要检查后续步骤是否仍引用原位置。

跨阶段取产物,指定阶段名和绝对源路径

不需要把构建工具留在最终镜像时,可用多阶段构建。以下仍是只展示复制关系的完整示例:

FROM alpine:3.22 AS assets
RUN mkdir -p /output && printf '%s\n' 'example page' > /output/index.html

FROM alpine:3.22
COPY --from=assets /output/ /srv/site/

assets 是明确命名的前一阶段,/output/ 从那个阶段的文件系统根解析。源文件应在该阶段已经生成。不要将当前阶段写成自己的 --from 来源,那会造成依赖循环;也不要误拼阶段名,让构建转而尝试读取一个外部镜像。

原始文件如果来自私有仓库,应使用适合构建工具的临时秘密或 SSH 转发机制,不把令牌放进仓库 URL 或 Dockerfile。修正 COPY 并不能清除已经写入旧镜像历史或构建记录的秘密,凭据暴露需要单独轮换和处置。

构建成功后再核对目录结构

修正后检查最终阶段,而不只是生成阶段:产物应位于应用实际读取的目录,不能多套一层 site,也不能仅存在于未交付的中间阶段。真实应用还需要确认读权限、静态服务根目录和运行挂载没有遮住产物。

此次改动作用于新镜像,不需要删除运行数据卷。保留旧 Dockerfile、发布配置和镜像供回退;发布新镜像涉及容器重建时安排维护窗口。成功标准是构建不再因这条 COPY 失败,最终镜像包含正确产物,应用能从预期位置提供内容。