JavaPub聊AI头像
关注
Docker 国内拉镜像为什么慢?从 Docker Hub 原理到国内镜像配置一次讲明白封面图

Docker 国内拉镜像为什么慢?从 Docker Hub 原理到国内镜像配置一次讲明白

在这里插入图片描述

如果你在国内使用 Docker,大概率遇到过这种场景:

docker pull nginx

然后屏幕一直停在:

Pulling from library/nginx

最后可能得到:

i/o timeout

或者:

context deadline exceeded

甚至:

TLS handshake timeout

很多人的解决方式非常直接:

找一个 Docker 国内镜像地址,复制到 daemon.json

这样确实可能解决问题。

但如果从 Docker 的角度来看,这里面其实涉及 Docker Registry、Docker Hub、镜像分层、Registry Mirror、缓存代理等一整套机制。

今天我们从底层把它讲明白。


一、docker pull 到底做了什么?

先来看最普通的一条命令:

docker pull nginx

很多初学者可能理解成:

我的电脑
   ↓
下载 nginx
   ↓
完成

实际上没有这么简单。

Docker 首先需要解析:

nginx

到底代表哪个仓库。

因为没有显式指定 Registry,所以 Docker 默认认为它来自 Docker Hub。

完整理解可以近似成:

docker.io/library/nginx:latest

其中:

docker.io

代表 Registry。

library

代表 Docker 官方镜像默认命名空间。

nginx

是镜像仓库名称。

latest

是 Tag。

所以:

docker pull nginx

实际上相当于请求:

Docker Hub
    ↓
library/nginx
    ↓
latest

这也是为什么很多 Docker 镜像代理要求你这样写:

docker pull docker.1ms.run/library/nginx:latest

而不是:

docker pull docker.1ms.run/nginx:latest

因为完整路径实际上存在一个:

library

命名空间。


二、Docker 镜像并不是一个 ZIP 文件

理解 Docker 国内镜像之前,还要理解一个非常重要的概念:

Docker Image 是分层的。

例如:

docker pull nginx

你可能看到:

a1b2c3d4: Pull complete
b2c3d4e5: Pull complete
c3d4e5f6: Pull complete
d4e5f6g7: Pull complete

这些就是不同的 Layer。

可以简单理解成:

nginx:latest
│
├── Linux 基础层
├── 系统依赖
├── nginx 程序
├── 配置文件
└── 其他文件

Docker 首先获取镜像 Manifest,然后根据里面记录的信息下载对应 Layer。

因此一次:

docker pull nginx

背后实际上会发生很多网络请求。

如果 Registry 访问不稳定,就可能出现:

某一层下载成功
某一层下载失败
某一层一直 timeout

所以你经常会看到 Docker Pull 下载到一半卡住。


三、为什么 Docker 国内镜像这么重要?

Docker Hub 是 Docker 生态最重要的公共镜像仓库之一。

比如我们平时使用的:

docker pull nginx
docker pull mysql
docker pull redis
docker pull postgres
docker pull node
docker pull python

默认基本都会访问 Docker Hub。

问题就在这里。

对于部分国内网络环境来说,访问 Docker Hub 的网络链路可能出现延迟高、连接不稳定或者超时等情况。

于是:

服务器
   │
   │ 网络请求
   ↓
Docker Hub

这条链路出现问题之后:

docker pull

自然也会跟着失败。

而 Docker Registry Mirror 的思路其实很简单:

原来:

Docker Client
     ↓
Docker Hub


加入 Mirror:

Docker Client
     ↓
Registry Mirror
     ↓
Docker Hub

如果镜像代理已经缓存过 nginx:

Docker Client
     ↓
Registry Mirror
     ↓
直接返回缓存

这样就不一定每次都需要重新访问 Docker Hub。

Docker 官方同样提供了 Registry Mirror 和 pull-through cache 机制,可以通过 Docker daemon 的 registry-mirrors 配置镜像地址。


四、一个专门整理 Docker 国内镜像的开源项目

我自己整理了一个项目:

Rodert/DockerHub

GitHub:

https://github.com/Rodert/DockerHub

这个项目做的事情并不复杂。

核心就是:

持续整理目前还能使用的 Docker 工具下载地址和 Docker Hub 公共镜像地址。

截至目前,项目中整理了多个公共入口,例如:

docker.1panel.live
docker.1ms.run
dockerproxy.net
dockerproxy.link
docker.m.daocloud.io
docker.jiaxin.site

同时项目已经把失效、需要 Token、付费或者仅限特定内网的地址排除掉,并提供 Linux、Windows、macOS 的配置方法。

为什么我觉得这种项目有必要长期维护?

因为 Docker 镜像地址不是:

配置一次,永久有效。

公共代理可能随时发生:

停止服务
限流
更换域名
网络调整
上游变化

所以相比在一篇两年前的博客里找地址,我更希望维护一个持续更新的仓库。


五、最简单的方法:直接通过镜像地址 Pull

假设原来的命令是:

docker pull nginx

可以直接通过镜像代理:

docker pull docker.1ms.run/library/nginx:latest

或者:

docker pull dockerproxy.net/library/nginx:latest

例如 Redis:

docker pull docker.1ms.run/library/redis:latest

MySQL:

docker pull docker.1ms.run/library/mysql:8.4

Node:

docker pull docker.1ms.run/library/node:22

这里有一个非常容易踩坑的地方。

Docker 官方镜像通常需要:

library/

比如:

library/nginx
library/mysql
library/redis

但如果原来的镜像本身属于某个用户或者组织:

username/myapp

就应该使用:

docker pull docker.1ms.run/username/myapp:latest

而不是:

docker.1ms.run/library/username/myapp

六、更推荐的方式:配置 registry-mirrors

如果你每天都在使用 Docker,每次写:

docker.1ms.run/library/nginx

显然非常麻烦。

Docker 提供了更方便的方法:

registry-mirrors

Linux 创建:

sudo mkdir -p /etc/docker

编辑:

sudo vim /etc/docker/daemon.json

配置:

{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://dockerproxy.net",
    "https://dockerproxy.link"
  ]
}

然后:

sudo systemctl daemon-reload

重启 Docker:

sudo systemctl restart docker

最后测试:

docker pull nginx

如果能够正常下载:

latest: Pulling from library/nginx
...
Status: Downloaded newer image for nginx:latest

说明配置已经生效。

项目 README 目前也是采用这种方式给 Linux 用户提供配置示例。


七、怎么看镜像加速有没有生效?

执行:

docker info

然后寻找:

Registry Mirrors:

例如:

Registry Mirrors:
 https://docker.1ms.run/
 https://dockerproxy.net/
 https://dockerproxy.link/

说明 Docker 已经读取到了配置。

也可以:

docker info | grep -A 10 "Registry Mirrors"

快速查看。


八、macOS 怎么配置?

如果使用的是 Docker Desktop,就不需要去修改:

/etc/docker/daemon.json

打开:

Docker Desktop

进入:

Settings
→ Docker Engine

里面通常会看到一段 JSON。

增加:

{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://dockerproxy.net"
  ]
}

注意:

如果本来已经有配置:

{
  "features": {
    "containerd-snapshotter": true
  }
}

不要整个覆盖掉。

应该合并:

{
  "features": {
    "containerd-snapshotter": true
  },

  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://dockerproxy.net"
  ]
}

然后点击:

Apply & Restart

重新测试:

docker pull nginx

即可。

Windows Docker Desktop 的配置逻辑也类似。


九、为什么我配置了镜像还是拉不下来?

这也是最常见的问题。

不要看到:

docker pull timeout

就认定是镜像地址坏了。

Docker Pull 涉及很多环节。

可以按照下面的方法排查。

首先:

docker info

确认:

Registry Mirrors

有没有读取成功。

然后:

curl -I https://docker.1ms.run

看看网络能不能连接。

再测试一个最简单的镜像:

docker pull hello-world

或者:

docker pull alpine

如果:

docker pull alpine

可以成功,但某个特殊镜像失败,那么问题未必出在 Docker Mirror。

还可能是:

镜像不存在
Tag 不存在
镜像为 Private
平台架构不支持
代理暂未同步
上游 Docker Hub 异常

十、还有一个很容易误判的问题:429

有时候:

docker pull

失败并不是网络问题。

而是:

429 Too Many Requests

Docker Hub 本身存在 Pull Rate Limit。

目前 Docker 官方文档显示,未登录用户和 Docker Personal 用户存在以 6 小时为周期的 Pull 限制;例如未认证访问按照 IP 地址计算。

所以:

Timeout

和:

429

其实完全是两个问题。

如果看到:

You have reached your pull rate limit

首先应该考虑:

docker login

而不是继续疯狂更换镜像地址。


十一、生产环境不要只依赖公共镜像

如果只是:

个人开发
学习 Docker
测试项目
临时服务器

使用公共 Docker 镜像代理通常很方便。

但是如果是:

企业生产环境
几十台服务器
Kubernetes 集群
CI/CD
高频构建

我更建议搭建自己的 Registry Cache。

例如:

开发服务器
       │
       ├─────────┐
       │         │
       ↓         ↓
   Kubernetes   CI/CD
       │         │
       └────┬────┘
            ↓
     Docker Registry Cache
            ↓
         Docker Hub

这样第一次:

docker pull nginx

访问 Docker Hub。

第二台服务器再拉:

docker pull nginx

就可以直接命中缓存。

Docker 官方同样建议通过缓存和镜像机制减少重复 Pull。


十二、甚至可以自己搭一个 Docker Hub 缓存

Docker Registry 支持:

pull-through cache

例如配置:

proxy:
  remoteurl: https://registry-1.docker.io

它的工作逻辑就是:

你的 Docker
      ↓
自己的 Registry
      ↓
缓存里有没有?
   ↙       ↘
 有          没有
 ↓            ↓
返回      Docker Hub
              ↓
             缓存
              ↓
             返回

对于几十台服务器来说,这个方案比:

所有机器分别访问 Docker Hub

更加合理。


十三、Docker Compose 同样会使用镜像加速

很多人还有一个疑问:

如果我运行:

docker compose up -d

镜像加速还有效吗?

答案是:

有效。

比如:

services:

  nginx:
    image: nginx:latest

  redis:
    image: redis:7

  mysql:
    image: mysql:8.4

执行:

docker compose up -d

Docker Compose 最终仍然需要 Docker Engine 去拉:

nginx
redis
mysql

因此只要:

registry-mirrors

已经正确配置:

docker compose pull

同样会受益。

不需要改成:

image: docker.1ms.run/library/nginx

这种写法。

这也是我更推荐修改 Docker daemon 配置,而不是修改项目 docker-compose.yml 的原因。


十四、不要把公共镜像地址写死到业务代码

例如有人会这样写:

services:

  nginx:
    image: docker.1ms.run/library/nginx:latest

个人测试没有问题。

但正式项目最好仍然保持:

services:

  nginx:
    image: nginx:1.28

为什么?

因为今天:

docker.1ms.run

可用。

不代表一年以后一定还可用。

如果写死:

第三方域名

你的项目就产生了额外依赖。

更合理的做法是:

应用代码
     ↓
nginx:1.28
     ↓
Docker Engine
     ↓
registry-mirrors

让:

业务配置

和:

基础设施配置

分开。


十五、还有一个比镜像加速更重要的问题:镜像安全

公共 Docker Mirror 很方便。

但不要忘了:

它毕竟属于你的软件供应链。

尤其生产环境,不建议随便在网上找到一个:

xxx-docker-mirror.com

就加入服务器。

对于重要服务最好:

使用可信 Registry
        +
固定镜像版本
        +
必要时固定 Digest

比如不要一直:

docker pull nginx:latest

而是:

docker pull nginx:1.28

更加严格的生产环境甚至可以使用:

nginx@sha256:xxxxxx

这样你部署的就不再只是:

某个 Tag

而是明确的镜像内容版本。


十六、一个 Docker 项目真正应该解决什么问题?

很多人认为:

Docker 国内镜像项目

就是:

整理 10 个网址

我反而觉得不是。

真正有价值的是持续维护:

Docker 官方安装入口
+
当前可用 Docker Hub 镜像
+
Linux 配置方法
+
Docker Desktop 配置方法
+
失效地址清理
+
常见问题排查

这也是我维护:

https://github.com/Rodert/DockerHub

这个项目的原因。

Docker 镜像服务的状态会随着网络环境和服务商调整不断发生变化,所以这个项目更像一个:

Docker Hub 国内访问的“可用资源索引”。

项目当前收录的公共地址也明确提醒:

地址会随着网络环境变化,应该优先选择当前可用项并合理使用。


总结

最后把 Docker 国内镜像这件事情总结成一张流程图:

docker pull nginx
        │
        ↓
Docker Engine
        │
        ↓
检查 registry-mirrors
        │
        ↓
Docker Mirror
        │
     ┌──┴──┐
     │     │
   有缓存  无缓存
     │     │
     ↓     ↓
   返回   Docker Hub
             │
             ↓
           下载
             │
             ↓
            缓存
             │
             ↓
            返回

所以 Docker 镜像加速真正改变的不是:

Docker 镜像本身

而是:

Docker 获取镜像的网络路径。

对于普通开发者来说,可以直接使用持续维护的公共 Docker 镜像。

对于团队和企业来说,更进一步则应该考虑:

Registry Mirror
        ↓
Pull-through Cache
        ↓
Harbor / 私有 Registry
        ↓
内部镜像供应链

从:

docker pull nginx

这样一条简单命令出发,背后其实已经涉及到了完整的容器镜像分发体系。

如果你最近也遇到了 Docker Hub 拉取失败的问题,可以收藏:

https://github.com/Rodert/DockerHub

里面会持续整理 Docker 国内可用镜像和相关资源。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_40374604/article/details/165287705

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--