我所在的团队使用多阶段 Docker 文件作为我们的构建引擎,使用 Docker BuildKit 中的 自定义构建输出。我们正在使用适用于 Windows 的 Docker Desktop 和 Linux 容器。
从 Docker 4.29 开始(现在 4.30 中的行为相同),我们遇到了一个问题,只要输出目标包含子目录,构建就会失败并显示
ERROR: failed to solve: error from receiver: context canceled
。
这是一个
Dockerfile
的最小示例,它展示了该问题:
FROM alpine
WORKDIR /app
RUN mkdir test
RUN touch test/mytestfile
FROM scratch
COPY --from=0 /app .
解释
app
的目录中创建一些构建输出。特别注意,构建输出包含一个名为 test
的子目录)/app
目录中获取构建输出,并将其发送到 Docker BuildKit 的自定义导出器在上面的示例中,Docker 调用如下所示:
docker build --no-cache --output . .
预期行为
预期的行为是自定义导出器将构建输出发送到本地工作目录,并且文件
test/mytestfile
出现在本地
实际行为
目录
test
已创建,但为空。 Docker 调用自动取消,并显示上述错误消息。完整日志如下:
[+] Building 4.4s (9/9) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 143B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 1.5s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [stage-0 1/4] FROM docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b 2.0s
=> => resolve docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b 0.0s
=> => sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b 1.64kB / 1.64kB 0.0s
=> => sha256:6457d53fb065d6f250e1504b9bc42d5b6c65941d57532c072d929dd0628977d0 528B / 528B 0.0s
=> => sha256:05455a08881ea9cf0e752bc48e61bbd71a34c029bb13df01e40e3e70e0d007bd 1.47kB / 1.47kB 0.0s
=> => sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8 3.41MB / 3.41MB 1.8s
=> => extracting sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8 0.1s
=> [stage-0 2/4] WORKDIR /app 0.1s
=> [stage-0 3/4] RUN mkdir test 0.3s
=> [stage-0 4/4] RUN touch test/mytestfile 0.3s
=> [stage-1 1/1] COPY --from=0 /app . 0.0s
=> CANCELED exporting to client directory 0.0s
=> => copying files 56B 0.0s
ERROR: failed to solve: error from receiver: context canceled
我还尝试过上述的变体:
scratch
图像中显式创建目录(并为其设置权限)--output ./test
scratch
和 Docker 4.28)结果总是一样的 -
ERROR: failed to solve: error from receiver: context canceled
。
在 Docker 调用中指定
--progress=plain
不再具有启发性:
...
#9 exporting to client directory
#9 copying files 56B done
#9 CANCELED
ERROR: failed to solve: error from receiver: context canceled
如果我们删除子目录
test
(即将第三条和第四条 Docker 指令替换为 RUN touch mytestfile
),那么结果就是预期的那样 - 在本地工作目录中出现一个名为 mytestfile
的空文件。但不幸的是,在我们的实际用例中,构建输出中的子目录是不可避免的(并且不能轻易重构)。
此外,我发现的一种解决方法是在 Docker 调用中使用
type=tar
:
docker build --output type=tar,dest=out.tar .
然后生成一个具有预期目录结构的
.tar
文件:
如果可能的话,我们宁愿不使用此解决方法,因为它会通过额外的构建步骤来解压
.tar
文件,从而污染我们的构建管道。
有趣的是 - 这种行为似乎是在 Docker 4.29 中引入的(并且在 4.30 中仍然存在)。我们在发行说明中没有看到任何与此相关的明显内容,这让我想知道我们现有的设置是否以某种方式“滥用”了该功能。也许有知情人士可以透露一些信息?
这是一个错误,已在 https://github.com/docker/buildx/issues/2433 报告。请按照该问题跟踪修复进度。