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

Dockerfile 没写 requirements.txt,构建仍在 ONBUILD 阶段报文件缺失

检查基础镜像登记的 ONBUILD 触发指令,区分父镜像构建约定与子 Dockerfile 内容,再选择准备输入或更换基础镜像。

Dockerfile 没有写 pip install,也没有复制 requirements.txt,构建却在处理基础镜像时要求这个文件:

Executing 3 build triggers
COPY failed: .../requirements.txt: no such file or directory

如果基础镜像登记了 ONBUILD 指令,构建器会在处理下游 Dockerfile 的 FROM 时先执行这些指令。报错中的 COPY 可以来自父镜像,不一定出现在当前文件里。此时在子 Dockerfile 后面补一个生成文件的 RUN,执行时间已经太晚。

这类问题可出现在历史的 python:3-onbuild 镜像或团队自制的构建基础镜像中。旧标签只用于识别场景,不建议继续采用;当前镜像是否有触发指令,应检查实际镜像元数据,不能只看名称里有没有 onbuild。

先查看基础镜像的触发清单

确定失败阶段的 FROM 最终指向哪个镜像。多阶段构建、ARG 展开的镜像名和 CI 覆盖参数都可能改变它。对已取得的同一镜像执行只读检查,示例地址需替换:

docker image inspect \
  --format '{{json .Config.OnBuild}}' \
  registry.example.com/team/python-base:version

要检查构建实际使用的镜像版本,必要时按构建记录中的 digest 对照。镜像只存在于远程构建器、没有载入本地 Engine 时,本地 inspect 失败不表示没有 ONBUILD;应通过原有镜像获取流程取得准确版本后检查,不要悄悄换成同名的另一个本地镜像。

输出可能包含这样的指令清单:

["COPY requirements.txt /app/","RUN pip install -r /app/requirements.txt"]

这就解释了文件需求的来源。触发指令按登记顺序执行;其中任何一条失败,当前 FROM 就无法完成,后面的 WORKDIR、COPY、RUN 尚未执行。若输出为空,应继续核对构建日志、实际基础镜像和失败的另一阶段,不要把所有文件缺失都归因于 ONBUILD。

应用不需要这套构建约定时,更换基础镜像

一个只运行单个 Python 脚本、没有第三方依赖的应用,不需要为了满足无关触发器制造一份依赖清单。可以选用经过项目确认、不带这些 ONBUILD 指令的 Python 基础镜像,再显式写出应用构建步骤。例如:

FROM python:3.13-slim
WORKDIR /app
COPY helloeveryone.py ./
CMD ["python", "helloeveryone.py"]

这是无第三方依赖脚本的最小示例。Python 版本、系统发行版和镜像变体需按项目兼容性选择;不能把示例替换视为对任意旧应用的直接升级方案。原基础镜像若还提供系统库、运行用户、证书或启动脚本,也要逐项保留或有意替换。

改用普通基础镜像后,应用真实需要的安装步骤应写进自己的 Dockerfile。遗漏依赖可能让构建通过、启动却失败,因此验收不能停在原先的 COPY 错误消失。

确实要遵守父镜像约定时,准备正确的构建输入

如果该基础镜像就是团队批准的构建契约,且应用确实通过 requirements.txt 管理依赖,则应在开始构建前准备有效清单,并确认它位于触发 COPY 所要求的上下文路径,没有被忽略规则排除。

这里的文件必须来自下游构建上下文。父镜像触发 COPY requirements.txt ... 时,不会去读取稍后 RUN 才生成的文件。调整后面的 WORKDIR,或者把文件复制到运行中的容器,都不能改变这一步的输入。

空清单只有在应用确实没有依赖、且父镜像契约允许时才有意义。不要为了消除错误把真实依赖全部删掉,也不要扩大为复制整个用户目录;先查清触发清单还会执行哪些命令。

ONBUILD 不是每次启动容器都执行的入口脚本。成功执行后,这些触发器会从构建结果的触发清单中清除,不会按同一继承链自动传给下一代构建;不能依靠修改 CMD 来关闭它们。

用原构建入口复验,保留原镜像回退

保存修改前的 Dockerfile、基础镜像版本和依赖清单,再在隔离构建环境使用原来的上下文、参数和目标阶段重建。核对日志中隐含触发器的来源已明确:换用普通基础镜像时不再执行那套触发器;保留父镜像时,所需输入已正确提供。

随后检查最终镜像的应用启动、依赖导入和运行身份。不要把当前服务直接替换为仅通过构建的镜像。需要回退时恢复原构建定义并使用已验证的旧镜像;这一问题不需要删除数据卷、清理全部缓存或放宽宿主目录权限。