在大规模 AI 推理服务中,当集群扩容时,新增推理实例从创建到承接请求,需要经历容器镜像准备、文件系统挂载、运行环境与推理框架初始化、模型权重加载等一系列冷启动过程。随着镜像体积和模型权重规模不断增长,数据准备的耗时日益突出;大规模扩容时,大量实例同时启动,还会进一步增加 Registry、网络和后端存储的并发压力。
针对这些问题,行业已经形成了多种优化思路,包括缩小镜像体积、通过 P2P 分发缓解并发拉取压力,以及通过按需加载减少启动关键路径上的数据传输。
本文进一步探索一种基于共享存储的方案:将 Nydus 的镜像按需加载能力与 JuiceFS 的分布式存储和缓存机制结合,通过共享存储和缓存复用减少大规模扩容中的重复数据读取,并缩短容器启动时间。对于 AI 推理场景,容器镜像和模型权重还可以统一纳入同一套存储、缓存和预取体系,在加速两类数据加载的同时,减少分别建设和维护两套数据链路带来的复杂度。
01 OCI 镜像分发为什么慢?
传统 OCI 镜像通常以分层压缩包的形式组织数据。在镜像拉取模式下,节点需要先完整下载并解压所需 Layer,才能启动容器。然而实践表明,容器启动阶段实际只访问镜像中约 6%~20% 的文件,其余绝大部分传输与解压开销都属于"非关键路径"上的无效等待。
在大规模集群中,这种"先全量准备、再启动"的方式会带来三个问题:
- 冷启动时间长:GB 级镜像(如 AI 推理镜像),全量下载和解压会显著延长新实例的启动时间,耗时动辄数分钟。
- Registry 瓶颈:当 HPA 爆发式扩容或大批量发布时,成百上千个节点同时向 Registry 请求 GB 级镜像 Layer,容易造成 Registry 性能瘫痪与网络拥堵。
- 网络与存储冗余膨胀:节点间缺乏高效的 P2P /分布式共享复用机制,相同 Layer 在集群网络中被反复重复传输,造成严重的带宽与存储空间浪费。
02 Dragonfly + Nydus:组合解决了什么,遗留了什么
面对大规模集群并发拉取镜像带来的 Registry 带宽压力,一种常见方案是引入 Dragonfly 等 P2P 分发系统。其核心思路是通过调度器和节点侧代理构建 P2P 分发网络,让节点之间共享镜像数据,从而分担 Registry 的出口流量。
但 P2P 分发主要优化的是数据传输路径,并没有改变传统 OCI 镜像需要先下载、解压所需 Layer 再启动容器的机制。Nydus 则从镜像加载方式入手,将"全量准备后启动"转变为"先获取轻量元数据,再按需读取实际数据",减少启动关键路径上的数据传输。
两者结合后,Nydus 负责按需加载,Dragonfly 负责分发按需读取的数据,在降低镜像实际读取量的同时,也缓解大量节点并发访问 Registry 的压力。
不过,这套组合也会引入额外的基础设施和运维复杂度。除了 Nydus Snapshotter,还需要部署和维护 Dragonfly 的调度、分发及节点侧组件,并处理 P2P 网络连通、缓存空间管理、节点异常以及缓存回收等问题。在较大规模的 GPU 推理集群中,这意味着团队需要额外维护一套独立于存储系统之外的数据分发和缓存体系。
这也带来一个新的思路:如果集群已经部署了成熟的分布式文件系统(如 JuiceFS),是否可以将镜像数据的分发和缓存交给共享存储体系,在复用已有存储基础设施的同时,减少额外 P2P 分发组件的建设和运维成本?
03 架构重塑:Nydus 数据块由 JuiceFS 承载
**Nydus + JuiceFS 方案的核心思路,将原本依赖 P2P 网络调度的镜像数据分发,转变为基于分布式文件系统的共享存储与缓存复用。**在该架构中,JuiceFS 作为底层统一数据引擎承载 Nydus 的镜像 Blob 数据块,Worker 节点则通过标准 POSIX 接口读取数据。
镜像通过 CI/CD 阶段构建并转换为 Nydus 格式后,会被拆分为两部分。
-
Bootstrap(元数据):体积极小(通常仅数百 KB,如 ~450KB),存放在 Image Registry 中,负责快速索引。
-
Blobs(实际数据块):涵盖所有实际文件内容(通常为数 GB,如 ~1GB),在转换完成后直接持久化到 JuiceFS 共享文件系统中(例如 /var/lib/nydus/blobs)。
当 Worker Node 启动容器时,containerd 触发 nydus-snapshotter,由 nydusd 通过 FUSE 挂载容器只读层。随后,当应用发起实际读请求时,nydusd 根据 Bootstrap 中的索引,通过 JuiceFS 挂载点按需读取对应的 Blobs 数据。
这条路径的关键变化是:Registry 仅负责 Manifest 和极轻量的 Bootstrap 分发;海量 Blobs 数据的传输完全交由 JuiceFS 的分布式缓存体系承载,从根源上消除了高并发拉取对 Registry 的网络瓶颈。
对于 AI 推理场景,这套架构还可以进一步统一镜像与模型数据的存储路径。除 Nydus 镜像 Blob 外,**模型权重也可以存储在 JuiceFS 中,**使两类数据复用同一套存储、缓存和预取机制,在加速数据加载的同时,减少分别建设和维护两套数据链路的复杂度。
| 维度 | OCI 全量镜像拉取方案 | Dragonfly + Nydus | JuiceFS + Nydus |
|---|---|---|---|
| 底层核心思想 | 镜像 Layer 全量拉取 + 分层解压 | 网络层 P2P 分流 + 镜像按需加载 | 存储层解耦收敛 + 分布式缓存 + 按需加载 |
| 容器冷启动方式与耗时 | 需等待全量下载并解压 一般需要 30s ~ 10min+ | 仅下载数百 KB 的 Bootstrap 元数据和启动所需的 Blobs 耗时通常需秒级 | 仅下载数百 KB 的 Bootstrap 元数据和启动所需的 Blobs 耗时通常需秒级 |
| 镜像数据传输路径 | Registry → Worker 本地完整下载与解压 | Registry / 数据源 → Dragonfly P2P 网络 → Worker 按需读取 | JuiceFS → Worker 通过 POSIX 接口按需读取,由本地及分布式缓存加速 |
| 核心组件 | Registry + containerd | Nydus, Manager, Scheduler, Seed Peer, dfdaemon 等组件 | JuiceFS + nydus-snapshotter |
| 镜像与模型权重协同方式 | 镜像从 Registry 拉取至节点本地,模型权重通过 NAS、对象存储等独立链路加载,两类数据分别管理 | 镜像通过 Nydus 按需加载,并由 Dragonfly 优化分发;模型权重仍依赖独立的外部存储链路 | 镜像 Blobs 与模型权重统一存储于 JuiceFS,并复用同一套缓存和预热机制 |
| 运维复杂度 | 大规模并发时,易压垮 Registry 带宽 | 需要额外维护 P2P 调度、节点网络及缓存体系 | 可复用已有 JuiceFS 存储与缓存基础设施,减少独立镜像分发组件 |
| 节点本地磁盘压力 | 高 需要存储全量镜像及解压后的本地数据 | 高且不可控 需作为 P2P 节点开辟 Cache 空间,剧烈扩缩容易 Disk Full | 可控 统一共享存储池,使用本地 NVMe 盘组成 P2P 分布式缓存 |
| 网络环境要求 | 普通 TCP | 要求节点间大范围开放 P2P 端口,跨 VPC/NAT 环境易失效 | 标准局域网,节点间拥有常规局域网高带宽即可,无侵入性网络要求 |
| 适用场景 | 无冷启动要求、集群规模小(< 20 节点)的通用业务 | 跨数据中心/跨云/边缘计算等无统一高带宽存储的超大规模集群;团队有较强的运维管理能力 | 跨数据中心/跨云/边缘计算等无统一高带宽存储的超大规模集群已部署或适合部署 JuiceFS,希望实现"镜像+模型"统一管理并极简运维 |
04 运行机制:Nydus 与 JuiceFS 如何协同
启动阶段:获取 Bootstrap(元数据)
应用启动时,nydusd 只需要从 Registry 获取轻量的 Bootstrap 文件树。Bootstrap 描述镜像文件树和数据块索引,但不包含任何实际文件内容。
容器根文件系统(Rootfs)挂载完成后,即可进入后续启动流程。在我们的测试场景中,容器可以在数百毫秒内完成拉起并通过健康检查,无需在启动前完成大体积镜像数据的全量传输。
读取阶段:按需访问 Blobs(数据块)
当应用进程真正发起读操作(如 open() / read())时,nydusd 通过 Linux FUSE 机制捕获 I/O 请求,根据 Bootstrap 索引定位到对应的 Blobs 数据块与偏移量(Offset)。
对上层应用来说,这个过程是完全透明的;对底层数据路径来说,读取粒度从完整镜像层变成了指定文件和偏移量(Offset)对应的数据块。nydusd 直接访问 JuiceFS 挂载点(如 /var/lib/nydus/blobs)读取目标块,彻底解决了运行时向 Registry 频繁 Fetch 的链路。
缓存阶段:通过 JuiceFS Cache Group 复用数据
JuiceFS 的分布式缓存组机制(Cache Group)可以在集群内复用已经缓存的 Blobs 数据。当缓存命中时,数据以近乎本地 NVMe 的速度回传给内核;当缓存未命中时,仅按需拉取指定偏移量的数据块并自动充填缓存。除了缓存未命中的自动填充机制,也可以采用提前预热 Blobs 数据到缓存的方式来保证按需读取 Blobs 数据块过程中能够全部命中缓存。
对于 AI 推理场景,同一机制也可以用于模型权重文件的加载流程。
05 生产场景实测数据
生产环境中,我们针对大体积 AI 推理镜像,对上述方案进行了对比测试。
1. 容器启动耗时对比
| 场景 | OCI overlayfs | Nydus + JuiceFS | 备注 |
|---|---|---|---|
| Cold Pull + Run | 116s | 16s | OCI 使用公有云 Registry 服务; Nydus + JuiceFS 使用 JuiceFS + 公有云对象存储服务 |
| Cold Pull + Run (分布式缓存预热) | N/A | 约 1.4s | 启动所需 Blobs 数据已预热至 JuiceFS 分布式缓存 |
| Warm Pull + Run | 约 0s | 0.22s | OCI 为同 tag 已在节点; Nydus + JuiceFS 为 blobcache 本地缓存 |
- 无缓存预热时,Cold Pull + Run 加速约 7 倍: 在本地缓存和分布式缓存均未预热的情况下,Nydus + JuiceFS 将容器启动耗时从传统 OCI 方案的 116 s 缩短至 16 s。
- 启动阶段数据读取量减少约 99.9%: 在 Cold Pull + Run 场景下,启动阶段的数据读取量从 11 GB 降至 11 MB。传统 OCI 方案需要在启动前完成镜像 Layer 的下载和解压,而 Nydus + JuiceFS 只需获取 Bootstrap 元数据,并按需读取启动过程中实际访问的 Blobs 数据。
- 分布式缓存预热后达 1.4 秒极速启动 :当 Blobs 数据在 JuiceFS 分布式缓存命中时,容器启动耗时为 1.4 秒;而在节点本地存在
blobcache的热启动(Warm Pull + Run)场景下,启动仅需 0.22 秒。
2. 文件按需读取性能
| 场景 | 传统 OCI(本地磁盘直读) | Nydus + JuiceFS(命中分布式缓存 Cache Hit) |
|---|---|---|
| 文件首块数据读取延迟(TTFB) | 约 0.9 ms | 约 3.2 ms |
| 小文件 / 散碎配置加载(IOPS) | 基准 | 贴近基准 |
| 大文件顺序读取吞吐(Throughput) | 本地磁盘上限 | GB/s 级别(JuiceFS NVMe 分布式缓存) |
- 首字节响应延迟(TTFB):分布式缓存命中时,首块数据读取延迟约为 3.2 ms,相比传统本地磁盘直读的 0.9 ms 增加约 2.3 ms。额外延迟主要来自 FUSE 数据路径和缓存访问,但整体仍保持在毫秒级,业务侧基本无感。
- 散碎小文件加载(IOPS):性能表现高度贴近本地磁盘基准。在 Python 依赖库及配置文件密集读取的场景下,JuiceFS 缓存机制未带来任何明显的 IOPS 瓶颈。
- 大文件顺序读取(Throughput):**Nydus + JuiceFS 在缓存命中时可达到 GB/s 级顺序读取吞吐。**相比直接受单节点本地磁盘性能约束的传统 OCI 路径,JuiceFS 可以利用 NVMe 分布式缓存提供高带宽的数据访问能力。
06 小结:按需加载与共享缓存的结合
Nydus 将镜像从全量拉取改为按需读取,JuiceFS 则将数据块放入共享存储和分布式缓存体系,两者结合后,容器冷启动耗时从 116s 降至 16s,分布式缓存预热后约为 1.4 s;启动阶段的数据读取量也从 11 GB 降至 11 MB。
这一方案的价值不只在于加速镜像启动。对于 AI 推理场景,镜像 Blobs 与模型权重可以统一由 JuiceFS 承载,并复用同一套缓存和预取机制;在大规模扩容时,分布式缓存还可以减少多个实例对相同数据的重复读取,降低 Registry 和后端存储的并发压力。
在缺少共享存储的环境中,Dragonfly + Nydus 仍是优秀的 P2P 替代方案;而在已有 JuiceFS 基础设施的集群中,Nydus + JuiceFS 能够以极低的运维成本,实现极致的镜像分发与模型加载加速。
我们希望本文中的一些实践经验,能为正在面临类似问题的开发者提供参考,如果有其他疑问欢迎加入 JuiceFS 社区与大家共同交流。