bigywater.tech
Docker · 6

构建上下文:docker build 到底把哪些文件发给了守护进程

很多人以为 docker build 会把整个目录打包发给守护进程。BuildKit 其实只传 Dockerfile 里用到的文件,而且是增量的。这篇用一个带 50 MiB 无关文件的目录实测:到底传了多少、第二次传了多少、改一个文件传了多少,以及上下文之外的东西为什么够不着。

40 B vs 52.44 MB
同一个目录,Dockerfile 只 COPY 一个小文件与 COPY . 的上下文传输量
137 B
什么都没改时,再次构建传输的上下文大小
155 B
改了一个小文件之后,重新构建传输的上下文大小(大文件没有重传)

只传用到的文件

实测 · 本机执行输出
  [只 COPY app.txt] 退出码 0 用时 564 ms
    #1 transferring dockerfile: 76B done
    #3 transferring context: 2B done
    #4 [internal] load build context
    #4 transferring context: 40B done
  [COPY . /ctx/] 退出码 0 用时 1923 ms
    #1 transferring dockerfile: 67B done
    #3 transferring context: 2B done
    #5 [internal] load build context
    #5 transferring context: 52.44MB 0.5s done

BuildKit 的 [internal] load build context 一步,在 Dockerfile 只 COPY app.txt 时只传了 40 B,尽管目录里有 50 MiB 的随机文件;改成 COPY . 才传 52.44 MB。所以“上下文很大导致构建慢”主要发生在 COPY . 这类写法上。

增量传输

实测 · 本机执行输出
  [COPY . /ctx/ 第 2 次] 退出码 0,用时 304 ms
    #1 transferring dockerfile: 67B done
    #3 transferring context: 2B done
    #4 [internal] load build context
    #4 transferring context: 137B done
  #6 CACHED

再构建一次,上下文传输只有 137 B(我推测只是比对了文件元数据,没有验证),COPY 层 CACHED,304 ms;改了 app.txt 后传输 155 B,50 MiB 的大文件没重传。但 COPY . /ctx/ 这一层因为目录内容变了仍要重做,1450 ms。

上下文之外的世界

实测 · 本机执行输出
  [COPY . /ctx/ 改了 app.txt] 退出码 0,用时 1450 ms
    #1 transferring dockerfile: 67B done
    #3 transferring context: 2B done
    #4 [internal] load build context
    #4 transferring context: 155B done

用 docker build - 从标准输入读 Dockerfile 没有上下文,COPY 找不到文件;COPY ../secret.txt 也出不了上下文,报的是 “not found”。用 -f 可以把 Dockerfile 放在别处,上下文由最后一个目录参数决定。

没测到的

实测 · 本机执行输出
  退出码 1;#6 ERROR: failed to calculate checksum of ref <id>: "/app.txt": not found | ERROR: failed to build: failed to solve: failed to compute cache key: failed to calculate checksum of ref <id>: "/app.txt": not found

压缩传输、远程上下文(git URL)、.dockerignore 的影响(下一篇)和旧的非 BuildKit 构建器都没有在本文实验。

怎么读构建输出

实测 · 本机执行输出
  退出码 1;#6 ERROR: failed to calculate checksum of ref <id>: "/secret.txt": not found | ERROR: failed to build: failed to solve: failed to compute cache key: failed to calculate checksum of ref <id>: "/secret.txt": not found

--progress plain 输出里,transferring context: N 前面的编号对应哪一步,#3 是读取 .dockerignore,[internal] load build context 才是真正传文件的那一步。

小结

实测 · 本机执行输出
  退出码 0,用时 209 ms;#1 transferring dockerfile: 97B done

上下文的成本是“Dockerfile 用到的文件”的成本,而不是“目录有多大”。真正要避免的是 COPY . . 这种把一切都纳入的写法,以及这一层随便一个文件变化都要重建的后果。

分步演示

构建上下文
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Docker Desktop(Engine 29.8.2,容器跑在 Docker Desktop 自带的 Linux 虚拟机里,内核与宿主机的 macOS 不同);镜像为 linux/arm64;需要看虚拟机内部时用特权容器 + nsenter;计时用 time / date +%s%N,数字每次运行会有小幅抖动。构建上下文是 dtmp 下的临时目录,含一个 os.urandom 生成的 50 MiB 文件;用 docker build --progress plain 的输出读传输量;每种情形只运行一次;实验后已删除所有镜像和临时目录
    • 没有验证:旧版构建器、git/tar URL 作为上下文、.dockerignore 的影响、Docker Desktop 里宿主机到虚拟机的文件共享对上下文传输的影响、多阶段构建里 COPY --from 的传输、构建缓存被清空后的首次传输
    京ICP备2026067502号-1京公网安备11010502063487号