中文 English

Docker 拉不动别急着换 DNS:国内镜像加速源挑选、配置与避坑全指南

发布时间: 2026-07-15
Docker 镜像加速 Container Manager 群晖 Debian Ubuntu 飞牛OS insecure-registries 私有仓库

先说结论

国内网络环境下,Docker 拉取镜像失败,往往不是镜像不存在,也不一定是 DNS 的锅。更常见的原因是:公共仓库的访问链路拥堵、某个加速源临时故障,或者配置文件改对了却没有重启 Docker。

我的建议是:先打开 status.anye.xyz,把它当作“镜像源天气预报”,看多个源最近是否可用;再根据自己的系统改对配置文件;改完先校验 JSON,再重启 Docker,最后用一个小镜像做真实拉取验证。

一、问题背景:为什么昨天能拉,今天就超时?

很多人第一次遇到这个问题,是在部署一个很普通的服务:nginxredispostgres,或者 NAS 上的媒体服务。命令可能只有一句:

docker pull nginx:alpine

但终端却出现了超时、context deadline exceededTLS handshake timeoutconnection reset by peer,甚至长时间停在下载层。换一个镜像标签、重启路由器、修改本地 DNS,有时能暂时恢复,于是大家容易得出一个结论:Docker 太玄学。

其实 Docker 拉镜像像是“从仓库借书”:

  1. Docker 客户端先找到仓库地址;
  2. 再通过网络连接仓库;
  3. 仓库返回镜像清单;
  4. 客户端逐层下载内容;
  5. 最后校验每一层是否完整。

这条链路中任何一段堵住,都会表现为“拉不下来”。所以,镜像加速源不是魔法按钮,而是给这条路增加一条更顺畅的入口。

Docker 镜像加速示意图:监控站、配置文件与 Docker 服务

二、问题表现:不要只看网页能不能打开

一个镜像源网页能打开,不等于 Docker 一定能用。选择加速源时,至少要看四件事:

镜像源挑选的四个问题

1. 地址是否真的可达

浏览器打开首页,只能证明首页响应了。Docker 还要访问 Registry API、鉴权接口和镜像层下载接口。网页正常、拉取失败,是完全可能的。

2. 是否支持完整的 Docker Hub 加速链路

有些地址只是一个网页代理或搜索页面,并不是 Docker Registry mirror。配置到 registry-mirrors 后,如果 docker info 看不到它,或者 docker pull 仍然直接访问默认仓库,就要怀疑它并非真正的加速服务。

3. 是否持续稳定,而不是刚好今天在线

镜像源很像城市道路:今天通车,不代表明天不施工。稳定性比某一次测速的峰值更重要。家用 NAS 可以接受偶尔切换,CI 构建和生产环境则应该准备备用方案。

4. 是否适合你的使用场景

个人电脑、NAS、家庭服务器、公司 CI 的要求不同。不要把一个“临时可用”的公共源当作生产供应链,也不要把未经确认的地址批量写入所有机器。

三、为什么推荐 status.anye.xyz?

我推荐把 status.anye.xyz 加入收藏,不是因为它承诺“永远最快”,而是因为它把多个 Docker 镜像源放到同一个页面,并进行定期可用性检查。你可以把它理解成车站的电子大屏:它不替你开车,但能告诉你哪条线路刚刚还在运行、哪条线路可能已经拥堵。

status.anye.xyz 多镜像源可用性页面截图

实际使用时,我建议按下面的顺序判断:

  1. 先看最近检查结果:不要只盯着某个源的名字,先看它最近是否持续有响应。
  2. 优先 HTTPS:没有特殊理由,不要主动选择 HTTP 源。
  3. 先用小镜像测试:例如 hello-worldalpine,确认链路后再拉几个 GB 的大镜像。
  4. 保留一个备用源:公共服务会维护、限流或变更,备用入口能减少临时救火。
  5. 定期复查:镜像源不是一次配置、终身有效;隔一段时间回到状态页复查。

这里有一个容易忽略的细节:状态站展示的是“监测视角”。你的宽带运营商、地区、IPv4/IPv6、代理和 DNS 都可能不同,所以最终结论仍然要以你自己的 docker pull 为准。

四、到底应该修改哪个文件?

Docker 的配置文件像“总电闸旁边的配电箱”:文件内容正确,Docker 才知道该去哪里找镜像;文件路径错了,改完不会有任何效果。

不同系统对应的 Docker 配置文件路径

群晖 DSM 7.x:Container Manager

DSM 7.x 通常使用 Container Manager,配置文件是:

vim /var/packages/ContainerManager/etc/dockerd.json

一个常见的配置骨架如下。把 <可用镜像源地址> 替换成你从状态页筛选出的地址,不要机械照抄过时的静态列表:

{
  "registry-mirrors": [
    "https://<可用镜像源地址>"
  ]
}

保存后,重启 Container Manager 的 dockerd 服务:

systemctl restart pkg-ContainerManager-dockerd

如果你的 DSM 版本或套件名称不同,先查服务名:

systemctl list-units --type=service --all | grep -Ei 'docker|container'

群晖 DSM 6.x:Docker

DSM 6.x 的 Docker 套件配置文件是:

vim /var/packages/Docker/etc/dockerd.json

配置格式仍然是 JSON,例如:

{
  "registry-mirrors": [
    "https://<可用镜像源地址>"
  ]
}

重启 Docker 套件:

synoservice --restart pkgctl-Docker

部分版本也可以通过 DSM 的套件中心停止再启动 Docker;如果命令报“服务不存在”,使用下面的查询命令确认实际服务名:

synoservice --status | grep -Ei 'docker|container'

Ubuntu、Debian、飞牛 OS 等普通 Linux

大多数标准 Docker 安装使用:

vim /etc/docker/daemon.json

没有文件就创建它。示例:

{
  "registry-mirrors": [
    "https://<可用镜像源地址>"
  ]
}

校验 JSON:

jq . /etc/docker/daemon.json

如果系统没有 jq,也可以用 Python:

python3 -m json.tool /etc/docker/daemon.json >/dev/null

重启 Docker:

systemctl daemon-reload
systemctl restart docker
systemctl --no-pager --full status docker

Docker 官方配置文档截图

五、为什么“改了配置”却没有生效?

最常见的根因有四类:

根因 1:JSON 多了一个逗号

JSON 比 YAML 更严格,最后一个字段后面不能多逗号。下面是错误写法:

{
  "registry-mirrors": [
    "https://<可用镜像源地址>",
  ]
}

保存后先执行 jq .,比直接重启更稳妥。Docker 因为配置解析失败而无法启动时,正在运行的容器也可能受到影响。

根因 2:把镜像源写进了错误的文件

群晖不是普通 Ubuntu。你在 /etc/docker/daemon.json 写得再正确,群晖套件也可能根本不读取它。一定要先确认当前 dockerd 的启动参数和套件版本。

根因 3:改完没有重启

Docker daemon 不会因为你保存了文本文件,就自动重新读取所有配置。就像你换了公交站牌,车辆不会瞬间知道新路线;需要重启服务,让它重新加载。

Docker 配置修改后的备份、校验、重启与验证流程

根因 4:把一个公共源配置成唯一依赖

公共源可能临时维护、限流或调整策略。如果只配置一个源,一旦它故障,所有新部署都会一起失败。更好的做法是:先用状态页筛选,保留少量可信候选,并定期复测。

六、registry-mirrorsinsecure-registries 不是一回事

这是本文最重要的安全提醒。

registry-mirrors:公共仓库的加速入口

它回答的问题是:“我访问公共镜像仓库时,能不能走一个更近、更稳定的入口?”

{
  "registry-mirrors": [
    "https://<可用镜像源地址>"
  ]
}

insecure-registries:允许访问 HTTP 私有仓库

它回答的问题是:“我有一个没有 HTTPS 证书的内网私有仓库,Docker 是否允许连接?”

在之前的文章《Docker 镜像不求人:从 0 到 1 在自己家里搭一个私有仓库》中,我们介绍过如何自建 Registry。假设你的内网仓库使用 HTTP,配置可以写成:

{
  "registry-mirrors": [
    "https://<可用镜像源地址>"
  ],
  "insecure-registries": [
    "<你的私有仓库主机>:<端口>"
  ]
}

这里故意使用占位符,不把任何真实内网地址写进文章。实际部署时替换成你自己的私有仓库地址即可。

公共镜像加速与内网 HTTP 仓库的边界

为什么叫 insecure?因为 HTTP 没有 TLS 加密和证书校验,链路上的内容可能被窃听或篡改。它像在小区内部允许“明文喊话”:内网测试环境可以接受,跨公网或生产环境不应该这样做。

所以请记住:

七、一套更稳妥的验证流程

不要以“服务重启没有报错”作为唯一成功标准。建议按四步验证:

# 1. 查看 Docker 是否读取到了镜像源
 docker info | sed -n '/Registry Mirrors/,+5p'

# 2. 查看 daemon 是否正常运行
 systemctl --no-pager --full status docker

# 3. 拉取一个小镜像
 docker pull hello-world

# 4. 查看镜像是否存在
 docker image ls hello-world

群晖上可以使用:

docker info | grep -A5 -Ei 'Registry Mirrors|Insecure Registries'
docker pull hello-world

如果状态页显示可用,但你的 docker pull 失败,按顺序排查:

  1. 配置文件路径是否与系统版本匹配;
  2. JSON 是否能被 jq 或 Python 解析;
  3. Docker 服务是否真的重启成功;
  4. docker info 是否出现预期配置;
  5. 当前网络是否经过代理、IPv6 或 DNS 劫持;
  6. 换另一个状态页上近期稳定的源复测。

此前自建私有 Registry 文章的线上截图

八、Q&A

Q1:镜像源越多越好吗?

不是。配置项是候选入口,不是越多越快。过多、质量不明的地址会增加排查难度,甚至带来不一致行为。建议先选择少量、近期稳定、HTTPS 的候选,出现问题再替换。

Q2:为什么 docker info 里看不到 registry-mirrors

优先检查三点:改错文件、JSON 无效、Docker 没重启。群晖还要额外确认你修改的是 Container Manager 或 Docker 套件实际使用的 dockerd.json

Q3:我只在局域网使用,能不能一直开 insecure-registries

能不能用和该不该长期用是两件事。实验室或隔离内网可以临时使用,但最好仍然给私有仓库配置 HTTPS;至少不要把 HTTP Registry 暴露到公网,也不要把真实内网地址写进公开配置和文章。

Q4:状态站显示可用,我这里还是拉不动怎么办?

状态站是参考,不是你的网络探针。先用 curldocker info 和小镜像拉取做本地验证,再检查代理、IPv6、DNS、运营商链路和防火墙。必要时换备用源,不要在一条故障链路上反复重试。

Q5:重启 Docker 会不会让现有容器消失?

正常重启 daemon 不会删除容器和镜像,但会造成短暂服务中断。修改前先备份配置;生产环境先确认容器的自动重启策略、挂载目录和业务窗口,再操作。

九、写在最后:把“玄学换源”变成可验证的运维动作

Docker 镜像加速真正难的地方,不是复制一段 JSON,而是建立正确的判断顺序:先观察可用性,再选择 HTTPS 入口;先确认系统路径,再校验配置;先重启 daemon,再用真实拉取验证;遇到内网 HTTP 私有仓库时,单独理解 insecure-registries 的安全边界。

如果你只记住三句话:

  1. 先看 status.anye.xyz,不要迷信一份过时的镜像源清单。
  2. 改完配置一定要校验 JSON、重启 Docker、检查 docker info
  3. registry-mirrors 是公共仓库加速,insecure-registries 是内网 HTTP 放行,二者不要混用。

参考资料: