在 AI 模型训练、高性能计算等对 I/O 敏感的场景中,底层文件系统的架构和性能将直接影响训练效率、资源利用率与整体成本。
Lustre 作为传统高性能文件系统,以极致性能著称;而 JuiceFS 则以云原生设计和对象存储集成为核心,提供更高的部署灵活性与经济性。为了帮助用户深入理解这两类系统在架构实现、性能优化和运维复杂度上的差异,我们撰写了这篇对比文章,供技术选型时参考。
01 架构对比
Lustre
Lustre 是一款专为高性能计算(HPC)环境设计的并行分布式文件系统,最初在美国政府资助下,由多个国家实验室联合开发,旨在支持大规模科学研究和工程计算任务。当前,Lustre 的主要开发与维护由 DDN(DataDirect Networks) 负责,广泛应用于超算中心、科研机构及企业级 HPC 集群中。Lustre 文件系统由以下几个核心模块组成:
- 元数据服务器(Metadata Servers,MDS):负责处理命名空间相关操作,如文件创建、删除、权限检查等。
- 对象存储服务器(Object Storage Servers,OSS):负责实际的数据读写,提供高性能的大规模 I/O 服务。
- 管理服务器(Management Server,MGS):作为全局配置注册中心,负责存储和分发 Lustre 文件系统的配置信息。MGS 在功能上独立于具体的 Lustre 实例。
- 客户端(Client):为用户应用程序提供访问 Lustre 文件系统的接口,实现标准的 POSIX 文件操作语义。
各组件通过 Lustre 专用的网络协议 LNet 连接,构成一个统一高效的文件系统整体。
JuiceFS
JuiceFS 是一个云原生分布式文件系统,其数据存储在对象存储中。社区版可与多种元数据服务集成,适用场景广泛,于 2021 年在 GitHub 开源(11.6K star)。企业版专为高性能场景设计,广泛应用于大规模 AI 任务,涵盖生成式 AI、自动驾驶、量化金融和生物科技等。
JuiceFS 文件系统包括三部分组成:
- 元数据引擎:用于存储文件元数据,包括常规文件系统的元数据和文件数据的索引。
- 数据存储:一般是对象存储服务,可以是公有云的对象存储也可以是私有部署的对象存储服务。
- 客户端:提供 POSIX(FUSE)、Hadoop SDK、CSI Driver、S3 网关等不同的接入方式。
架构差异
可以看出,Lustre 和 JuiceFS 在模块组成及功能划分上较为相似,但在具体实现上存在较大差异。
客户端
Lustre 采用 C 语言实现,其客户端模块运行在内核态;而 JuiceFS 使用 Go 语言开发,客户端通过 FUSE(Filesystem in Userspace)暴露文件系统接口,运行在用户态。由于 Lustre 客户端运行于内核空间,访问元数据服务器(MDS)或对象存储服务器(OSS)时无需进行用户态与内核态的上下文切换或额外的内存拷贝,从而显著减少了系统调用所带来的性能开销,在吞吐和延迟方面具备一定优势。
然而,内核态实现也带来了运维和调试的复杂性。相比用户态的开发环境和调试工具,内核态工具门槛更高,不易为普通开发者所掌握。同时,与 C 语言相比,Go 语言更易于学习、维护和开发,具备更高的开发效率和可维护性。
值得注意的是,JuiceFS 企业版 5.2 中引入零拷贝等技术优化,以减少系统调用开销,进一步提升性能。
存储模块
在数据存储方面,Lustre 和 JuiceFS 也采用了不同的实现方式。
Lustre 在部署时通常需要配置一块或多块共享磁盘来存储文件数据。这一设计源于其早期版本尚不支持文件级冗余( File Level Redundancy,FLR)。为了实现高可用性(HA),当某个节点下线时,必须将其文件系统挂载(mount)到对等节点(peer node),否则该节点上的数据块(Chunk)将不可访问。因此,数据的可靠性需依赖于共享存储本身的高可用机制,或用户自行配置的软件 RAID 实现。
为确保高可用性和数据一致性正常运行,部署 Lustre 前通常需要进行详细的规划。尽管,新版本的 Lustre 已支持 FLR,一个文件可以具有多个副本,但副本之间的数据一致性仍需通过手动命令进行同步和确认。此外,Lustre 支持使用 ldiskfs
或 ZFS
作为底层本地文件系统,因此使用 Lustre 时还需了解并配置相应的本地文件系统。
JuiceFS 利用对象存储作为数据存储解决方案,从而可享有对象存储带来的若干优势,如数据可靠性、一致性等。存储模块提供了一组用于对象操作的接口,包括 GET/PUT/HEAD/LIST 等,用户可以根据自己的需求对接具体的存储系统,既包括主流云厂商的对象存储,也支持如 MinIO、Ceph RADOS 等私有部署的对象存储系统。社区版 JuiceFS 提供本地缓存来应对 AI 场景下的带宽需求,JuiceFS 企业版使用分布式缓存满足更大的聚合读带宽的需求。
元数据模块
在元数据服务方面,Lustre 和 JuiceFS 都提供统一的命名空间和文件元数据管理功能。为了实现元数据访问负载的横向扩展,Lustre 自 2.4 版本起引入了 Distributed Namespace(DNE)功能,支持将单个文件系统的不同目录分布在多个元数据服务器(MDS)上。
Lustre 的 MDS 高可用性依赖于软硬件协同实现:
- 硬件层面:MDS 使用的磁盘需配置 RAID,以避免因单点磁盘故障导致服务不可用;磁盘也需具备共享能力,以便当主节点宕机时,备节点能接管磁盘资源。
- 软件层面:使用 Pacemaker 与 Corosync 构建高可用集群,确保任一时刻仅有一个 MDS 实例处于活动状态。
JuiceFS 社区版的元数据模块,与存储模块类似也提供一组操作元数据的接口,可以接入不同的元数据服务,包括 Redis,TiKV ,MySQL,PostgreSQL , FoundationDB 等不同类型的数据库。
JuiceFS 企业版使用自研高性能元数据服务,可根据负载情况来平衡数据和热点操作,以避免大规模训练中元数据服务热点集中在某些节点的问题(比如频繁操作临近目录文件的元数据)。目前,企业版支持的数据规模已达到千亿级别,更多技术细节请点击此处阅读。
02 文件分布对比
Lustre文件分布
Normal File Layout
Lustre 早期采用的文件分布方式被称为 Normal File Layouts。在该模式下,文件被切分为多个数据块(Chunk),并分别存储在多个对象存储目标(Object Storage Targets,OSTs)上,其策略类似于 RAID 0。
文件分布策略主要由以下两个参数控制:
- Stripe Count:指定文件可以同时分布到多少个 OST 上。该值越大,文件并行访问能力越强,但也可能带来额外的调度和管理开销。
- Stripe Size:定义在切换到下一个 OST 之前,每个数据块的大小。也就是说,写入达到设定的 Stripe Size 后,数据将被写入下一个 OST,这也决定了每个 Chunk 的粒度。
通过合理配置这两个参数,用户可以在性能与资源利用之间做出权衡,优化文件系统的 I/O 行为以适配具体的应用场景。
上图展示了一个 Stripe Count 为 3、Stripe Size 为 1 MB 的文件在多个 OST 上的分布方式。每个数据块(Stripe)采用轮询(Round-Robin)方式依次分布到不同的 OST 上。
这种文件布局方式的主要问题在于参数配置不可变:一旦文件创建,Stripe Count 和 Stripe Size 就无法修改。然而,文件大小在实际使用中通常是动态变化的,用户可以通过追加写不断扩展文件。这种不灵活的布局策略在实际场景中容易引发以下问题:
- 空间耗尽(ENOSPC):由于每个文件的 Stripe 固定分布在特定的 OST 上,当某个 OST 空间耗尽时,即便其他 OST 仍有剩余空间,也会导致整个文件写入失败。
- 磁盘使用不均衡:即使初始写入时文件数据在 OST 之间分布均匀,随着不同文件在不同时段被追加写入,各个 OST 的使用增长速率会出现差异,导致磁盘空间不平衡。一旦形成不均衡状态,由于文件分布不能更改,这种状态将长期存在并持续恶化。
因此,在大规模、动态增长的数据场景中,Normal File Layouts 的静态分布策略在灵活性和资源利用率方面存在明显限制。
Progressive File Layouts
为了解决 Normal File Layouts 在应对动态数据增长和资源分配方面存在的局限,Lustre 引入了一种新的文件分布机制,称为 Progressive File Layouts(PFL)。
PFL 支持为同一个文件的不同区段定义不同的布局策略,允许根据文件大小或访问模式的变化进行灵活的分布控制。通过配置多个布局段(Extent),用户可以为文件的不同范围指定不同的 Stripe Count 和 Stripe Size,从而实现更加合理的资源利用和负载分担。
这种机制具备以下优势:
- 动态适应文件增长:小文件可以使用较小的 Stripe Count 降低访问开销,而随着文件变大,可以启用更高的并发度以提升带宽利用率。
- 减缓磁盘不均衡问题:通过为文件的后续部分指定不同的 OST 分布策略,可以在一定程度上缓解因追加写入导致的磁盘负载不均。
- 提高空间利用率和灵活性:PFL 支持自动选择布局段,使得布局策略可以随文件内容增长而自动调整,无需用户手动干预。
相较于传统布局模式,PFL 更适用于需要高性能访问的大型文件场景。
如上图所示,该文件采用 Progressive File Layouts(PFL)进行分布,共分为三个布局段(Extent):
- 第一部分:Stripe Size 为 1 MB,Stripe Count 为 1,用于优化小文件或文件开头部分的访问效率;
- 第二部分:Stripe Count 提升至 4,提高并发 I/O 能力;
- 第三部分:Stripe Size 增加至 4 MB,Stripe Count 为 16,适用于大文件高吞吐量的访问需求。
尽管 PFL 引入了更具弹性的布局策略,但在实际运行中,仍无法完全解决磁盘使用不均衡问题。为此,Lustre 进一步结合 Lazy Initialization 技术,以实现更高效的资源调度。
Lazy Initialization 是一种延迟分配机制,指的是文件在首次写入前不分配实际的物理存储位置。结合该机制,系统可在检测到磁盘负载不均时,针对尚未分配的文件区域动态指定新的分布策略,将这些数据写入负载较轻的 OST 上。
通过 PFL + Lazy Initialization 的联合使用,Lustre 实现了更具适应性的 I/O 分布能力,显著缓解了由于追加写入或文件增长带来的磁盘空间不均衡问题,同时提升了系统的可维护性与数据分布的灵活性。
File Level Redundancy
如前文所述,Lustre 在实现高可用性(HA)时的部署和运维流程相对复杂。为简化 HA 架构并提升系统容错能力,社区提出了 File Level Redundancy(FLR) 机制。
FLR 允许为每个文件配置一个或多个副本(Replica),实现文件级别的冗余保护。在写入操作发生时,数据仅写入其中一个副本,其余副本会被标记为 STALE(过期) 。随后,系统通过一个称为 Resync 的同步过程,将最新数据复制到所有 STALE 副本中,以确保数据一致性。
在读取操作中,任何包含最新数据的副本都可以作为数据源,提高了读性能的并发性和容错能力。
这种机制 在不依赖共享存储或底层 RAID 的情况下,为 Lustre 提供了一种更加灵活和易于维护的数据冗余方案,适用于对高可用性有更高要求的大规模分布式环境。
JuiceFS 文件分布
JuiceFS 按照 Chunk、Slice、Block 的规则进行数据块管理。每个 Chunk 的大小固定为 64M,主要用于优化数据的查找和定位。实际的文件写入操作则在 Slice 上执行,每个 Slice 代表一次连续的写入过程,属于特定的 Chunk,并且不会跨越 Chunk 的边界,因此长度不超过 64M。Chunk 和 Slice 主要是逻辑上的划分,而 Block(默认大小为 4M)则是物理存储的基本单位,用于在对象存储和磁盘缓存中实现数据的最终存储。更多细节可以参考官网介绍。
JuiceFS 中的 Slice 是在其他文件系统中不常见的一个结构。主要功能是记录文件的写入操作,并在对象存储中进行持久化。对象存储不支持原地文件修改,因此,JuiceFS 通过引入 Slice 结构允许更新文件内容,而无需重写整个文件。这与 Journal File System 有些类似,其中写入操作仅创建新对象,而不是覆盖现有对象。修改文件时,系统会创建新的 Slice,并在该 Slice 上传完毕后更新元数据,从而将文件内容指向新的 Slice。被覆盖的 Slice 内容随后通过异步压缩过程从对象存储中删除,导致在某些时刻对象存储的使用量会暂时超过文件系统实际使用量。
此外,JuiceFS 的所有 Slice 均为一次性写入,这减少了对底层对象存储一致性的依赖,并大大简化了缓存系统的复杂度,使数据一致性更易于保证。这种设计还为实现文件系统的零拷贝语义提供了便利,支持如 copy_file_range 和 clone 等操作。
03 功能特性对比
对比项 | Lustre | JuiceFS 社区版 | JuiceFS 企业版 |
---|---|---|---|
元数据 | 分布式元数据服务 | 独立数据库服务 | 自研高性能分布式元数据引擎(可横向扩展) |
元数据冗余保护 | 需要存储设备提供 | 取决于使用的数据库 | 三副本 |
数据存储 | 自主管理 | 使用对象存储 | 使用对象存储 |
数据冗余保护 | 存储设备提供或异步复制 | 对象存储提供 | 对象存储提供 |
数据缓存 | 客户端本地缓存 | 客户端本地缓存 | 自研高性能多副本分布式缓存 |
数据加密 | 支持 | 支持 | 支持 |
数据压缩 | 支持 | 支持 | 支持 |
配额管理 | 支持 | 支持 | 支持 |
网络协议 | 支持多种网络协议 | TCP | TCP |
快照 | 文件系统级别快照 | 文件级别快照 | 文件级别快照 |
POSIX ACL | 支持 | 支持 | 支持 |
POSIX 兼容性 | 兼容 | 完全兼容 | 完全兼容 |
CSI 驱动 | 非官方支持 | 支持 | 支持 |
客户端 | POSIX | POSIX(FUSE)、Java SDK、S3 网关、Python SDK | POSIX(FUSE)、Java SDK、S3 网关、Python SDK |
多云镜像 | 不支持 | 不支持 | 支持 |
跨云和跨区数据复制 | 不支持 | 不支持 | 支持 |
主要维护者 | DDN | Juicedata | Juicedata |
开发语言 | C | Go | Go |
开源协议 | GPL 2.0 | Apache License 2.0 | 商业软件 |
04 小结
Lustre 是一款高性能并行分布式文件系统,客户端运行于内核态,直接与元数据服务器(MDS)和对象存储服务器(OSS)交互,避免了用户态与内核态之间的上下文切换。结合高性能存储设备,Lustre 在高带宽 I/O 场景下展现出卓越的性能。
然而,由于客户端运行在内核态,这使得运维过程更具挑战性,运维团队需具备深入的内核调试经验和底层系统故障排查能力。此外,由于 Lustre 使用固定容量的存储方案,文件分布设计相对复杂,需要精细的规划与配置来实现资源的高效利用。因此,Lustre 的部署和运维门槛较高。
JuiceFS 是一款云原生、用户态分布式文件系统,紧密集成对象存储,并原生支持 Kubernetes CSI(Container Storage Interface),从而简化了在云平台上的部署和运维。用户无需深入关注底层存储设备和复杂的存储调度机制,即可在容器化环境中实现弹性扩展、高可用数据服务。在性能方面,JuiceFS 企业版通过分布式缓存,有效降低对象存储的访问延迟,提升文件操作的响应速度。
从成本角度看,Lustre 需要依赖高性能的专用存储设备,初始投资和长期维护成本较高。对象存储相则更加经济,具备天然的可扩展性以及按需付费的灵活性。
希望这篇内容能够对你有一些帮助,如果有其他疑问欢迎加入 JuiceFS 社区与大家共同交流。