LANUNION ARCHIVELANUNION蓝色札记

← 项目索引

PROJECT ID · PRJ-002

MIRROR MESH:内网镜像源与缓存层

给所有内网机器一套可用的软件源:按机架就近命中、未命中才回上游、证书与上游同步。目标是让"装包失败"不再是一个需要人介入的事件。

状态 STATUSMAINTENANCE
VERSION1.8.0
LANGUAGEPython / nginx / Shell
LAST UPDATE2026.08.30
MIRROR MESH 数据流:客户端按机架就近命中镜像节点,未命中才回上游源站 CLIENTS HASH LAYER MIRRORS RACK-01 RACK-02 RACK-03 nginx 一致性哈希 MIRROR-A MIRROR-B 回源 UPSTREAM 定时同步 实线:命中路径(92%) 虚线:未命中与上游同步 客户端只配置哈希层一个源
ARCHITECTURE · mirror-mesh.svg

目标

内网有两百多台机器,其中大部分不能直连外网。在这个前提下,装包失败是最不该 发生的一类故障——它的原因通常在网络策略上,而表现却是编译器报错,排查路径很长。

这个项目把”取包”这件事收敛成一条可控的路径:客户端只配置一个源,剩下的事情 (就近、缓存、回源、证书)都在服务端决定。

数据流

上图是一条请求的完整走向。三个机架各自的客户端都指向哈希层,由它按机架固定到 一个镜像节点;只有缓存里没有的包才会回上游,且回源是串行的、限速的, 不会在发布的瞬间把上游打满。

命中率不是目标,回源的可预测性才是

92% 的命中率是个结果,不是被优化的对象。真正在意的是剩下那 8%:

  • 回源带宽有上限。 上游同步的是差分索引,不是全量仓库。
  • 回源失败不阻塞客户端。 拿不到就报 502,而不是让连接挂在那里等超时。
  • 缓存的淘汰是按大小而不是按时间,因为老版本的包偶尔还会被装回来。

部署

# 容器起停,配置全在仓库里
docker compose -f deploy/mirror-mesh.yml up -d

# 同步状态与最近一次上游时间
curl -s http://mirror.lanunion.example/-/status | jq '{last_sync, packages}'

节点本身的配置由 Ansible 下发,两个镜像节点之间不做副本——它们是互相独立的 缓存,这样任何一台宕机都只影响它自己那份缓存,不会连带。

已知限制

  • 不缓存容器镜像。 容器仓库的体积与索引结构差异太大,目前单独一套, 不在这个项目里。
  • 证书一年一换,换的时候客户端要一起改。 这一点反复出过问题, 处理办法见 LAB LOG 027。
  • 哈希层是单点。 它只是一个 nginx,重启在秒级,但重启期间所有装包都会失败。