Donkey 上手指南:给 NAS 和家庭实验室做 Docker 镜像加速

虫子樱桃

发布于1天前
苔花如米小,也学牡丹开。

Donkey 上手指南:给 NAS 和家庭实验室的 Docker 镜像加速

如果你在 NAS 或家里的小服务器上拉镜像,多半遇到过这种画面:docker pull 卡在 Pulling fs layer 一动不动,或者干脆超时。官方源慢,公共镜像源又不稳定,换一个挂一个。

Donkey 就是冲着这件事来的。它是一个用 Rust 写的 Docker / OCI Registry 拉取代理,可以自托管、可审计,前端编译后直接嵌进二进制,跑起来不需要额外装 Node.js。它参考 KSpeeder 的公开功能说明重新设计,和 KSpeeder、LinkEase、Docker Inc. 都没有隶属关系。

https://appstore.lazycat.cloud/#/shop/detail/community.lazycat.app.donkey

为什么要自己搭一个

公共镜像源什么时候限速、什么时候只缓存热门镜像、什么时候直接下线,你都管不着。今天是它,明天可能就换了。

商店里原本有一个 KSpeeder,Donkey 的灵感来自它。不过它是闭源的,配置也偏繁琐,所以我做了一个适合懒猫设备的版本:

https://appstore.lazycat.cloud/#/shop/detail/peterpig.lzcapp.kspeeder

Donkey 的思路是把多个镜像源当成一个池子,按实际表现动态挑选,把拉下来的内容存在本地,下次直接从磁盘给。你既可以用它当 Docker 的镜像源,也可以把它当成一个独立的 Registry 来 docker pull。

运行概览:一屏看清节点和流量

运行概览

首页每 3 秒刷新一次,卡片分三组:上排是健康节点数、缓存容量、累计命中、当前调度模式;中排是代理请求数、代理流量、多源并发状态和重试次数;下方左侧是各节点的实时吞吐曲线,右侧是按综合分排序的「当前路由」列表。

「当前路由」这一列,每个节点后面跟着三个数字:实时速率、平滑速率和延迟。平滑速率是历史吞吐的加权估计,比实时速率更能反映一个节点的真实水平。一次抖动拉低不了它的分数,一个节点真的变慢了,它也会慢慢掉下来。

右边还标着 可用 或者 异常。截图里 actima 是异常状态,说明最近一次探测没连上上游,调度会先跳过它。等它恢复,或者你点一下卡片右侧的重试按钮手动测速,它才会重新参与派活。

加速节点:把镜像源管起来

加速节点

Donkey 把「逻辑 Registry」和「镜像端点」拆成两层。Docker Hub 是根命名空间,GHCR 用 /ghcr 前缀,你还可以加自定义的。每个命名空间下面挂一个或多个镜像端点,也就是真正的上游地址。

同一个命名空间挂多个端点,才是这套东西的价值所在。镜像的每个 Blob 会被切成若干分片,分片同时发往不同的节点下载,谁快谁多拿。某个节点中途断了,没下完的那一段会转到别的节点接着下,不用从头再来。一个 100 MB 的 layer,如果三个节点都在线,实际耗时可能只有一个节点的三分之一。

节点之间怎么分工,背后是一套打分。分数大致由三样东西决定:近期真实吞吐、成功率、以及当前还有多少任务挂在它身上。吞吐越高分越高,成功率越低分越低,正在忙的节点会被扣分,避免所有分片都挤在一个节点上。

有个细节值得单独说:调度会留出大约八分之一的机会,去试用那些空闲的、还没测过速或者刚恢复的节点。不然分数一旦定下来,新节点就永远没有出头之日。这也是为什么你新加一个节点后,它会很快被纳入调度,而不是等很久。

节点卡片上能看到实时速率、平滑速率、延迟、累计下载、并发上限和成功率。认证支持 Basic、Bearer 和自定义 Header 三种,密钥用 AES-256-GCM 加密后存进 SQLite,界面上永远不会回显明文。

缓存:内容寻址加授权隔离

缓存管理

缓存是内容寻址的,每个 Blob 按 digest 落盘,命中时可以直接响应 Range 请求。第二次拉同一个镜像,同样的 layer 直接从磁盘给,不用再出网。

拉下来的内容要过一遍 SHA-256。Donkey 一边收数据一边算哈希,收完比对 digest,多收了或者少收了都算失败,宁可重下也不把半个文件留在缓存里。落盘用的是先写临时文件、再重命名提交,所以不会出现「文件写了一半就被下一个请求读到」的情况。

另一个细节是并发去重。同一时刻有多个请求要同一个 Blob,Donkey 只放一个去回源,其余的等着拿结果。这个在拉一整套镜像的时候很常见,几十个请求同时打过来,实际只出一趟网。

带 Authorization 的请求,Donkey 会把整个头值不可逆地混进缓存 key。用不同凭据拉下来的私有内容因此不会互相串味,明文凭据本身也不会落盘。只有当某个命名空间的所有节点都没配认证时,才可能跨 Bearer 令牌轮换复用缓存,而且这个行为要显式打开开关。

回收策略有平衡、LRU、LFU 三种,配合高低水位使用。默认 90% 开始回收、回收到 80% 停止,容量上限 50 GB。正在传输的对象不会被回收,这点对并发拉取很关键。

拉取日志:谁在什么时候拉了啥

拉取日志

每次经过 Donkey 的镜像拉取都会记一条:镜像仓库、Tag、Registry 命名空间、解析出的 digest、时间和状态。日志只记 tag 和 digest,不保存认证信息和客户端凭据。

记录条数有上限,默认保留 30 天或 10000 条,两个条件谁先到算谁。这个模块可以整体关掉,关掉后页面隐藏,已有历史仍然保留。

日志里的 digest 比 tag 更有用。同一个 tag 可能被上游重新指到别的镜像,digest 才是内容的真实身份。这也是后面定时同步判定「要不要重传」的依据。

域名加速:不止镜像

域名加速

这块功能和镜像代理是两回事,它把 GitHub Release、软件包等公开下载站点的地址转换成加速链接。你填一条映射,填一个原始域名和一个上游地址,之后凡是命中这个域名的链接都能走加速。

和普通 Registry 代理的区别在于:域名加速按请求 Host 命中映射,由 Donkey 自己去取上游内容再原样回传,客户端看到的是 Donkey 的地址。它不缓存、不做分片,但会把客户端的 Range 头原样转发,所以断点下载这类语义是保留的。上游返回的 302 也会在服务端跟随,最多 3 跳。

这里有一层安全考虑:Donkey 在发请求前会先解析目标域名的 IP,解析结果落在内网或者云厂商的元数据地址就直接拒绝。也就是说,这个功能不会被人拿去当跳板扫你家里的内网。

镜像工具:浏览、导出、复制、定时同步

镜像工具

这是 Donkey 里最像运维工具的部分,它挂在管理端,不改变公共 /v2 代理的只读边界。

  • 内容:按平台解析镜像,合并 layer 后展示文件树,可以下载单个文件。想知道某个镜像里到底装了什么、有没有多余的调试工具,不用先拉下来再起容器。
  • 离线包:生成 Docker archive 或 OCI archive,拿到没有网络的机器上直接 docker load。
  • 复制:把镜像推到另一个 OCI Registry。推送前先检查目标端已有的 Blob,只补缺失的,最后才写 manifest。manifest 最后写,意味着镜像不会出现「一半传过去就能被拉到」的中间状态。
  • 同步:用 Cron 定期检查某个 mutable tag,源 digest 没变就跳过,变了才重新复制。

任务有 pending / running / completed / failed / cancelled / skipped 六种状态。接口支持 Idempotency-Key,同一个 key 重复提交会直接返回已有任务,不会重复跑。

服务重启后,租约过期的未完成任务会自动恢复。这里的租约默认 10 分钟,任务在跑的时候会定期续约。如果机器突然断电,续约断了,重启后这个任务会被捡回来重跑,而不是永远卡在 running。

源站、加速节点、目标 Registry 用的是三套完全独立的身份。节点凭据只会附加在发往该节点的请求上,不会流向目标 Registry。也就是说,你给加速节点配的密钥,不会在复制镜像时被带到目标端去。

参数设置:调优都在这里

参数设置

运行时参数保存到数据库,新请求和任务立即生效,环境变量只在首次启动时作为默认值。

  • 自适应分片:默认开启。分片大小不是固定的,Donkey 会按当前可用节点的并发能力反推,把单个 Blob 切成大概四波能干完的份数,再钳在 2 到 8 MiB 之间。节点多、带宽足就切大一点,节点少就切小一点。
  • 自动汇总节点并发:每个命名空间分别汇总启用节点的并发容量,当前最高算到 64。
  • 节点策略:速度优先按真实吞吐分配;稳健均衡适合节点表现稳定的环境。
  • 调度算法:当前算法适合稳定节点,预计完成时间算法会把延迟、成功率和在途负载一起算进去。节点之间速度差得越多,后者的优势越明显。
  • 淘汰策略:平衡 / LRU / LFU。平衡策略会同时考虑新鲜度、访问热度和对象大小,偏向留住常用的小对象。

页面还支持导入导出设置,换机器时可以整套搬走。

部署帮助:接到 Docker 上

部署帮助

Donkey 会按你当前的访问域名动态生成配置脚本,界面上直接给出替换好域名的命令。

最省事的是一键切换,Linux 下执行:

curl -fsSL https://donkey.你的懒猫设备名称.heiyu.space/helper | sudo sh -s -- configure

脚本只配置 Docker Hub 的 registry-mirrors,不会自动改写 ghcr.io 这类地址,也不会安装 Docker 或保存凭据。运行前可以先加 --dry-run 看改动,它会先备份已有的 daemon.json。

有一点要记牢:registry-mirrors 通常只接管 Docker Hub 拉取。GHCR 等其他 Registry 必须走命名空间路径:

镜像地址用法

docker pull donkey.你的懒猫设备名称.heiyu.space/library/alpine:latest
docker pull donkey.你的懒猫设备名称.heiyu.space/ghcr/owner/image:tag

如果不想动 Docker daemon,也可以直接把 Donkey 当独立 Registry 用:

docker login donkey.你的懒猫设备名称.heiyu.space
docker pull donkey.你的懒猫设备名称.heiyu.space/library/alpine:latest

关于

Donkey 用 Rust 写,前端是 React + Mantine,编译期把前端产物嵌进二进制。数据落在 SQLite 加文件系统缓存里,备份时整个数据目录一起打包即可。源码在 github.com/ca-x/donkey,MIT 协议。

它不绑定任何商业服务,节点、凭据和策略都由你自己配,装上就能给懒猫设备做镜像加速。

评论

0

暂无评论

说点什么呢~
收藏
0
0
0