↓Skip to main content
  1. Docs/

Dragonfly(一):入门、组件与工作原理

·8 mins· ·
Owl Dawn
Author
Owl Dawn
Table of Contents

Dragonfly 解决什么问题
#

假设要把一个 2 GiB 的镜像层下载到 100 台节点。如果所有节点都直接访问镜像仓库,仓库大约需要发送 200 GiB 数据,跨机房链路也可能被同时打满。

Dragonfly 的做法是:让一部分节点先获取数据,其他节点再从这些节点下载;下载过的节点还可以继续向别人提供数据。这就是 P2P 分发,可以理解为“节点互相帮忙下载”。

它主要改善的是相同内容的大规模重复下载,常见用途包括容器镜像、模型文件和软件包分发。它不能消除第一次获取新内容的成本,也不会自动加快镜像解压、容器初始化和应用启动。

如果文件很小、每次内容都不同,或者只有一台机器下载,P2P 带来的收益可能有限,甚至不及额外的调度和缓存开销。

本文介绍的是 Dragonfly P2P 分发项目,不是同名的 Redis 兼容数据库。

阅读顺序与版本
#

这一组文章分为三篇:

  1. 本篇:先建立整体认识,再解释组件和下载原理。
  2. 部署与使用:搭建测试环境,验证跨节点下载,再接入 containerd。
  3. 注意事项与排障:缓存、鉴权、容量、可用性和效果验证。

配置示例统一使用下面这组已发布版本,作为可核对的学习基线,不表示它们是最新版本或生产升级建议:

项目 版本
Dragonfly Helm Chart 1.7.0
Manager / Scheduler v2.5.0
Client / dfinit v1.4.0

它们的对应关系可在固定版本 Chart及其 values.yaml 中核对。阅读官方 next 文档时,要注意其中可能包含更新版本的行为。

先认识五个名称
#

名称 容易理解的说法 例子
Origin,源站 原始数据所在的位置 Harbor、其他 Registry、HTTP 文件服务器
Task,任务 Dragonfly 识别的一份待分发内容 某个镜像层的 blob 下载任务
Piece,分片 任务中可单独传输的一段数据 文件中某个偏移和长度的数据
Host,主机 运行 dfdaemon 的节点实例 worker-1 上的客户端
Peer,对等节点 某个 Host 对某个 Task 的参与记录 worker-1 正在参与下载文件 A

一台主机可以同时参与多个任务,所以 Host 与 Peer 不是一个概念。下载关系中的 Parent 是当前数据提供方,Child 是从它获取数据的一方;同一个节点可以一边下载,一边上传已经完成的分片。

组件各自做什么
#

dfdaemon:真正下载、缓存和上传数据
#

dfdaemon 是常驻客户端进程。在 Kubernetes 中,它通常随 Client DaemonSet 部署在工作节点上。

它主要做四件事:接收本机下载请求、从源站或其他节点获取分片、保存缓存、向其他节点上传分片。因此,节点不只是消费者,也可能成为数据提供者。

业务可以通过 dfget、HTTP 代理或集成接口使用它。dfget 是发起请求的命令行工具,后台真正承担缓存和 P2P 服务的是 dfdaemon。

Scheduler:告诉节点可以从谁那里下载
#

Scheduler 收集任务、主机和下载状态,为请求方选择合适的候选父节点。父节点失败或负载变化时,它还可以重新安排下载来源。

Scheduler 传递的是“节点信息、分片状态和调度结果”,不是文件内容。如果下载瓶颈在磁盘或节点出口,单纯增加 Scheduler 带宽通常解决不了问题。

Seed Peer:比较稳定的数据提供者
#

Seed Peer 不是另一套独立客户端,而是承担种子角色的 dfdaemon。Chart 中通常称为 seedClient,常用 StatefulSet 部署。

当某份内容第一次进入 P2P 网络,Seed Peer 可以先从源站获取数据,供其他节点下载。它适合安排在磁盘和带宽比较充足的位置,也常用于提前准备热门内容。

Seed 不必下载完整文件后才开始服务。某个分片准备好后,其他节点就可以获取它,让内容边下载、边扩散。

Manager:管理组件、配置和集群
#

Manager 负责管理 Scheduler 集群、Seed Peer 集群、组件注册与动态配置,还提供管理界面、API 和预热任务等能力。

它通常不参与每个分片的传输。已有客户端下载可能在 Manager 暂时不可用时继续工作,但新节点发现、配置更新和管理任务仍可能受影响,不能因此忽略 Manager 的可用性。

Manager 不是所有部署都必需。无 Manager 的轻量部署可使用本地动态配置连接 Scheduler,但这时需要自行管理发现和配置,也没有完整的集中管理能力。

dfinit:帮助接入容器运行时
#

dfinit 用于调整 containerd 等运行时配置,让镜像请求进入本机 dfdaemon。它是接入辅助程序,不负责日常分片调度或数据上传。

可以选择自动修改,也可以手工配置。是否重启运行时取决于版本和变更内容,不能在不了解影响的情况下直接启用所有节点自动修改。

把组件连起来看
#

Dragonfly 官方架构图:Manager、Scheduler、Seed Peer 与客户端的关系

图源:Dragonfly 官方 v2.5.0 架构文档。原图保留英文标注。

读图时先看三处:Manager 管配置,Scheduler 安排来源,Daemon 实际下载和提供数据;Seed Peer 也是以种子角色运行的 Daemon。图中箭头既有请求关系,也有控制关系,不能全部当作文件流向。这是组件关系图,不是协议清单:本文基线客户端的分片传输还涉及 TCP/QUIC,不能把图中的 “All Communication on gRPC Framework” 理解为所有文件字节都通过 gRPC 传输。

管理和调度部分通常叫控制面;真正传输文件字节的部分叫数据面。这一区分有助于定位瓶颈:注册和调度失败看控制面,文件传得慢则要继续检查数据来源、网络和磁盘。

完整部署通常还包含数据库和 Redis。数据库主要保存 Manager 的管理信息;Redis 用于相应缓存、作业和部分任务状态等功能。它们不是所有文件分片的存放位置,文件主要在 Peer/Seed 的本地存储或相应后端中。

一次文件下载经过哪些步骤
#

首次由 Seed 回源与后续节点复用已有分片的下载流程

第一次下载:网络里还没有这份内容
#

假设节点 A 要下载文件 model.bin:

  1. 应用向本机 dfdaemon 发起请求。
  2. dfdaemon 根据请求信息识别任务,检查本地是否已经有可用数据。
  3. 本地没有时,联系 Scheduler 寻找下载来源。
  4. 若还没有可用 Peer,Scheduler 可以触发 Seed 回源;在相应策略允许时,普通 Peer 也可能回源。
  5. Seed 下载并准备好分片,节点 A 开始从它获取数据。
  6. 节点 A 校验、缓存分片,并把数据交给本机调用者;已经完成的分片也可供其他节点下载。

具体分支受任务类型、策略和失败处理影响,不是每次都必须经过同一个 Seed。

后续节点下载:复用已有内容
#

节点 B 请求相同内容时,Scheduler 可以返回 Seed、节点 A 等候选来源。节点 B 从可用来源获取分片,不必让源站再发送一份完整数据。

缓存复用也分层次:

  • 本机命中:从本机 Dragonfly 缓存读取。
  • 其他 Peer 命中:经过节点间网络传输,减少源站流量。
  • 回源:没有合适数据源,或策略要求访问源站。

这三种情况成本不同,性能测试时应分别记录。

父节点故障:换来源,而不是一律重下整个文件
#

某个父节点中断时,客户端可以重试、改用其他候选节点,必要时请求重调度。符合策略时还可回源。

已完成分片可能继续复用,但最终能否成功仍取决于是否存在可用数据源。源站不可用、所有缓存又都缺失时,P2P 也不能凭空恢复内容。

为什么要分片
#

分片允许多个来源并行提供数据,也让下载中的节点尽早参与上传。某个分片失败时,可以只重试受影响部分。

例如,A 已有前两个分片,B 已有第三个分片,C 就可能分别向它们下载。它不必等待其中一个节点拥有完整文件。

代价是要管理更多状态、连接、校验和磁盘读写。分片越小不一定越快,并发越高也不一定越快;过多并发可能先压垮磁盘和节点网络。

调度为什么分两步
#

Dragonfly 官方两阶段调度图:Scheduler 筛选候选父节点,客户端按分片选择来源

图源:Dragonfly 官方 v2.5.0 Scheduler 文档。图中的 Stage 1 是 Scheduler 推荐候选父节点,Stage 2 是客户端为具体分片选择来源。

Scheduler 先从整体视角筛选候选父节点,例如考虑位置、负载和任务关系;客户端再结合分片是否可用、连接情况和实时负载,为具体分片选择来源。

可以理解为:Scheduler 推荐几家合适的店,客户端再看哪一家现在有货、排队更短。 这比固定所有分片只从一个节点下载更灵活。

调度关系通常组织成 DAG,也就是没有环的有向图,避免出现 A 等 B、B 又等 A 的循环依赖。候选关系存在,不表示父节点一定拥有所有分片,下载前仍需检查实际可用数据。

Dragonfly 怎样判断“这是同一份内容”
#

缓存能否共享,取决于任务标识。URL、相关下载参数、tag、application、分片配置等可能影响任务标识;具体以版本和请求入口为准。

两个常见问题是:

  • 同一个文件使用不同临时签名 URL,结果被识别成多个任务,重复下载。
  • 不同内容共用一个被过度简化的标识,结果错误复用缓存。

只有确认某些参数不影响内容时,才适合从标识计算中排除它们。URL 一直不变但内容会更新的文件,也需要明确的版本策略。不可变 URL、版本号或内容 digest 通常更容易管理。

镜像 blob 按 digest 识别内容时,可以更准确地复用相同镜像层;但鉴权和隔离仍需单独设计,不能把“内容相同”当作“所有人都有权读取”。

镜像加速并不是把整个镜像当一个文件
#

一次拉镜像通常涉及 manifest、配置和多个 layer blob。Dragonfly 可以按相应请求分发内容,多个镜像共享同一个层时,也可能复用该层缓存。

kubelet → containerd → 本机 dfdaemon → 其他 Peer / 源站
                      下载完成后
containerd → 校验内容、解压和准备镜像 → 启动容器

是否进入 P2P,取决于代理规则和请求类型。例如示例版本常见默认规则针对 blobs/sha256.*,不能看到经过代理就断言所有 manifest、认证请求也都进行了 P2P。

Dragonfly 主要减少重复传输;Nydus 等方案关注镜像格式和按需读取。两者可以结合,但“分发更快”和“不下载整个镜像就启动”是不同能力。

预热、缓存和持久化不要混为一谈
#

预热是提前下载内容,把成本从业务启动时挪到之前。预热成功后,普通缓存仍可能被 GC 回收;后续请求的任务标识和所属 P2P 集群不同,也可能无法复用。

普通任务缓存是可回收的加速数据,不应当作唯一存储。Dragonfly 还提供持久任务、持久缓存等能力,但它们的后端、副本和 TTL 语义不同,需要按功能单独配置。不能因为名称中有 persistent,就把它当成永不丢数据的对象存储。

下一篇从一个可控的 HTTP 文件开始验证这些过程:部署与使用。

图片来源与许可
#

本篇两张标注“官方”的图片直接取自 Dragonfly 官方文档仓库的 v2.5.0 目录,未修改原图。仓库快照固定为 991f992cbaf3d02daa49bb5b29a3e6160aa0fb72,以免后续更新造成图文版本混用。随文保留 Apache-2.0 许可证及图片来源记录。系列中的其他中文流程图为本文绘制。

参考
#

Related

Dragonfly(三):注意事项、性能验证与故障排查
·7 mins
Dragonfly(二):从文件下载到 containerd 镜像加速
·7 mins
Kubernetes 高可用集群部署
·4 mins
go 1.24 新特性
·2 mins
分布式浅谈
·12 mins
分布式
Kubenetes 搭建(使用 debian12)
·4 mins