4 min read

Docker 镜像拉取太慢?教你配置代理彻底解决这个烦恼

大家好,今天来聊聊一个让很多开发者头疼的问题 —— Docker 镜像拉取速度慢,甚至经常超时失败。特别是在国内网络环境下,从 Docker Hub 拉取镜像简直是一场修行,动不动就超时,有时候好不容易拉了一半又断了,真的很搞心态。

不过别担心,今天我就把压箱底的配置方法分享给大家,手把手教你给 Docker 配置代理,让你告别龟速拉取的痛苦。

先说说最直接的配置方法

Docker 本身是支持代理配置的,最简单的方式就是直接修改 daemon. 文件。打开你的终端,执行:

vim /etc/docker/daemon.

然后把下面的配置贴进去:

{
  "proxies": {
    "http-proxy": "http://192.168.1.1:7890",
    "https-proxy": "http://192.168.1.1:7890",
    "no-proxy": "localhost,127.0.0.1,*.test.example.com"
  },
  "registry-mirrors": ["https://registry.docker-cn.com"]
}

这里面的 IP 地址要改成你自己代理服务器的地址和端口,registry-mirrors 可以配置国内的镜像加速器,这样双重保障。配置完记得重启 Docker 服务才能生效。

但是我踩过的坑,你可能会遇到

上面的配置理论上是可以用的,但实际操作中我发现好几个坑,都是血泪经验啊。下面我把遇到的问题和解决方法一一列出来。

问题一:docker compose pull 直接超时

刚开始配置好代理后,我兴冲冲地执行 docker compose pull,结果报错了:

Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp 108.160.170.33:443: i/o timeout (Client.Timeout exceeded while awaiting headers)

一看这个错误就知道,Docker daemon 根本没走代理,还是直连 Docker Hub。国内网络对 Docker Hub 的访问有限制,所以直连肯定超时。

解决办法是给 Docker systemd 服务配置代理。先创建配置目录:

sudo mkdir -p /etc/systemd/system/docker.service.d
sudo vi /etc/systemd/system/docker.service.d/proxy.conf

在 proxy.conf 中写入:

[Service]
Environment="HTTP_PROXY=http://172.24.48.1:7890"
Environment="HTTPS_PROXY=http://172.24.48.1:7890"
Environment="NO_PROXY=localhost,172.24.48.1"

同样,把 IP 改成你自己的代理地址。然后重新加载 systemd 配置并重启 Docker:

sudo systemctl daemon-reload
sudo systemctl restart docker

问题二:配置代理后仍然失败

以为这样就万事大吉了,结果 docker compose pull 还是报错:

Error response from daemon: Get "https://registry-1.docker.io/v2/": read tcp 172.24.48.247:60752->172.24.48.1:7890: read: connection reset by peer

这次虽然连上了代理服务器,但是连接被重置了。我当时就很纳闷,代理服务器明明在运行,为什么会这样呢?

后来用 curl 测试才发现问题:

curl -I https://registry-1.docker.io/v2/ --proxy http://172.24.48.1:7890

返回结果是:

HTTP/1.1 200 Connection established 
curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to registry-1.docker.io:443

原来 docker 相关的域名没有在代理规则里,所以走了直连。国内的代理软件一般默认都有分流规则,docker 相关的域名可能被放到了直连列表里,导致连接被重置。

解决办法就是在代理软件里强制让 docker 相关的域名走代理。以 Clash 为例,需要添加这些规则:

rules:
  - DOMAIN,registry-1.docker.io,PROXY
  - DOMAIN,auth.docker.io,PROXY
  - DOMAIN-SUFFIX,docker.com,PROXY
  - DOMAIN-SUFFIX,docker.io,PROXY

重启 Clash 后再测试,curl 和 docker compose pull 就都能成功了。

一个小优化

如果你用的是 Clash 并且经常更新订阅配置,可能会发现每次更新后自定义的 docker 规则都被清掉了。这时候可以把这些规则加到 mixins 里,这样更新订阅时规则就会自动保留。

创建一个 mixin 文件,内容大概是这样的:

module.exports.parse = ({ content, name, url }, { yaml, axios, notify }) => {
  const rules = [
    // ... 其他规则

    // docker
    "DOMAIN-SUFFIX,docker.com,PROXY",
    "DOMAIN-SUFFIX,docker.io,PROXY",
    "DOMAIN,registry-1.docker.io,PROXY",
    "DOMAIN,auth.docker.io,PROXY"
  ]
  content.rules.unshift(...rules)
  return content
}

这样每次更新订阅,docker 相关的代理规则都会自动加到规则列表的最前面,省去了手动添加的麻烦。

最后验证一下

配置完成后,用 curl 测试一下:

curl -I https://registry-1.docker.io/v2/ --proxy http://172.24.48.1:7890

如果能正常返回 200 状态码,说明代理配置没问题。然后再执行 docker compose pull,应该就能顺利拉取镜像了。

总结一下,Docker 代理配置的关键点就是:
1. 给 Docker daemon 配置代理环境变量
2. 确保代理软件的分流规则包含 docker 相关域名
3. 如果使用订阅配置,可以用 mixins 保持自定义规则

希望这篇文章能帮到大家,让你们的 Docker 开发体验提升一个台阶。有什么问题欢迎在评论区讨论,我们一起交流进步!