如果你最近在国内使用 Docker,大概率遇到过这些情况:
dockerpull nginx然后一直:
Waiting或者:
TLS handshake timeout甚至:
context deadline exceeded还有一种更折磨人的:
有时候能拉, 有时候完全拉不下来。这时候很多教程都会告诉你:
换个 Docker 镜像源。
然后给你一份:
{"registry-mirrors":["https://xxx.xxx.com"]}复制进去。
能用最好。
不能用的时候,很多人就不知道:
到底是哪一层出了问题。
最近我整理了一个开源仓库:
DockerHub 镜像集中营项目地址:
https://github.com/Rodert/DockerHub同时还有 GitHub Pages:
https://rodert.github.io/DockerHub/这个项目目前主要做两件事情:
Docker 官方工具下载入口整理 + Docker Hub 公共镜像地址整理并分别给出了:
Windows macOS Linux Linux Rootless几种环境的配置方法。
今天不只是给大家抄几个地址。
我们顺便把一个问题彻底讲清楚:
Docker 镜像代理到底是怎么工作的?
以及:
registry-mirrors和:
docker pull 镜像代理地址/xxx到底有什么区别。
一、Docker pull 到底在拉什么?
比如我们执行:
dockerpull nginx:latest表面上只有:
nginx:latest实际上 Docker 会把它解释成类似:
docker.io/library/nginx:latest这里面包含几层信息:
docker.ioRegistry。
libraryNamespace。
nginxRepository。
latestTag。
所以完整理解:
docker.io/library/nginx:latest二、为什么 nginx 前面有 library?
很多刚学 Docker 的同学都会疑惑。
自己平时明明写:
dockerpull nginx为什么通过一些镜像代理时却要写:
dockerpull docker.1ms.run/library/nginx:latest原因就在:
library这是 Docker Hub 官方镜像使用的 Namespace。
比如:
nginx redis mysql ubuntu node golang这些官方镜像,完整路径可以理解成:
library/nginx library/redis library/mysql library/ubuntuDocker CLI 平时帮我们省略掉了:
library/所以:
dockerpull nginx实际上相当于:
docker.io/library/nginx三、个人镜像就不一样
比如:
username/myapp就不能写成:
library/myapp因为它真正的 Namespace 是:
username完整结构:
docker.io/username/myapp:latest所以通过代理地址时:
dockerpull proxy.example.com/username/myapp:latest而不是:
dockerpull proxy.example.com/library/myapp这个细节非常容易踩坑。
四、Docker Hub 国内访问慢,问题在哪?
执行:
dockerpull nginxDocker 并不是直接下载一个:
nginx.tar就结束。
背后大概会经历:
Docker CLI ↓ Docker Daemon ↓ Docker Registry ↓ 获取 Manifest ↓ 解析镜像 Layer ↓ 逐层下载 ↓ 校验 Digest ↓ 解压 ↓ 写入本地镜像存储一张 Docker 镜像:
不是一个大文件而通常由:
多个 Layer组成。
例如:
nginx ├── Layer A ├── Layer B ├── Layer C └── Config只要其中某一层:
连接超时整个:
docker pull体验就会很差。
五、这就是镜像代理存在的意义
原本:
你的服务器 ↓ Docker Hub如果链路不稳定,
可以变成:
你的服务器 ↓ 国内/可访问代理 ↓ Docker Hub代理服务器负责:
获取 Manifest 获取 Layer 缓存 Layer 转发 Registry 请求用户连接的首先变成:
镜像代理节点。六、项目目前整理了哪些公共地址?
仓库当前收录了一批:
无需付费 无需 Token 公开可使用的 Docker Hub 镜像服务。
例如:
docker.1panel.livedocker.1ms.rundockerproxy.netdockerproxy.linkdocker.m.daocloud.iodocker.jiaxin.site需要强调一点:
公共镜像地址并不是永久基础设施。
它们可能因为:
流量压力 服务器维护 网络变化 限速 运营策略随时发生变化。
所以这个仓库的意义其实不是:
找到一个永远不失效的神仙地址。
而是:
持续维护一份可以快速切换的候选列表。
七、镜像地址有两种常见使用方式
这是整篇最重要的一部分。
第一种:
直接修改 docker pull 地址。
例如原来:
dockerpull nginx:latest改成:
dockerpull docker.1ms.run/library/nginx:latest或者:
dockerpull dockerproxy.net/library/nginx:latest整个路径变成:
Docker CLI ↓ dockerproxy.net ↓ library/nginx八、这种方式最大的优点是什么?
非常简单。
不用修改 Docker 配置。
比如你只是:
临时下载一次镜像。直接:
dockerpull docker.1ms.run/library/nginx:latest即可。
特别适合排查问题。
因为如果:
dockerpull nginx失败,
但是:
dockerpull docker.1ms.run/library/nginx成功,
基本就可以确定:
Docker 本身没问题,主要是 Docker Hub 链路有问题。
九、但是直接代理地址有一个缺点
最终镜像名字可能带代理地址。
例如:
dockerimages看到:
docker.1ms.run/library/nginx而不是:
nginx某些 Docker Compose 文件:
services:nginx:image:nginx:latest仍然引用:
nginx这时候可能不如:
registry-mirrors方便。
十、第二种方式:registry-mirrors
项目里推荐的另一种方式:
修改 Docker Daemon 配置。
Linux:
/etc/docker/daemon.json例如:
{"registry-mirrors":["https://docker.1ms.run","https://dockerproxy.net","https://dockerproxy.link"]}然后:
sudosystemctl daemon-reloadsudosystemctl restartdocker以后还是:
dockerpull nginx不需要改成:
dockerpull dockerproxy.net/...Docker Daemon 会根据 Registry Mirror 配置:
尝试通过镜像服务获取。十一、两种方法可以这样理解
方法一:改镜像名
docker pull ↓ 代理 Registry ↓ Docker Hub命令直接指定:
dockerpull dockerproxy.net/library/nginx方法二:配置 registry-mirrors
用户依旧:
dockerpull nginx然后:
Docker Daemon ↓ Registry Mirror ↓ Docker Hub代理过程对上层:
更透明。
十二、哪种方法更适合小白?
我通常建议排错时:
先直接 pull。
例如:
dockerpull docker.1ms.run/library/nginx:latest如果成功,
说明:
代理本身可用。再考虑写进:
registry-mirrors。而不是一开始:
daemon.json 改五遍 Docker 重启五遍最后还不知道到底哪有问题。
十三、Linux 正确配置方式
创建目录:
sudomkdir-p/etc/docker编辑:
sudonano/etc/docker/daemon.json或者:
sudovim/etc/docker/daemon.json写:
{"registry-mirrors":["https://docker.1ms.run","https://dockerproxy.net","https://dockerproxy.link"]}保存。
重新加载:
sudosystemctl daemon-reload然后:
sudosystemctl restartdocker十四、第一件事不要急着 pull
先检查 Docker 有没有正常启动:
sudosystemctl statusdocker如果:
active (running)再继续。
十五、检查 Mirror 是否真的读取到了
执行:
dockerinfo找到:
Registry Mirrors:理想情况下应该看到:
https://docker.1ms.run/ https://dockerproxy.net/ https://dockerproxy.link/如果完全没有:
说明 Docker 没读到你的配置。
这时候一直:
dockerpull意义不大。
十六、然后再测试最简单的镜像
我建议:
dockerpull hello-world或者:
dockerpull nginx:latest而不是一上来:
dockerpull 一个几 GB 的 AI 镜像测试镜像越简单:
变量越少。十七、daemon.json 最常见的问题:JSON 写错
例如:
{"registry-mirrors":["https://docker.1ms.run",]}注意最后:
多了一个逗号。JSON 不允许:
trailing comma。Docker 可能直接:
启动失败。十八、怎么检查 JSON?
如果机器安装:
jq可以:
jq./etc/docker/daemon.json正确会格式化输出。
错误会直接:
parse error。这个方法非常实用。
十九、Docker 重启失败怎么办?
执行:
sudosystemctl statusdocker还不够,
继续:
sudojournalctl\-udocker\--no-pager\-n50经常可以看到:
invalid characterunable to configureinvalid mirror等真正错误。
所以:
不要看到 Docker 起不来就重装。
先看日志。
二十、一个比较完整的 Linux 排错流程
可以直接保存:
dockerversion第一步:
dockerinfo第二步:
cat/etc/docker/daemon.json第三步:
jq./etc/docker/daemon.json第四步:
sudosystemctl restartdocker第五步:
sudosystemctl statusdocker第六步:
dockerpull nginx:latest如果失败,
再:
dockerpull\docker.1ms.run/library/nginx:latest这样很容易定位:
Docker 问题 配置问题 Mirror 问题 网络问题到底是哪一个。
二十一、Docker Desktop 怎么配置?
Windows 和 macOS 使用:
Docker Desktop时,一般不建议直接找系统里的:
daemon.json因为 Docker Desktop 底层实际上运行:
Linux VM或者对应虚拟化环境。
直接:
Docker Desktop ↓ Settings ↓ Docker Engine修改 JSON。
二十二、比如原来的配置是
{"builder":{"gc":{"defaultKeepStorage":"20GB","enabled":true}},"experimental":false}千万不要直接全部删掉,
改成:
{"registry-mirrors":["https://docker.1ms.run"]}更合理的是:
合并字段。
例如:
{"builder":{"gc":{"defaultKeepStorage":"20GB","enabled":true}},"experimental":false,"registry-mirrors":["https://docker.1ms.run","https://dockerproxy.net"]}二十三、然后点击
Apply & Restart等待 Docker Desktop 重启。
重新打开 Terminal:
dockerinfo检查:
Registry Mirrors然后:
dockerpull nginx二十四、Windows Server 又不一样
如果你使用:
Windows Server Docker Engine而不是 Docker Desktop,
配置可能在:
C:\ProgramData\docker\config\daemon.json修改以后:
Restart-Servicedocker再:
docker info这和普通 Windows Docker Desktop:
不是一回事。
很多教程把二者混在一起,
导致用户找半天配置文件。
二十五、Rootless Docker 又是一个坑
如果使用:
Rootless Docker它通常不会读取:
/etc/docker/daemon.json而是当前用户:
~/.config/docker/daemon.json创建:
mkdir-p~/.config/docker编辑:
vim~/.config/docker/daemon.json写:
{"registry-mirrors":["https://docker.1ms.run"]}然后:
systemctl--userrestartdocker注意:
不是 sudo systemctl restart docker而是:
systemctl --user二十六、为什么有时候配置了多个 Mirror 还是失败?
因为:
registry-mirrors不是:
高可用 CDN 魔法。
不同镜像服务可能:
缓存策略不同 支持镜像范围不同 限流不同 访问链路不同 同步状态不同某一个地址:
今天可以不代表:
明天永远可以。所以仓库才会:
持续维护多个候选地址。二十七、不要一次配置十几个地址
看起来:
越多越稳。实际未必。
如果里面:
大部分已经失效。反而可能:
增加排错复杂度。我更建议:
2~3 个已验证可用地址足够。
定期测试。
失效就:
替换。二十八、怎么快速测试一个镜像代理?
最简单:
dockerpull\dockerproxy.net/library/hello-world:latest或者:
dockerpull\docker.1ms.run/library/nginx:latest如果连:
hello-world都无法获取,
基本没必要继续拿它:
拉大镜像。二十九、也可以先测试 HTTP
例如:
curl-Ihttps://dockerproxy.net但是要注意:
HTTP 首页能打开,不代表 Docker Registry API 一定正常。
因为 Docker 拉镜像涉及:
Registry API Manifest Blob 鉴权 Redirect多个流程。
所以最终还是:
dockerpull最有代表性。
三十、Docker 镜像拉取常见错误怎么判断?
1. context deadline exceeded
例如:
net/http: request canceled while waiting for connection常见方向:
网络链路 DNS 代理 Registry 不可访问2. TLS handshake timeout
说明:
连接建立阶段就卡住。
优先看:
网络 代理 证书 Registry 可达性3. manifest unknown
这和:
网络不一定有关。
例如:
dockerpull nginx:not-exist-tag就会:
manifest unknown。因为这个 Tag:
不存在。三十一、再比如 unauthorized
如果:
private image没有登录:
dockerpull username/private-image可能:
unauthorized这时候换:
10 个镜像源也没用。
因为真正问题:
权限。
应该:
dockerlogin而不是:
疯狂换代理。三十二、429 又是什么?
例如:
Too Many Requests说明:
限流。可能:
Docker Hub 限流也可能:
公共代理服务限流。公共镜像毕竟:
不是无限带宽。这也是为什么公共服务应该:
合理使用。
三十三、镜像下载到一半失败怎么办?
Docker 的 Layer 是:
内容寻址。很多已经下载并校验完成的 Layer:
不一定需要全部重新来一遍。再次:
dockerpull nginxDocker 会根据本地 Layer 状态继续处理。
这也是分层镜像:
Layer Cache的优势之一。
三十四、为什么很多镜像看起来下载好多份?
例如:
Downloading Extracting Pull complete因为:
Docker Image不是单文件。
可以理解:
Image ├── Base OS Layer ├── Runtime Layer ├── Dependency Layer └── Application Layer每一层:
都有自己的 Digest。三十五、Digest 是什么?
比如:
sha256:xxxxxxx本质上:
内容哈希。它帮助 Docker:
校验 Layer 识别重复 Layer 复用缓存 保证内容一致性所以镜像代理并不是:
偷偷重新制作一个 nginx。正常情况下,它代理的是:
同一套 Registry 内容。
最终还要通过:
Digest进行内容校验。
三十六、所以镜像代理和“重新打包镜像”不是一回事
这两件事情一定要区分。
镜像代理:
Docker Hub ↓ Proxy ↓ Client主要改变:
下载路径。重新打包:
原镜像 ↓ 重新 build / 修改 ↓ 另一张镜像这时候:
Digest都会变化。
安全语义:
完全不同。
三十七、企业环境最好怎么做?
如果只是:
个人开发 小团队公共镜像代理非常方便。
但是企业生产环境,
更建议:
Docker Hub ↓ 企业 Registry / Pull-through Cache ↓ 内部服务器例如:
Harbor Registry Cache Nexus Artifactory之类架构。
这样可以:
内部缓存 权限控制 审计 镜像扫描 稳定性控制不依赖:
随机公共节点。三十八、为什么企业更应该缓存?
假设有:
100 台服务器全部拉:
node:22如果每台:
都去公网重新拉。浪费:
带宽 时间 外部请求额度。有内部 Cache:
第一次 ↓ Docker Hub ↓ 内部 Registry 后面 99 台 ↓ 内部 Registry效率会高很多。
三十九、Docker Compose 同样会受到镜像问题影响
比如:
services:mysql:image:mysql:8.4redis:image:redis:7nginx:image:nginx:latest执行:
dockercompose up-d实际第一步:
检查本地镜像。如果没有:
自动 pull。所以你看到:
Docker Compose 一直卡住很可能不是:
Compose 本身有 Bug。而是:
背后的 image pull 卡住。
四十、怎么验证?
先单独:
dockerpull mysql:8.4dockerpull redis:7dockerpull nginx:latest全部成功以后:
dockercompose up-d如果这时候秒起,
说明:
之前卡的是镜像下载。这是很实用的排错技巧。
四十一、CI/CD 也是一样
例如 GitHub Actions、Jenkins、自建 Runner:
Build ↓ docker pull ↓ docker build ↓ docker push如果基础镜像:
FROM golang:1.25拉不到,
后面:
Go 编译根本都没开始。
日志却可能看起来:
Docker Build 失败。实际上是:
Base Image Retrieval 失败。
四十二、所以排 Docker Build 也要先看 FROM
例如:
FROM node:22-alpine先测试:
dockerpull node:22-alpine如果这一步都失败,
那:
RUN npm install根本不是重点。
这个思路能省大量时间。
四十三、项目本身为什么用了 GitHub Pages?
这个仓库并不是一个复杂后端项目。
结构目前非常简单:
DockerHub ├── README.md ├── index.html ├── assets └── .github/workflows/pages.yml也就是说:
它本质上是一份持续维护的资源索引。
四十四、index.html 做什么?
项目同时准备:
index.html用于把:
Docker 工具下载 镜像地址 配置示例直接做成一个简单网页。
用户不一定:
打开 GitHub 看 README。也可以访问:
GitHub Pages。四十五、Pages 又是怎么自动发布的?
仓库里的:
.github/workflows/pages.yml使用 GitHub Actions。
主要流程:
push main ↓ Checkout ↓ Configure Pages ↓ Upload Artifact ↓ Deploy Pages例如:
on:push:branches:-main意味着:
main 分支更新后:
自动触发网页部署。四十六、这个思路其实非常适合做“持续维护型资料库”
比如:
Docker 镜像 AI API 地址 开源软件下载 开发者工具导航 模型价格这些资料有一个共同特点:
经常变化。
如果只是发一篇博客:
2026 年写完 ↓ 半年以后大量失效。而 GitHub 仓库:
持续 Commit ↓ README 更新 ↓ Pages 自动上线更适合。
四十七、这也是这个项目真正值得借鉴的地方
它不是:
代码越复杂越好。而是:
解决一个高频实际问题 + 持续更新数据 + 给用户一个固定入口比如用户以后只需要记:
https://github.com/Rodert/DockerHub镜像地址变化:
仓库维护者更新。用户不用重新搜索:
几十篇过期博客。四十八、如果让我继续升级这个项目,我会加什么?
第一:
镜像状态自动检测。
例如维护:
mirrors:-name:1msurl:https://docker.1ms.run-name:dockerproxyurl:https://dockerproxy.netGitHub Action:
每 6 小时 ↓ 测试 ↓ 更新 Status最终网页:
🟢 可用 🟡 较慢 🔴 不可用这个价值会非常大。
四十九、甚至可以写一个检测脚本
例如 Bash:
#!/bin/bashMIRRORS=("docker.1ms.run""dockerproxy.net""dockerproxy.link")formirrorin"${MIRRORS[@]}";doecho"Testing$mirror"start=$(date+%s%3N)iftimeout20\dockermanifest inspect\"$mirror/library/nginx:latest"\>/dev/null2>&1thenend=$(date+%s%3N)echo"OK$((end-start))ms"elseecho"FAILED"fidone定时运行。
五十、为什么用 manifest inspect 测试很合适?
因为:
dockerpull真的会下载全部 Layer。
如果每隔 10 分钟:
拉几十个镜像成本太高。
而:
dockermanifest inspect主要验证:
Registry Manifest 基础访问链路更适合:
健康检查。
五十一、再进一步可以生成 mirrors.json
例如:
[{"name":"1ms","url":"docker.1ms.run","status":"online","latency":236},{"name":"dockerproxy","url":"dockerproxy.net","status":"online","latency":410}]前端:
fetch("./mirrors.json")自动显示。
这样项目就从:
静态地址列表升级成:
Docker Mirror Status Page。
五十二、甚至可以按地区测试
比如:
北京 上海 广州 香港 新加坡分别跑检测节点。
得到:
Mirror A 北京 120ms 上海 90ms 广州 Timeout用户就能:
根据网络地区选择。这会比单纯:
给 10 个地址更有价值。
五十三、还可以自动生成 daemon.json
用户在网页选择:
☑ 1ms ☑ dockerproxy页面自动生成:
{"registry-mirrors":["https://docker.1ms.run","https://dockerproxy.net"]}点击:
复制。然后分别提供:
Linux Windows macOS Rootless操作说明。
项目体验会非常完整。
五十四、Docker 镜像问题最终应该这样排查
如果:
dockerpull nginx失败,
不要马上:
重装 Docker。按照下面顺序:
1. docker versionDocker 是否正常。
2. docker infoDaemon 是否正常,Mirror 是否生效。
3. 直接访问代理地址拉取确认代理是否可用。
4. 检查 daemon.json确认 JSON 正确。
5. 查看 Docker 日志确定是否配置问题。
6. 判断错误类型是网络、Tag、权限还是限流。
五十五、一个最终可保存的 Linux 配置
例如:
{"registry-mirrors":["https://docker.1ms.run","https://dockerproxy.net","https://dockerproxy.link"]}保存:
/etc/docker/daemon.json然后:
sudosystemctl daemon-reloadsudosystemctl restartdocker检查:
dockerinfo测试:
dockerpull nginx:latest如果还失败,
直接:
dockerpull\docker.1ms.run/library/nginx:latest做对比。
这个流程基本能解决:
大部分“小白只知道 Docker 拉不下来,但不知道问题在哪”的情况。
总结
Rodert/DockerHub这个项目本身并不复杂。
它解决的是一个特别实际的问题:
Docker Hub 地址不稳定时,到哪里快速找到仍可测试的公共镜像,并且知道 Windows、macOS、Linux 应该怎么配置。
但如果只是收藏几个镜像地址,
其实还不够。
真正值得理解的是背后的关系:
docker pull nginx ↓ docker.io/library/nginx ↓ Registry ↓ Manifest ↓ Layers ↓ 本地 Docker Image加入 Mirror 后:
Docker Client ↓ Docker Daemon ↓ Registry Mirror ↓ Docker Hub ↓ Manifest / Layers ↓ 本地缓存而:
registry-mirrors解决的是:
让 Docker Daemon 在不改变日常镜像名的情况下,通过镜像服务获取 Docker Hub 内容。
如果只是临时排错:
dockerpull\docker.1ms.run/library/nginx更直接。
如果希望:
Docker Compose Docker Build 日常 docker pull都自动使用 Mirror,
再配置:
registry-mirrors。这两种使用场景不要混淆。
最后还要记住:
公共镜像地址本质上是外部服务,不可能保证永远有效。
所以相比:
收藏一篇两年前的 Docker 镜像教程更有价值的做法是:
维护一个持续更新、可以共同反馈、最好还能自动检测可用性的镜像资源库。
这也是这个开源项目后面最值得继续发展的方向。
项目地址:
https://github.com/Rodert/DockerHubGitHub Pages:
https://rodert.github.io/DockerHub/