说明如何识别 Docker COPY 空来源导致整个构建上下文进入镜像,并通过固定来源、dockerignore、镜像检查和凭据轮换完成止损。
Docker COPY 变量为空却复制整个构建上下文怎么办
先停止发布可疑镜像,确认 COPY 来源是否展开为空,再用显式路径、最小上下文和产物断言阻止无关文件进入镜像
直接答案:如果 Dockerfile 中的 COPY ${FILE} /app/ 在 FILE 未定义或为空时仍构建成功,不要把成功退出当作安全。2026 年 10 月 9 日的 BuildKit 上游问题报告显示,空来源可能被解释为构建上下文根目录,导致整个上下文被复制到目标目录;报告同时指出当时的 docker build --check 没有提示。先停止推送和部署该镜像,检查最终镜像内容,再把来源改成必须存在的固定路径。
先确认是不是空 COPY 来源
在仓库中查找所有使用变量的 COPY 和 ADD,同时核对 ARG 声明与 CI 的 build-arg。不要在生产节点直接重建;使用隔离分支和不含真实密钥的测试上下文。先只读检查 Buildx 版本、Dockerfile 和流水线定义。如果 ARG FILE 没有默认值,而 CI 没有传入对应构建参数,继续检查变量是否因分支保护、作用域或拼写而为空。不要打印可能包含秘密的变量值,只判断是否为空。
docker buildx version; grep -n 'COPY' Dockerfile; grep -n 'ARG' Dockerfile在调用构建前执行非空和普通文件检查。检查必须发生在 docker build 之前。把检查写进 Dockerfile 的后续 RUN 没有保护作用,因为危险的 COPY 已经执行。
test -n "${FILE:-}" || exit 2; test -f "$FILE" || exit 3怎样检查镜像是否已带入无关文件
先查看镜像层和目标目录。运行容器可能触发入口程序,因此优先创建但不启动临时容器,再导出文件系统。导出会占用本地磁盘,应使用受控临时目录并在检查后清理。
docker history --no-trunc IMAGE:TAG; docker create --name inspect-copy IMAGE:TAG; docker export inspect-copy -o image-rootfs.tar; docker rm inspect-copy; tar -tf image-rootfs.tar把镜像名和目标路径替换为实际值。若目标目录出现 Dockerfile、源码根目录、CI 配置、测试数据或本应排除的文件,镜像内容已偏离预期。镜像体积变大只能作为线索,依赖或基础镜像更新也会改变大小。还应记录镜像摘要,以便判断哪些环境拉取过同一份内容。
修复时不要依赖空变量默认行为
最稳妥的修复是删除可为空的动态来源,改用明确、稳定且必须存在的路径。构建前把选定文件复制到固定暂存目录,Dockerfile 只复制固定文件。固定路径不存在时,构建应明确失败,这比静默生成内容错误的镜像更安全。
mkdir -p .build-input; cp -- "$FILE" .build-input/artifact.binCOPY .build-input/artifact.bin /app/artifact.bincp -- 避免以连字符开头的文件名被当作选项,但不能替代路径白名单。若变量来自外部输入,应限制来源只能位于仓库允许目录,避免复制任意宿主文件。不要用一个默认的点号代替空变量,因为这会把危险行为正式化。
.dockerignore 是第二道防线
Docker 官方说明,构建上下文决定构建器能访问的文件,.dockerignore 会在发送上下文前排除匹配项。应默认排除版本控制目录、环境文件、私钥、生产配置、备份、数据库导出和本地缓存,再按需要放行唯一产物。
.git .env* *.pem *.key *.sql backup/ node_modules/实际文件应逐行填写规则。Dockerfile 专用忽略文件可能优先于根目录规则,远程 Git 上下文和命名上下文也有不同边界。不要因为配置了 .dockerignore 就继续保留空 COPY;忽略文件只能减小事故范围,不能修复错误来源。
CI 应增加哪些硬性验收
构建前检查变量非空和文件存在;构建后检查目标文件存在、类型正确,并确认代表性敏感路径不存在。当前问题现场显示内置检查可能漏报,所以 build check 可以保留,但不能作为唯一门槛。若基础镜像带有 shell,可以使用只读断言;没有 shell 时,导出临时容器文件系统检查,不要为了测试给正式镜像额外安装工具。
docker build --check --build-arg FILE="$FILE" -t app:candidate .; docker run --rm --entrypoint sh app:candidate -c 'test -f /app/artifact.bin && test ! -e /app/.env && test ! -e /app/.git'还可对镜像大小设置相对历史基线的趋势告警,但不要设置脱离项目的统一阈值。大小正常也不代表没有秘密,小文件同样可能造成严重泄露。
已推送或部署时怎么止损
先暂停发布流水线并记录受影响镜像摘要。若镜像可能包含密钥、令牌、配置或客户数据,仅删除标签不能消除已拉取副本:应按凭据类型轮换或吊销,并审查镜像仓库访问记录。随后从干净上下文重建新摘要,验证文件清单,再分批替换工作负载。回退目标应是最后一个已验证不含额外文件的镜像摘要,而不是同名可变标签。
修复不需要停机的前提是平台支持滚动替换且旧副本仍健康;单实例部署可能短暂停机。镜像重建和仓库存储会产生常规计算、流量与存储费用,金额以实际平台账单为准。若误入的是公开源码而非秘密,也要评估许可证、镜像体积和攻击面,不能只看服务是否正常。
怎样确认问题真正关闭
同时满足四项才算完成:动态来源不再为空;构建上下文不含无关敏感文件;镜像目标目录只有预期产物;部署使用新的不可变摘要。上游 Issue 当前仍为开放、待分诊状态,没有可引用的正式修复版本,因此不要写成升级某版本即可解决。发布前应再次复核 Issue、BuildKit 与 Docker Engine 的发行说明。
常见错误分支
如果 Dockerfile 明确写了 COPY 点号,复制全上下文本来就是预期语义,应缩小上下文而不是归因于空变量。如果是多阶段 COPY --from 失败,应核对阶段名和产物路径。如果只是发送上下文很慢但镜像中没有额外文件,应检查 .dockerignore 和构建入口。只有空字符串或空变量与全部文件进入目标目录同时出现,才与本问题现场一致。
相关教程
官方与上游来源
本稿未发现需要补充确认的黑鲨云业务事实。

