Docker BuildKit 无法拉取基础镜像问题分析

问题现象

构建 Docker 镜像时执行:

1
docker build -t icraft:3.36.1 .

构建刚开始便失败:

1
2
3
4
5
6
7
[+] Building ...
 => ERROR [internal] load metadata for docker.io/library/ubuntu:20.04

failed to solve:
failed to fetch anonymous token:
Get "https://auth.docker.io/token?...":
dial tcp 199.59.148.96:443: i/o timeout

错误发生在:

1
FROM ubuntu:20.04

说明 Docker 甚至还没有开始执行 Dockerfile 中的任何指令。

排查过程

检查 Docker Daemon 代理

Docker 已配置 systemd 代理:

1
systemctl show docker --property=Environment

输出:

1
2
3
Environment=HTTP_PROXY=http://127.0.0.1:10808
HTTPS_PROXY=http://127.0.0.1:10808
NO_PROXY=localhost,127.0.0.1,...

说明 Docker Daemon 已正确加载代理配置。

检查代理是否可用

使用 curl 直接通过代理访问 Docker Hub:

1
2
curl -x http://127.0.0.1:10808 \
     https://auth.docker.io/token -I

返回:

1
2
3
HTTP/1.1 200 Connection established
HTTP/2 405
Allow: GET

其中:

  • HTTP/1.1 200 Connection established 表示 HTTP CONNECT 建立成功
  • HTTP/2 405 是因为使用了 HEAD 请求,而接口只允许 GET/POST

说明:

  • HTTP Proxy 正常
  • Docker Hub 可以正常访问
  • 网络没有问题

因此可以排除:

  • DNS 问题
  • 代理失效
  • Docker Hub 无法访问

关闭 BuildKit

执行:

1
DOCKER_BUILDKIT=0 docker build ...

镜像立即构建成功。

至此可以确定:

问题仅发生在 BuildKit。

原因分析

Docker Build 实际上有两套实现。

Legacy Builder(传统 Builder)

docker build
      │
        
    dockerd
      │
        
读取 systemd 环境变量
      │
        
HTTP_PROXY / HTTPS_PROXY
      │
        
访问 Docker Hub

所有操作均由 dockerd 完成,因此能够正常继承 systemd 中配置的代理。

BuildKit

docker build
      │
        
 BuildKit
      │
        
解析 Dockerfile
      │
        
拉取基础镜像
      │
        
访问 auth.docker.io

BuildKit 是独立的构建引擎。

它拥有:

  • 独立网络实现
  • 并行下载
  • Build Cache
  • 多平台构建(buildx)
  • Rootless 支持

因此代理、DNS、网络配置与 Legacy Builder 并不完全一致。

在某些 Docker 版本(尤其 20.x 左右)以及部分 Linux 发行版中,BuildKit 对代理环境变量、镜像加速器的继承存在兼容性问题。

最终导致:

1
2
failed to fetch anonymous token
dial tcp xxx:443: i/o timeout

虽然:

  • curl 能访问
  • docker pull 能访问(部分环境)
  • Legacy Builder 能访问

但 BuildKit 自己访问 Docker Hub 却超时。

为什么关闭 BuildKit 就恢复正常?

关闭 BuildKit 后:

1
DOCKER_BUILDKIT=0 docker build

Docker 会退回使用 Legacy Builder。

整个流程重新交给 dockerd:

dockerd
    │
      
读取 systemd 代理
    │
      
HTTP_PROXY
HTTPS_PROXY
    │
      
Docker Hub

由于代理能够正确继承,因此基础镜像能够正常下载。

解决方案

方法一:临时关闭 BuildKit(推荐)

1
DOCKER_BUILDKIT=0 docker build ...

适用于偶尔构建镜像。

方法二:永久关闭 BuildKit

用户配置:

1
~/.docker/config.json
1
2
3
4
5
{
  "features": {
    "buildkit": false
  }
}

或者:

1
export DOCKER_BUILDKIT=0

写入:

1
~/.bashrc

方法三:升级 Docker

较新的 Docker(24.x、25.x、26.x)对 BuildKit 的代理继承进行了大量修复。

如果环境允许,建议升级 Docker Engine。

方法四:为 BuildKit 单独配置代理

对于必须使用 BuildKit 或 buildx 的环境,可以为 BuildKit 配置专用代理,而不是仅依赖 dockerd 的 systemd 环境变量。

这种方式适用于 CI/CD、多平台镜像构建等场景,但配置相对复杂。

总结

本次问题最终定位如下:

检查项结果
Dockerfile正常
Docker Daemon正常
HTTP Proxy正常
curl 访问 Docker Hub正常
Legacy Builder正常
BuildKit异常

根本原因并不是代理配置错误,而是当前 Docker 版本下 BuildKit 与代理环境之间存在兼容性问题。

关闭 BuildKit 后,构建重新由 dockerd 接管,能够正确继承 systemd 配置的代理,因此镜像可以正常拉取并完成构建。