不想折腾 Harbor?用 Docker Registry 自建一个轻量级 Docker Hub 镜像缓存

在这里插入图片描述

前面围绕这个项目:

https://github.com/Rodert/DockerHub

我们已经聊过很多 Docker 镜像相关问题:

公共 Docker Mirror
registry-mirrors
Manifest / Layer / Digest
Harbor Proxy Cache
docker pull 排错
Docker Build 网络
Build Cache
Docker 磁盘清理

其中有一篇我们讲过:

用 Harbor Proxy Cache 自建 Docker Hub 缓存。

Harbor 确实很好用。

但很多个人开发者或者小团队看完以后会有一个想法:

我就是想缓存一下 Docker Hub 镜像,有必要装一整套 Harbor 吗?

毕竟 Harbor 还包含:

Web UI
用户管理
Project
RBAC
Replication
Retention
Scanner
数据库
Redis

如果你的需求真的只有:

第一次去 Docker Hub 下载,第二次直接从自己的服务器拿。

那完全可以再轻一点。

直接使用:

CNCF Distribution Registry。

也就是 Docker Registry 背后的开源 Registry 实现。

官方 Distribution 当前直接支持:

Pull-through Cache

模式。

简单来说:

Docker Host
      ↓
自己的 Registry Mirror
      ↓
Docker Hub

第一次:

Cache Miss
↓
去 Docker Hub 拉
↓
本地缓存
↓
返回客户端

第二次:

Cache Hit
↓
直接从自己服务器返回

Distribution 官方文档当前仍然明确支持这种 Pull-through Cache 架构,而且特别适合多个 Docker Host、虚拟机或者 Kubernetes 节点共享外部镜像缓存。

今天就基于:

https://github.com/Rodert/DockerHub

继续把这套轻量级方案讲透。


一、先看我们到底要解决什么问题

假设你有:

10 台 Linux 服务器

每一台都要运行:

nginx
redis
mysql
node
golang

现在每台机器都直接:

Server 1 ─────→ Docker Hub
Server 2 ─────→ Docker Hub
Server 3 ─────→ Docker Hub
...
Server 10 ────→ Docker Hub

同一个:

nginx Layer

可能被下载:

10 次。

同一个:

golang:1.25

可能每台服务器都重新跨公网拉取。

这就出现三个问题:

重复公网流量

Docker Hub 访问不稳定

Docker Hub Rate Limit

如果加一台缓存服务器:

               Docker Hub
                   ↑
                   │
            Registry Mirror
             /     |      \
            ↓      ↓       ↓
         Server1 Server2 Server3

第一次:

Server1
↓
Mirror
↓
Docker Hub

第二台开始:

Server2
↓
Mirror Cache

整个体验就完全不一样了。


二、什么是 Pull-through Cache?

名字其实已经解释得很清楚:

Pull
Through
Cache

也就是说客户端:

向本地 Registry Pull。

本地没有:

继续向上游 Pull。

同时:

把内容缓存下来。

下次:

直接使用缓存。

Distribution 官方现在的描述就是:

第一次请求镜像时,从远程 Registry 获取并存储到本地;后续请求可以直接从本地 Registry 提供。

这和普通 HTTP CDN:

Origin
↓
Edge Cache
↓
User

其实是同一个思路。


三、为什么 Docker 镜像特别适合缓存?

因为 Docker 镜像不是:

一个巨大 zip。

而是:

Manifest
+
Config
+
Layers

每一个 Layer:

都有自己的 Digest。

例如:

sha256:abc123...

如果服务器已经缓存:

Layer A
Layer B

另外一个镜像也使用:

Layer A

就不需要再重新保存完全相同的内容。

所以:

Docker Registry 天生就很适合做代理缓存。


四、它和普通 Private Registry 有什么区别?

普通 Registry:

Developer
↓
docker push
↓
Registry

里面的镜像:

由你主动 Push。

例如:

docker tag myapp:v1 \
  registry.example.com/myapp:v1

docker push \
  registry.example.com/myapp:v1

Pull-through Cache:

Developer
↓
docker pull nginx
↓
Registry Mirror
↓
Docker Hub

里面的数据:

由 Pull 请求自动触发。

也就是:

一个是主动 Push。

一个是按需缓存 Pull。


五、Pull-through Cache 模式不能拿来正常 Push

这是一个特别需要注意的点。

Distribution 当前配置文档明确说明:

配置成 pull-through cache 的 Registry
不支持 Push。

所以不要把一个 Registry:

既当 Docker Hub Cache
又当自己公司正常 Push 的私有仓库

混着用。

更合理:

registry-cache.example.com
→ Docker Hub Cache

registry.example.com
→ 公司自己的 Private Registry

分开。


六、最核心配置只有一个:proxy.remoteurl

Distribution 配置文件:

config.yml

最关键的部分:

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

意思很简单:

当前 Registry
的上游
=
Docker Hub Registry

Distribution 官方现在的 Pull-through Cache 最低配置要求就是指定这个 proxy.remoteurl。


七、先准备目录

例如服务器:

/opt/docker-registry/

创建:

sudo mkdir -p \
  /opt/docker-registry/data

再:

sudo mkdir -p \
  /opt/docker-registry/config

最终:

/opt/docker-registry
├── config/
│   └── config.yml
└── data/

八、写 config.yml

例如:

version: 0.1

log:
  fields:
    service: registry

storage:

  filesystem:
    rootdirectory: /var/lib/registry

  delete:
    enabled: true

http:
  addr: :5000

proxy:
  remoteurl: https://registry-1.docker.io
  ttl: 168h

几个重点。


九、filesystem 是什么?

storage:

  filesystem:
    rootdirectory: /var/lib/registry

代表:

镜像缓存

存到 Registry Container 内:

/var/lib/registry

我们后面把这个目录:

Mount 到宿主机。

Distribution 官方 Pull-through Cache 文档当前也建议缓存 Registry 使用 filesystem Storage Driver,以获得正确且稳定的缓存行为。


十、delete.enabled 为什么打开?

delete:
  enabled: true

因为 Pull-through Cache:

时间长了

一定会积累:

旧 Manifest
旧 Blob
不再使用的数据。

Distribution 当前文档特别提醒:

如果希望缓存清理调度器能够清理旧内容,需要启用 delete。

这和我们上一篇:

Docker 磁盘越来越大

正好连接起来。

缓存解决性能问题,

但:

缓存必须有生命周期。


十一、ttl: 168h 又是什么?

proxy:
  ttl: 168h

就是:

7 天。

Distribution 当前配置说明中:

proxy.ttl

默认就是:

168h

也就是七天;如果配置为 0,则可以关闭 Cache Expiration。

例如:

proxy:
  remoteurl: https://registry-1.docker.io
  ttl: 72h

就是:

3 天。

十二、ttl 并不是“3 天后所有镜像立刻消失”

这里不要误解。

它主要用于:

Proxy Cache 的过期和回收策略。

真正什么时候清理:

还要看内部调度和缓存使用。

所以不要把:

ttl

简单理解成:

Linux rm -rf 每 72 小时。

它是:

Registry Cache Policy。


十三、然后用 Docker Compose 跑起来

例如:

services:

  registry:

    image: registry:2

    container_name: dockerhub-cache

    restart: unless-stopped

    ports:
      - "5000:5000"

    volumes:

      - ./data:/var/lib/registry

      - ./config/config.yml:/etc/distribution/config.yml:ro

启动:

docker compose up -d

检查:

docker ps

应该能看到:

dockerhub-cache

十四、查看日志

docker logs \
  -f dockerhub-cache

如果第一次请求某个 Blob:

本地不存在

Registry 会:

向上游获取。

官方文档甚至特别提醒,有些日志看起来像:

unknown blob

但实际上只是:

本地缓存暂时没有
正在去上游获取

的信息日志,不一定是真错误。

所以:

看日志一定要看 level。

不要看到 error 字样就立刻判断服务挂了。


十五、现在先直接测试 Registry

例如:

curl http://127.0.0.1:5000/v2/

如果正常:

{}

或者返回符合 Registry V2 行为的结果,

说明:

Registry Service

基本启动成功。


十六、但是这里有一个问题:HTTP

我们现在:

http://server:5000

没有 HTTPS。

Docker 默认:

更希望 Registry 使用 HTTPS。

如果你直接:

docker pull \
  192.168.1.20:5000/library/nginx:latest

可能遇到:

server gave HTTP response
to HTTPS client

怎么办?

有两条路。


十七、测试环境:insecure-registries

例如:

{
  "insecure-registries": [
    "192.168.1.20:5000"
  ]
}

写入:

/etc/docker/daemon.json

然后:

sudo systemctl restart docker

就能:

通过 HTTP Registry。

但这更适合:

内网实验。

生产环境还是建议:

HTTPS。


十八、正式环境:Nginx + HTTPS

例如:

mirror.example.com

前面放:

Nginx

结构:

Docker Client
↓
HTTPS 443
↓
Nginx
↓
Registry :5000

Nginx:

server {

    listen 443 ssl;

    server_name mirror.example.com;

    ssl_certificate
        /etc/nginx/ssl/fullchain.pem;

    ssl_certificate_key
        /etc/nginx/ssl/privkey.pem;

    location / {

        proxy_pass
            http://127.0.0.1:5000;

        proxy_set_header
            Host $host;

        proxy_set_header
            X-Real-IP $remote_addr;

        proxy_set_header
            X-Forwarded-Proto https;
    }
}

这样客户端:

正常走 HTTPS。

不需要:

insecure-registries。

十九、为什么我强烈建议 HTTPS?

因为你以后可能:

开放给多个服务器
甚至多个团队。

如果继续 HTTP:

Registry 请求

没有 TLS 保护。

正式环境不值得省这点事。


二十、客户端怎么配置成透明 Mirror?

这才是最方便的地方。

你不想每次写:

docker pull \
  mirror.example.com/library/nginx

而是还想:

docker pull nginx

Docker Daemon 可以配置:

{
  "registry-mirrors": [
    "https://mirror.example.com"
  ]
}

Distribution 官方当前文档就是这样建议让 Docker Daemon 使用 Pull-through Registry Mirror 的。

然后:

sudo systemctl restart docker

检查:

docker info

应该看到:

Registry Mirrors:
 https://mirror.example.com/

二十一、以后使用方式完全不变

还是:

docker pull nginx:latest

不需要告诉开发者:

换地址。

Docker Daemon 会优先:

通过 Registry Mirror 获取。

所以:

Docker Compose

原来:

services:

  nginx:
    image: nginx:latest

  redis:
    image: redis:7

也不需要改成:

mirror.example.com/...

这就是:

透明代理。


二十二、这和 Harbor Proxy Cache 最大区别之一

Harbor 常见使用:

harbor.example.com/dockerhub/library/nginx

你显式:

修改 Image Reference。

而 Distribution Pull-through Mirror:

{
  "registry-mirrors": [
    "https://mirror.example.com"
  ]
}

用户依然:

docker pull nginx

体验:

更透明。


二十三、第一次 Pull 会发生什么?

假设缓存目录:

完全为空。

执行:

docker pull nginx:latest

流程:

Docker Daemon
↓
mirror.example.com
↓
Cache Miss
↓
registry-1.docker.io
↓
Manifest
↓
Layers
↓
写入本地 Registry Cache
↓
返回 Docker Host

所以:

第一次不一定快很多。

因为它:

还是需要访问 Docker Hub。

二十四、甚至第一次可能略慢一点

因为链路从:

Client
↓
Docker Hub

变成:

Client
↓
Mirror
↓
Docker Hub

多了一层代理处理。

Pull-through Cache 真正的价值不是:

第一次立刻比 Docker Hub 快十倍。

而是:

后面的请求越来越便宜。


二十五、第二台服务器才是价值体现

Server A:

docker pull nginx

触发:

Mirror Cache Miss。

下载完成。

Server B:

docker pull nginx

此时:

Mirror 已经有 Layers。

流程:

Server B
↓
Mirror
↓
Local Cached Blob

不需要重复:

Mirror
↓
Docker Hub

下载全部 Blob。

这时候速度差距:

才会真正体现。


二十六、怎么正确测试 Cache?

很多人会犯一个错误:

在同一台客户端:

docker pull nginx

第二次马上再:

docker pull nginx

然后发现:

秒开。

就说:

Mirror Cache 太牛了。

其实未必。

因为第二次:

可能只是客户端自己的 Docker Layer Cache。

二十七、正确测试应该用两台机器

机器 A:

docker pull nginx:latest

让:

Mirror 完成缓存。

机器 B:

确认本地:

docker images

没有 nginx。

然后:

time docker pull nginx:latest

如果:

B 拉取明显更快。

这才更能证明:

命中了 Registry Server Cache。


二十八、也可以删除客户端本地镜像

例如:

docker rmi nginx:latest

然后:

docker image prune

确认客户端:

没有对应 Layers。

再:

time docker pull nginx

但多台机器测试:

更干净。


二十九、Tag 更新以后怎么办?

比如今天:

nginx:latest

指向:

Manifest A。

Mirror:

缓存 A。

过几天 Docker Hub:

latest
→ Manifest B。

Pull-through Cache 会不会永远返回旧 A?

不会这么简单。

Distribution 当前官方说明:

当通过 Tag 请求镜像时,
Registry 会检查上游内容,
确认是否仍然是最新版本;
如果上游发生变化,
则获取并缓存新内容。

所以:

Cache 不是永久死缓存。


三十、但 Digest Pull 更简单

例如:

nginx@sha256:abc123

Digest:

代表确定内容。

内容不变:

Digest 也不变。

这种资源:

天生特别适合长期缓存。

这也是 Docker Registry 设计很漂亮的地方。


三十一、磁盘会不会越来越大?

会。

比如团队一年内拉:

Ubuntu 20 个版本
Node 50 个版本
Java 100 个版本
各种 AI 镜像
各种数据库

这些:

Manifest
Blob
Layer

都会占用服务器磁盘。

所以 Pull-through Cache 不是:

无限免费缓存。

你还是要:

监控磁盘。

三十二、Distribution 会清理旧 Cache

官方当前说明:

Pull-through Cache

在高变化环境里会产生旧数据,

Registry 会周期性回收旧内容,之后如果再次请求已经被移除的数据,就重新从上游拉取。

这就是:

TTL
+
Delete

配置存在的意义。


三十三、不要把 ttl 直接设置成 0 就觉得最好

比如:

proxy:
  ttl: 0

意味着:

不因为 TTL 过期。

听起来:

缓存永远保留
=
性能最好。

但你的磁盘:

也可能永远增长。

如果只是:

几台个人服务器
固定十几个镜像

没什么问题。

如果:

大型 CI
每天拉几百种 Image Tag

风险就很大。


三十四、所以 TTL 应该跟使用场景有关

个人:

7 天
14 天
甚至更久

都可以。

CI 高频环境:

可以更积极清理旧缓存。

关键不是:

哪个 TTL 最正确。

而是:

你的磁盘成本和网络成本谁更贵。


三十五、这和 Build Cache 是同一个思想

缓存本质都是:

Disk
换
Network / CPU / Time

BuildKit:

Build Cache

Registry:

Pull Cache

npm:

Package Cache

Redis:

Memory Cache

背后的工程思想:

完全相同。


三十六、Docker Hub 登录账号要不要配置?

Distribution Proxy 支持:

proxy:

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

  username:
    YOUR_USERNAME

  password:
    YOUR_PASSWORD

用于访问:

Docker Hub Private Repository

或者:

通过账号请求上游。

Distribution 当前配置也支持 username/password 或 Credential Helper。

但是这里:

风险非常大。


三十七、为什么?

假设这个 Docker Hub 用户:

能访问:

company/private-api
company/private-db

你的 Mirror:

又完全公开在 Internet。

那么本来:

只能这个 Docker Hub Account

看到的 Private Repository,

现在有可能通过:

你的 Proxy Cache

暴露出去。

Distribution 官方对此有明确警告:如果给 Pull-through Cache 配置具有私有仓库访问权限的 Docker Hub 凭据,就必须保护 Mirror 自身,因为这些私有资源可能通过 Mirror 被访问。

所以:

Public Mirror 不要随便配置高权限 Docker Hub Account。


三十八、如果只缓存 Public Image 呢?

最简单:

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

不配置:

username
password

减少:

凭据泄露
私有 Repository 暴露

风险。

对于你的:

DockerHub 镜像集中营

这种项目场景:

公共镜像缓存更合理。


三十九、镜像仍然受到 Docker Hub 使用政策影响

这里也需要说明。

自建 Pull-through Cache:

不是无限绕过 Docker Hub 规则的神器。

Distribution 官方当前 Pull-through Cache 文档仍提醒:

Docker Hub Mirror

会受到 Docker Hub Fair Usage Policy 影响。

所以正确使用场景:

减少团队重复请求
提高稳定性
提高内网访问速度

而不是:

拿公共缓存无限刷 Docker Hub。

四十、Pull-through Cache 目前还有一个限制

Distribution 当前官方说明:

一个 Registry Mirror 一次只能 Mirror 一个 Upstream Registry。

比如这个实例:

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

就是:

Docker Hub Cache。

你不能再同时把同一个实例:

又指向 GHCR
又指向 Quay
又指向 Docker Hub。

四十一、如果想同时缓存多个 Registry?

最简单:

运行多个 Registry 实例。

例如:

dockerhub-cache.example.com
→ Docker Hub

ghcr-cache.example.com
→ GHCR

quay-cache.example.com
→ Quay

每个:

独立 config
独立 Storage
独立 Domain

更清晰。


四十二、但这里还有 Docker Daemon 的限制

Distribution 当前 Mirror 文档说明:

Docker daemon

通过 registry-mirrors 机制:

主要支持 Docker Hub Mirror。

不能直接把这一机制当成:

所有 Registry 的通用 Mirror 功能。

所以:

GHCR
Quay
ECR

这类 Registry 的缓存策略:

需要另外设计。

Harbor 在这一点上:

能力会更全面。


四十三、这就是 Harbor 和 Distribution 的真正区别

如果你的需求:

Docker Hub
↓
缓存
↓
几台服务器

Distribution:

非常合适。


如果你的需求:

Docker Hub

GHCR

ECR

公司内部镜像

权限

团队

扫描

审计

Web UI

Harbor:

更合适。


四十四、可以简单做一个对比

公共 Mirror

优点:

不用维护
开箱即用

缺点:

不可控
可能失效
可能限流

适合:

个人临时使用。

Distribution Pull-through Cache

优点:

非常轻量
部署简单
自己控制
Docker Hub 缓存透明
资源消耗低

缺点:

管理能力少
没有完整 UI
没有复杂 RBAC
一个实例一个 Upstream

适合:

个人
家庭实验室
小团队
少量服务器。

Harbor

优点:

Proxy Cache

Private Registry

用户权限

Project

Replication

Security

Retention

UI

缺点:

部署和维护成本更高。

适合:

团队
企业。

四十五、最小架构其实可以非常简单

比如一台:

2C2G
50GB Disk

的小服务器。

运行:

Nginx
+
Registry

架构:

                 Docker Hub
                     ↑
                     │
             registry:2
                     ↑
                     │
               Nginx HTTPS
                     ↑
          ┌──────────┼──────────┐
          │          │          │
       Server1    Server2    Server3

如果只是:

几十个常用镜像

这套就已经很好用了。


四十六、甚至可以和 Tailscale 结合

比如你的 Mirror:

只给自己的设备使用。

可以:

Docker Cache Server
↓
Tailscale
↓
MacBook
Linux Server
NAS

这样:

根本不需要把 Registry
公开到整个互联网。

安全性和运维复杂度:

都会下降。

四十七、比如访问地址

docker-cache.tailnet.ts.net

然后:

{
  "registry-mirrors": [
    "https://docker-cache.tailnet.ts.net"
  ]
}

自己的:

Mac mini

MacBook

云服务器

NAS

共享一套缓存。

对于个人开发者:

其实非常实用。


四十八、还有一个非常实用的场景:国内服务器 + 海外缓存节点

比如:

香港 

访问 Docker Hub:

很稳定。

你的国内服务器:

访问香港 比 Docker Hub 稳定。

那么:

Docker Hub
↓
香港 Registry Cache
↓
国内服务器

就成为:

自建 Docker Hub 加速层。

相比:

今天换这个 Mirror
明天换那个 Mirror

你自己:

控制域名

控制服务器

控制缓存

控制日志

稳定性更高。


四十九、如果缓存服务器本身访问 Docker Hub 也困难呢?

那当然:

它也解决不了。

Pull-through Cache 的前提就是:

Cache Server 到 Upstream 必须可访问。

如果 Cache Server:

同样访问 Docker Hub Timeout。

第一次 Cache Miss:

照样失败。

所以节点选址:

很重要。


五十、可以这样理解节点选择

用户到 Mirror:

链路 A

Mirror 到 Docker Hub:

链路 B

最终体验取决于:

A + B

第一次。

后续 Cache Hit:

主要只剩 A。

所以最理想:

Mirror 到 Docker Hub
网络好

用户到 Mirror
网络也好

这就是:

Cache Node 选址。


五十一、甚至可以做多个节点

例如:

香港 Cache

新加坡 Cache

东京 Cache

然后不同服务器:

选择最近节点。

不过一旦做到这里:

运维复杂度

就开始上涨。

个人项目:

一个稳定节点通常足够。


五十二、缓存目录一定要挂 Volume

这个坑特别容易踩。

如果 Compose:

services:

  registry:

    image: registry:2

什么 Volume 都没挂。

Registry 数据:

存在 Container Writable Layer。

然后:

docker compose down

重新创建:

缓存可能跟着 Container 生命周期丢失。

所以:

volumes:
  - ./data:/var/lib/registry

非常重要。

否则:

你搭的不是缓存服务器。

而是:

临时缓存 Container。


五十三、磁盘监控同样重要

例如:

du -sh \
  /opt/docker-registry/data

定期看:

5GB

20GB

80GB

如果增长特别快:

需要调整 TTL
或者扩容磁盘。

也可以接:

Node Exporter
Prometheus
Grafana

监控:

Filesystem Usage。

五十四、日志也别无限增长

Registry Container:

本身也会输出日志。

如果使用 Docker 默认:

json-file

又没有 Rotation,

Registry 访问量高:

日志同样可能越来越大。

例如 Compose:

services:

  registry:

    logging:

      driver: local

      options:
        max-size: "20m"
        max-file: "5"

这样:

Cache Data

和:

Registry Logs

都不会完全失控。


五十五、一个更完整的 Compose

可以写成:

services:

  registry:

    image: registry:2

    container_name:
      dockerhub-cache

    restart:
      unless-stopped

    ports:
      - "127.0.0.1:5000:5000"

    volumes:

      - ./data:/var/lib/registry

      - ./config/config.yml:/etc/distribution/config.yml:ro

    logging:

      driver: local

      options:
        max-size: "20m"
        max-file: "5"

注意:

5000

只绑定:

127.0.0.1

然后:

Nginx

对外提供:

443。

安全性更合理。


五十六、为什么不直接暴露 5000?

如果:

5000

直接:

0.0.0.0:5000

那么互联网:

可能直接绕过 Nginx。

你前面配置:

HTTPS

认证

限流

就可能失去意义。

所以更好的架构:

Internet
↓
443 Nginx
↓
127.0.0.1:5000 Registry

这也是常见反代原则。


五十七、公开 Registry Mirror 最好再加访问控制

如果只是自己使用:

完全公开

意义不大。

因为别人发现以后:

可能拿你的服务器
当公共 Docker 加速站。

然后:

流量

磁盘

Docker Hub Pull

全部你买单。

所以至少考虑:

IP 白名单

Basic Auth

VPN / Tailscale

Cloudflare Access

等访问控制。


五十八、不过 registry-mirrors 和认证组合需要提前测试

因为:

Docker Daemon

对 Registry Mirror:

认证

重定向

TLS

都有自己的交互逻辑。

所以如果只是:

自己的几台服务器。

我更推荐:

网络层限制。

例如:

Security Group

Tailscale

内网 IP

往往比:

把 Registry Authentication
搞得特别复杂

简单。


五十九、DockerHub 项目可以怎么利用这个方向?

现在:

https://github.com/Rodert/DockerHub

主要是:

公共 Mirror 地址。

可以增加一个:

自建镜像源

章节。

分成:

Level 1
公共 Mirror

Level 2
registry:2 自建 Cache

Level 3
Harbor Proxy Cache

用户一眼就知道:

不同规模应该用什么。

六十、甚至可以提供一键部署脚本

比如:

curl -fsSL \
  https://your-domain/install-registry-cache.sh \
  -o install.sh

bash install.sh

脚本:

检测 Docker

创建目录

生成 config.yml

生成 docker-compose.yml

启动 Registry

检查 /v2/

输出 daemon.json 配置

最后:

✅ Docker Hub Cache Started

Mirror:
https://mirror.example.com

Client config:

{
  "registry-mirrors": [
    "https://mirror.example.com"
  ]
}

这会非常适合小白。


六十一、也可以做状态检测

例如:

Mirror Status:
🟢 Online

Upstream:
Docker Hub

Cache Size:
18.4GB

TTL:
7 days

Disk:
43%

Last Pull:
2 minutes ago

甚至:

Top Cached Images

虽然 Distribution 本身不是为了:

做漂亮 Dashboard。

但完全可以在外层:

自己做监控。

六十二、未来还可以做预热

比如常用:

nginx:latest

redis:7

mysql:8.4

node:24

golang:1.25

部署 Registry 后:

docker pull nginx
docker pull redis:7
docker pull mysql:8.4

通过:

已经配置 Mirror

自动:

Cache Warm-up。

这样团队第一次真正使用:

也不需要等上游下载。

六十三、甚至 GitHub Actions 可以每天预热

例如:

name:
  Warm Docker Cache

on:

  schedule:
    - cron: "0 2 * * *"

然后:

每天检查基础镜像
↓
Pull
↓
让 Mirror 更新热门缓存

不过:

不要无意义大量拉镜像。

只维护:

团队真正使用的 Base Images。

六十四、如果上游内容变了怎么办?

比如:

node:24

更新。

客户端下一次请求:

Mirror

会根据 Tag:

检查远程版本。

如果上游变了:

拉新内容
+
缓存新 Layers。

如果没变:

继续使用本地缓存。

这就是 Pull-through Cache:

比纯静态镜像拷贝方便的地方。


六十五、但是生产环境仍然建议固定版本

Cache:

解决下载问题。

并不解决:

latest 会变化

的问题。

例如:

FROM node:latest

即使有自建 Mirror:

内容仍然可能变化。

所以正式环境:

FROM node:24.10.0

甚至:

Tag + Digest

仍然更稳。

Cache 和版本治理是两件不同的事。


六十六、Pull-through Cache 不是备份

这也一定要讲清楚。

如果:

上游镜像消失

你本地:

碰巧还缓存了一份。

短期可能:

还能使用。

但 Cache 本身:

有 TTL
有清理策略
不是永久归档。

所以关键生产镜像:

不应该只依赖 Cache。

真正重要:

应该主动 Mirror 到自己的 Private Registry。

六十七、可以这样区分

Cache:

“如果有就快一点。”

Private Mirror / Archive:

“这是我要长期保留的。”

前者:

性能系统。

后者:

资产管理系统。

这个思路特别重要。


六十八、比如公司基础镜像

这些:

ubuntu:24.04

node:24

golang:1.25

eclipse-temurin:21

可以:

Pull-through Cache

提高日常速度。

但发布生产的:

company-api:v1.8.2

应该:

正式 Push 到企业 Registry。

不要:

把 Cache 当生产制品仓库。

六十九、最终我们可以得到一条非常清晰的演进路线

最开始:

docker pull 太慢

于是:

公共 Mirror

但:

公共 Mirror 会失效。

于是:

自建 Distribution Cache

团队继续扩大:

需要用户权限
扫描
审计
多 Registry

于是:

Harbor。

再继续:

多机房
Kubernetes
企业供应链

就进入:

完整 Registry Platform。

也就是说:

基础设施应该跟规模一起长。

不是:

个人两台服务器
也一定装一整套企业平台。

七十、三种方案最终怎么选?

如果你只有:

1~3 台个人机器

优先:

公共 Mirror。

如果:

3~20 台服务器

而且:

主要缓存 Docker Hub

我会优先考虑:

Distribution Pull-through Cache。


如果:

团队开发

Kubernetes

公司内部镜像

多 Registry

权限管理

我会倾向:

Harbor。

这套判断:

比单纯说 Harbor 更专业

因为:

没有一个方案适合所有规模。


总结

很多开发者遇到 Docker Hub 网络问题以后,会一直寻找:

下一个能用的公共镜像地址。

今天:

Mirror A。

明天:

Mirror B。

后天:

Mirror C。

但如果你已经拥有:

多台长期运行的服务器

一个更稳定的思路其实是:

自己成为自己的 Mirror。

Distribution Pull-through Cache 的核心架构非常简单:

Docker Host
↓
Local Registry Mirror
↓
Docker Hub

第一次:

Cache Miss
↓
访问 Docker Hub
↓
保存 Manifest / Layer

第二次:

Cache Hit
↓
直接返回

整个部署最核心的配置甚至只有:

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

再通过:

{
  "registry-mirrors": [
    "https://mirror.example.com"
  ]
}

让 Docker Daemon:

透明使用你的缓存节点。

相比 Harbor:

它更轻。

相比公共 Mirror:

它更可控。

相比每台服务器直接 Docker Hub:

它更节省重复公网流量。

如果只记住一句话:

公共 Docker Mirror 是“别人帮你缓存”,Distribution Pull-through Cache 是“你自己掌握缓存节点”。

对于:

https://github.com/Rodert/DockerHub

这个项目来说,我觉得这也是非常自然的一步:

公共 Docker 镜像列表
↓
镜像健康检测
↓
Docker Pull 排错
↓
一键自建 Distribution Mirror
↓
Harbor 企业级 Registry

这样项目就可以真正形成一条:

从“小白解决 docker pull 失败”到“自己搭 Docker Registry 基础设施”的完整路线。

项目地址:

https://github.com/Rodert/DockerHub
posted @ 2026-10-09 13:25  JavaPub  阅读(3)  评论(0)    收藏  举报