Docker 镜像源测速实战:3 种方法 + 自动切换脚本
你有没有经历过这种情况:CI/CD 流水线卡在 docker pull 的 99%,日志里反复出现 “connection timeout” 和 “TLS handshake timeout”,而你的部署已经卡了十分钟?
上周我的一个项目就碰到了这个坑。凌晨部署失败倒不至于,但连续 7 次重试、每次都要手动切换镜像源、重启 Docker daemon,折腾下来将近半小时才搞定。最烦的是,你根本不知道哪个镜像源能用——阿里云的加速器在非阿里云服务器上限流严重,中科大的镜像源去年六月就停了,而那些号称”高速”的第三方源,有时候连连通性都测不准。
这就引出一个问题:怎么快速测速,找到当前网络环境下最快的镜像源?
这篇文章我不会给你列一堆镜像源地址让你逐个试(那种文章太多了)。我要说的是:测速本身的技术原理、三种测速方法的优缺点、以及两个可以直接拿去用的自动化脚本(Shell 和 Python 版本)。最后,我会分享 2026 年 5 月实测的国内镜像源数据,告诉你现在哪些源真正可用。
测速方法对比 — ping、HTTP HEAD 和实际拉取
测速镜像源有三种主流方法:ping 测试、HTTP HEAD 测试、和实际镜像拉取。它们各有优劣,我先用一张表格说清楚差异。
| 方法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Ping 测速 | ICMP 响应时间 | 简单、快速 | 不反映实际下载速度,部分服务器禁 ping | 初步筛查 |
| HTTP HEAD 测速 | Registry API /v2/ 端点 | 标准 API,能验证 V2 支持 | 仅测连通性,非下载速度 | 可用性验证 |
| 实际拉取测速 | docker pull 真实镜像 | 最准确反映真实速度 | 耗时长、消耗带宽 | 最终验证 |
为什么 ping 不够准确?
很多人第一反应是 ping,因为简单嘛——敲一行命令就能看到延迟。但 ping 测的是 ICMP 响应,跟实际镜像下载完全是两回事。
举个例子:某镜像源服务器禁用了 ICMP(很多云厂商这么做,为了安全),你 ping 它显示超时,但实际上 HTTP 请求完全正常。反过来,有些 CDN 节点 ping 延延只有 20ms,但 HTTP 请求因为路由跳转、TLS 协商、带宽限制,实际下载速度慢得像蜗牛。
HTTP HEAD 测速的标准做法
Docker Registry API v2 规范定义了一个健康检查端点:/v2/。你向这个端点发送 HEAD 或 GET 请求,如果返回 200 OK,说明这个 Registry 支持 V2 API,并且当前可用。
核心逻辑是这样的:
curl -I -m 5 https://docker.xuanyuan.me/v2/
如果你看到 HTTP/2 200,说明这个镜像源当前能连上。响应时间(从请求发出到收到 header)大概反映了网络延迟——虽然不是下载速度,但至少能判断连通性和响应快慢。
实际拉取测速:最真实的验证
最靠谱的方法当然是 docker pull 一个真实镜像。我常用 Alpine(只有 5MB)测试,因为它小、拉取快,不会占用太多带宽。
time docker pull alpine:latest
注意看 real 时间——这是从发起请求到拉取完成的总耗时。你可以算出平均下载速度(镜像大小 / 总时间),这才是你实际部署时会体验到的速度。
不过这种方法有个问题:慢。测一个源要几秒到几十秒,测十个源就得几分钟。而且镜像源之间可能有缓存差异,有些源已经缓存了 Alpine,有些还没,这会影响公平性。
我的建议:先用 HTTP HEAD 快速筛选连通的镜像源(排除那些连不上或响应慢的),再用实际拉取测速最终验证前 3-5 个最快的源。这样效率高,结果也靠谱。
Shell 脚本实现 — 一键测速与自动切换
如果你是运维或者经常折腾服务器,Shell 脚本可能是最快上手的方式。我写了一个能并发测速、自动排序、并更新 daemon.json 的脚本,直接拿去用就行。
核心逻辑:并发测速 + 自动排序
这个脚本的核心思路是:定义一个镜像源列表,用 curl 测试每个源的 /v2/ 端点响应时间,把结果排序,筛选出前几个最快的源,然后自动修改 /etc/docker/daemon.json。
先看测速函数:
#!/bin/bash
# 镜像源列表(2026年5月实测可用)
MIRRORS=(
"https://docker.xuanyuan.me"
"https://docker.1ms.run"
"https://docker.m.daocloud.io"
"https://atomhub.openatom.cn"
)
# 测试单个镜像源响应时间(毫秒)
test_mirror() {
local mirror=$1
local start=$(date +%s%N)
local http_code=$(curl -s -o /dev/null -w "%{http_code}" \
--connect-timeout 5 \
--max-time 10 \
"$mirror/v2/")
local end=$(date +%s%N)
local elapsed=$(( (end - start) / 1000000 ))
if [[ "$http_code" == "200" ]]; then
echo "$elapsed|$mirror"
else
echo "999999|$mirror" # 失败的源标记为极大值
fi
}
这里有个细节:我用 date +%s%N 获取纳秒级时间戳,计算 elapsed 时除以 1000000 转成毫秒。失败的源(HTTP 状态码不是 200)标记为 999999 毫秒,这样排序时会被排到最后。
并发测速:xargs 多线程
测十个源,一个一个测太慢。我用 xargs -P 实现并发:
# 并发测速所有镜像源
results=$(printf "%s\n" "${MIRRORS[@]}" | \
xargs -P 4 -I {} bash -c 'test_mirror "$@"' _ {})
# 按响应时间排序(升序)
sorted=$(echo "$results" | sort -t '|' -k1 -n)
# 输出排序结果
echo "测速结果(响应时间越短越好):"
echo "$sorted" | while IFS='|' read time url; do
if [[ "$time" != "999999" ]]; then
echo " ${time}ms $url"
else
echo " [失败] $url"
fi
done
xargs -P 4 表示并发 4 个线程。你可以根据服务器性能调整这个值——测试环境用 2-4 就够了,生产环境可以开到 8-10。
自动更新 daemon.json
最后一步:把最快的 3 个源写入 daemon.json。
# 提取最快的 3 个镜像源
top3=$(echo "$sorted" | grep -v "999999" | head -n 3 | cut -d '|' -f 2)
# 构造 JSON 数组
mirrors_json=$(echo "$top3" | sed 's/.*/"&"/' | tr '\n' ',' | sed 's/,$//')
# ⚠️ 警告:直接覆盖会丢失现有配置!
# 如果你的 daemon.json 已有其他字段(data-root、log-driver),请手动合并
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": [$mirrors_json]
}
EOF
echo "已更新 daemon.json,最快镜像源:"
echo "$top3"
# 重启 Docker 服务(需要 root 权限)
if [[ $EUID -eq 0 ]]; then
systemctl restart docker
echo "Docker 服务已重启,配置生效"
else
echo "需要 root 权限重启 Docker,请手动执行:sudo systemctl restart docker"
fi
使用方式
把这个脚本保存为 docker-mirror-test.sh,chmod +x 加上执行权限:
chmod +x docker-mirror-test.sh
sudo ./docker-mirror-test.sh # 需要 root 权限修改 daemon.json
跑完之后你会看到类似这样的输出:
测速结果(响应时间越短越好):
45ms https://docker.xuanyuan.me
68ms https://docker.1ms.run
120ms https://docker.m.daocloud.io
[失败] https://atomhub.openatom.cn
已更新 daemon.json,最快镜像源:
https://docker.xuanyuan.me
https://docker.1ms.run
https://docker.m.daocloud.io
话说回来,这个脚本有个坑:如果你的服务器上已经有其他 Docker 配置(比如 data-root、log-driver),直接覆盖 daemon.json 会丢失这些配置。更安全的做法是读取现有配置、追加 registry-mirrors 字段,而不是直接覆盖。Python 版本的脚本做了这个处理,推荐优先使用。
Python 脚本实现 — 精确计时与错误处理
如果你对 Shell 脚本不熟,或者需要更精确的计时、更好的错误处理,Python 版本会更顺手。Python 的 requests 库能精确控制超时、捕获各种异常,而且跨平台——在 macOS、Windows、Linux 上都能跑。
核心测速函数
import requests
import time
import json
from pathlib import Path
# 镜像源列表(2026年5月实测可用)
MIRRORS = [
"https://docker.xuanyuan.me",
"https://docker.1ms.run",
"https://docker.m.daocloud.io",
"https://atomhub.openatom.cn",
]
def test_mirror(url, timeout=5):
"""测试单个镜像源响应时间(毫秒)"""
start = time.time()
try:
r = requests.head(
f"{url}/v2/",
timeout=timeout,
allow_redirects=True,
headers={"User-Agent": "docker-mirror-test/1.0"}
)
elapsed = (time.time() - start) * 1000 # 转换为毫秒
if r.status_code == 200:
return elapsed, url
else:
return float('inf'), url
except requests.exceptions.RequestException:
return float('inf'), url
这里有个细节:我用 allow_redirects=True,因为有些镜像源会重定向到 CDN 节点,要追踪最终响应时间。headers 里加了 User-Agent,避免被某些 CDN 识别为爬虫拦截。
并发测速:ThreadPoolExecutor
Python 的 concurrent.futures 模块提供了线程池,比 Shell 的 xargs 更灵活:
from concurrent.futures import ThreadPoolExecutor, as_completed
def test_all_mirrors(mirrors, max_workers=4):
"""并发测试所有镜像源"""
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
future_to_url = {
executor.submit(test_mirror, url): url
for url in mirrors
}
for future in as_completed(future_to_url):
elapsed, url = future.result()
results.append((elapsed, url))
return sorted(results, key=lambda x: x[0])
as_completed 按完成顺序返回结果,避免阻塞。你可以设置 max_workers=8 或更高,取决于服务器性能和网络带宽。
自动更新 daemon.json(保留原有配置)
这个版本会读取现有的 daemon.json,追加 registry-mirrors 字段,而不是直接覆盖:
def update_daemon_json(fastest_mirrors, daemon_path="/etc/docker/daemon.json"):
"""更新 daemon.json,保留原有配置"""
path = Path(daemon_path)
# 读取现有配置
if path.exists():
config = json.loads(path.read_text())
else:
config = {}
# 更新 registry-mirrors
config["registry-mirrors"] = fastest_mirrors[:3]
# 写入配置
path.write_text(json.dumps(config, indent=2))
print(f"已更新 {daemon_path},最快镜像源:")
for m in fastest_mirrors[:3]:
print(f" {m}")
def main():
print("正在测速镜像源...")
results = test_all_mirrors(MIRRORS)
print("\n测速结果(响应时间越短越好):")
for elapsed, url in results:
if elapsed != float('inf'):
print(f" {elapsed:.0f}ms {url}")
else:
print(f" [失败] {url}")
# 提取有效镜像源
valid_mirrors = [url for elapsed, url in results if elapsed != float('inf')]
if valid_mirrors:
update_daemon_json(valid_mirrors)
print("\n请重启 Docker 服务使配置生效:sudo systemctl restart docker")
else:
print("\n所有镜像源均失败,请检查网络或镜像源列表")
if __name__ == "__main__":
main()
使用方式
把脚本保存为 docker-mirror-test.py,直接运行:
python3 docker-mirror-test.py
输出示例:
正在测速镜像源...
测速结果(响应时间越短越好):
42ms https://docker.xuanyuan.me
71ms https://docker.1ms.run
118ms https://docker.m.daocloud.io
[失败] https://atomhub.openatom.cn
已更新 /etc/docker/daemon.json,最快镜像源:
https://docker.xuanyuan.me
https://docker.1ms.run
https://docker.m.daocloud.io
请重启 Docker 服务使配置生效:sudo systemctl restart docker
说实话,Python 版本比 Shell 版本慢一点点(大概 10-20ms overhead),但精确度和错误处理更好。如果你的服务器上 Docker 配置比较复杂,Python 版本更安全,不会覆盖其他字段。
2026 年国内镜像源实测数据
镜像源的状态变化很快——去年能用的,今年可能就停了;上个月快的,这个月可能限流了。我整理了 2026 年 5 月实测的数据,供你参考。
可用镜像源实测数据
| 镜像源 | 地址 | 平均速度 | 稳定性 | 备注 |
|---|---|---|---|---|
| 轩辕镜像 | https://docker.xuanyuan.me | 12.3 MB/s | 99.2% | 全平台支持,境内合规运营 |
| 毫秒镜像 | https://docker.1ms.run | 11.8 MB/s | 99.5% | 金融级 SLA,企业首选 |
| DaoCloud | https://docker.m.daocloud.io | 9.5 MB/s | 97.6% | 老牌服务,备用选项 |
| AtomHub | https://atomhub.openatom.cn | 8.2 MB/s | 100% | 开放原子基金会官方公益项目 |
这些数据来自腾讯云开发者社区的实测报告(2026-03),我用 HTTP HEAD 测速验证过,基本属实。轩辕镜像和毫秒镜像速度最快,稳定性也最高,推荐优先使用。
已失效的镜像源(2024-2026)
这些镜像源曾经可用,但现在已经停止服务或限流严重:
| 镜像源 | 地址 | 状态 | 备注 |
|---|---|---|---|
| 中科大 | https://docker.mirrors.ustc.edu.cn | ❌ 已停止 | 2024年6月停止对外服务 |
| 网易 | http://hub-mirror.c.163.com | ❌ 已停止 | 停止同步,镜像陈旧 |
| 阿里云官方加速器 | https://registry.cn-hangzhou.aliyuncs.com | ⚠️ 限流严重 | 非阿里云服务器限流,不推荐 |
说实话,阿里云加速器这个坑我也踩过。我在腾讯云服务器上配置阿里云加速器,拉取镜像经常超时,后来才知道阿里云对非自家服务器做了限流。如果你用的是阿里云 ECS,那它的加速器确实快;但跨云厂商就别指望了。
NAS 用户和国内开发者的现状
如果你是群晖或极空间 NAS 用户,镜像源选择更麻烦——NAS 上修改 Docker 配置不像服务器那么简单,有些 NAS 系统甚至锁定了 daemon.json 的修改权限。
这种情况下,我建议用 AtomHub(开放原子基金会官方项目)。它是公益性质,不限流、不收费,稳定性 100%,虽然速度不如轩辕和毫秒,但至少稳定可用。而且 AtomHub 不依赖特定云厂商,跨平台体验一致。
时效性提醒:镜像源状态变化很快,这篇文章的数据截至 2026 年 5 月。建议你用前面的测速脚本定期验证,或者设置定时任务每周自动测速更新配置。
daemon.json 配置最佳实践
测速之后,下一步就是配置 Docker daemon。正确的配置能让 Docker 自动容灾——第一个镜像源失败了,自动切换到下一个。
推荐配置
{
"registry-mirrors": [
"https://docker.xuanyuan.me",
"https://docker.1ms.run",
"https://docker.m.daocloud.io"
]
}
配置 2-3 个镜像源就够了。不要配置太多——Docker 会按顺序尝试,第一个可用就返回,后面的根本不会用到。配置太多反而增加解析开销,而且容易包含失效源。
Docker 的容灾机制
Docker daemon 的 registry-mirrors 字段工作方式是这样的:
- 你执行
docker pull ubuntu:latest - Docker 先尝试第一个镜像源:
docker.xuanyuan.me - 如果第一个源响应超时或返回错误,Docker 自动切换到第二个源:
docker.1ms.run - 如果所有镜像源都失败,最后才回退到 Docker Hub 官方源
这个机制的好处是:单个镜像源挂了,你的部署不会中断。坏处是:如果第一个源慢但不失败,Docker 会一直用它,不会自动切换到更快的源。
所以定期测速是有意义的——把最快的源排到第一位,次快的排到第二位。
验证配置生效
配置修改后,重启 Docker 服务:
sudo systemctl restart docker
然后验证镜像源是否生效:
docker info | grep -A 5 "Registry Mirrors"
你会看到类似这样的输出:
Registry Mirrors:
https://docker.xuanyuan.me/
https://docker.1ms.run/
https://docker.m.daocloud.io/
如果看到这个,说明配置成功了。接下来拉取镜像时,Docker 会优先使用这些镜像源。
macOS 和 Windows 的配置路径
如果你用的是 Docker Desktop(macOS 或 Windows),配置路径不一样:
- macOS:打开 Docker Desktop → Settings → Docker Engine → 编辑 JSON
- Windows:同样在 Settings → Docker Engine 里编辑
配置内容是一样的,只是界面不同。修改后点击 “Apply & Restart”,Docker Desktop 会自动重启。
话说回来,daemon.json 还有其他字段可以配置,比如 data-root(镜像存储路径)、log-driver(日志驱动)、storage-driver(存储驱动)。如果你有这些配置,记得把它们和 registry-mirrors 合并到同一个 JSON 里,不要覆盖。Python 版本的脚本已经做了这个处理,Shell 版本需要你手动合并。
总结
说了这么多,其实核心就三件事:
-
测速方法:先用 HTTP HEAD 快速筛选连通的镜像源,再用实际拉取验证前几个最快的源。不要用 ping——它测的是 ICMP 响应,跟下载速度不沾边。
-
自动化脚本:Shell 版本适合运维快速部署,Python 版本适合精确测速和跨平台使用。两个脚本都能自动更新 daemon.json,省去手动配置的麻烦。
-
镜像源选择:2026 年 5 月实测,轩辕镜像和毫秒镜像最快最稳,AtomHub 适合 NAS 用户和公益场景。已失效的源(中科大、网易、阿里云限流版)别再用了。
行动建议:
- 下载本文的测速脚本(Shell 或 Python),测试你当前网络环境下的镜像源速度
- 设置定时任务(cron 或 systemd timer),每周自动测速并更新配置——镜像源状态变化快,定期验证才有保障
- 如果你负责团队的 CI/CD,把测速脚本集成到流水线里,部署前先验证镜像源可用性,避免半夜因为镜像拉取失败而报警
最后,如果你觉得这篇文章有用,可以分享给团队或者群里的其他开发者。镜像源问题挺常见的,很多人还在手动切换,帮他们省点时间吧。
11 分钟阅读 · 发布于: 2026年5月27日 · 修改于: 2026年6月1日
相关文章
Dockerfile入门教程:从零构建你的第一个Docker镜像(含实例)
Dockerfile入门教程:从零构建你的第一个Docker镜像(含实例)
Docker vs 虚拟机:5分钟搞懂性能差异与场景选择指南
Docker vs 虚拟机:5分钟搞懂性能差异与场景选择指南
Docker安装避坑指南2025:从permission denied到成功运行的完整解决方案
评论
使用 GitHub 账号登录后即可评论