问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

基于JuiceFS构建高校AI存储方案:高并发、系统稳定、运维简单

创作时间:
2025-01-21 23:53:21
作者:
@小白创作中心

基于JuiceFS构建高校AI存储方案:高并发、系统稳定、运维简单

中山大学iSEE实验室在进行深度学习任务时,遇到了NFS存储方案的性能瓶颈。本文将详细介绍实验室如何选择和部署JuiceFS,以构建一个高并发、系统稳定、运维简单的AI存储方案。

深度学习场景存储需求

实验室集群主要用于深度学习的训练和方法验证。在训练过程中,我们面临四种主要的读写需求:

  1. 训练过程中需要读取大量的数据集文件。
  2. 在模型训练的初始阶段,我们需要加载一个Conda的环境,这涉及读取众多不同大小的库文件。
  3. 在训练过程中,随着模型参数的调整,我们需要频繁地写入不同大小的模型参数切换文件,这些文件的大小可能从几十兆字节到数几字节不等,具体取决于模型的大小。
  4. 我们还需要记录(每次训练的)训练日志,这主要涉及到少量数据的频繁写入。

基于上述需求,我们对存储系统有以下四个主要期望:

  • 首要的是,系统在高并发的读写环境下应具备出色的稳定性。
  • 其次,我们希望系统能够消除单点故障,即任何节点的故障都不应影响整个集群的训练进程。
  • 第三,我们期望系统具有友好的运维特性。考虑到我们实验室团队成员主要专注于AI深度学习,对存储系统的专业知识了解有限,因此我们需要一个运维简便、维护频率较低的存储系统。
  • 最后,我们希望系统能够提供一个POSIX接口。由于我们使用PyTorch框架进行模型训练,如果系统支持POSIX接口,将极大地降低用户的学习成本,同时减少对现有代码的改动。

实验室现有的硬件配置

首先,我们的硬件系统主要包括三类设备。

第一类是数据节点,我们目前拥有大约三四台这样的数据节点。这些节点主要由大量机械硬盘构成,每个节点的机械硬盘都搭建RAID6阵列,所有节点加起来的总存储容量近700TB。

第二类是我们的计算节点,这些节点主要搭载了GPU,用于处理计算密集型任务。在存储方面,由于JuiceFS具有缓存功能,我们为这些节点配备了1至3个SSD缓存盘,每个盘的大小约为800GB。尽管这些缓存盘的大小看起来并不庞大,但它们通常能够缓存多个完整的数据集,前提是数据集的大小适中。

第三类是基于我们使用TiKV作为元数据引擎的考量,我们配置了三个TiKV节点,专门用于作为元数据引擎运行。这些节点的数据盘容量大约为2TB,内存则主要配置了512GB。根据目前的使用情况,当处理大约5至6亿的文件量时,每台节点大约使用了300多GB的内存。

NFS的存储解决方案所面临的挑战

在初期,当计算节点数量较少时,我们采用了在数据节点上构建单机文件系统的策略,并通过简单的NFS挂载方式,将这些文件系统挂载到各个计算节点的目录上。这样,当需要添加第二台数据节点时,我们只需将其挂载到新的目录上,即可实现数据在所有节点间的共享。此方案在运维上相对简单,但随着节点数量的增加,我们逐渐发现基于NFS的存储系统性能显著下降。

NFS挂载方案

在高并发训练高峰期,系统常出现明显的卡顿甚至卡死现象,这极大地影响了我们的工作效率。为了缓解这一问题,我们曾尝试将集群划分为两个小集群,以减少每个节点的数据压力。然而,这一临时措施并未带来显著的改进,反而带来了新的问题,如不同集群间数据的互通性受限。

总的来说,我们在处理大量小文件读取的场景下,原有的NFS存储方案表现出了较低的读取性能和较高的宕机风险。此外,整个系统缺乏缓存机制,而深度学习数据集在训练过程中需要频繁读取,这无疑增加了读写压力。因此,我们需要寻找一种更为高效、稳定的存储解决方案,以应对这些挑战。

为何选择JuiceFS

以下是我们选择JuiceFS的主要原因:

  • JuiceFS支持POSIX接口,这意味着在迁移系统时,用户几乎不会感受到任何影响,实现了无感的迁移体验。
  • JuiceFS的缓存功能对于深度学习模型的训练尤为重要。尽管在首轮数据加载时可能无法直接命中缓存,但随着训练的进行,后续轮次几乎能够百分之百地利用缓存,从而显著提升训练过程中的数据读取速度。
  • JuiceFS的回收站功能为用户提供了数据恢复的可能性,我们设置的为期一天的回收站使得误删的文件在一天内得以恢复,这在实际应用中已帮助用户及时恢复了误删的数据。
  • JuiceFS还配备了一系列特有的文件管理命令,这些命令不仅方便用户使用,而且相较于传统文件系统,其功能更为快速和高效。
  • 值得一提的是,在运维层面,JuiceFS的表现格外出色。由于我们实验室没有专职的人员负责存储,我们期望的存储系统应具备简单性和低运维频率的特点,而JuiceFS恰好满足了这些需求。它提供了丰富的文档资源,使得我们在上手和解决问题时能够迅速找到解决方案。JuiceFS能够自动备份元数据,为数据提供了额外的保护层,增强了我们的安全感。JuiceFS自带的Prometheus和Grafana监控功能使得我们能够方便地在网页端查看整个系统的状态,包括文件数量的增长情况、文件使用量以及整个系统的大小等关键信息,这为我们提供了及时的系统监控和管理的便利。

搭建基于JuiceFS的存储系统

首先,我们的JuiceFS采用了TiKV作为元数据引擎,同时利用SeaweedFS作为对象存储。

iSEE实验室存储架构

总体来看,JuiceFS分为两个目录进行挂载。第一个目录用于存储用户文件,采用独立挂载的方式使我们能够灵活调整挂载参数,以满足不同目录的特定需求。在用户文件目录方面,我们主要沿用了默认的挂载参数,以确保稳定性和兼容性。

第二个在数据集目录方面,鉴于其几乎只读的特性,我们特别增加了元数据缓存的失效时间。这样做可以在多次读取过程中多次命中元数据缓存,从而避免重复访问原始数据,显著提升数据访问的速度。正是基于这一改动,我们选择了将数据集和用户文件分别挂载于两个不同的目录。

此外,我们还进行了一项实践上的优化。考虑到计算节点的主要任务是处理计算任务而非后台任务,我们在一台相对空闲的节点上配备了较大的内存,专门用于处理后台任务,如备份元数据。随着文件数量的增加,备份元数据对内存的需求也逐渐增大,因此这种分配方式既确保了计算节点的性能,又满足了后台任务对资源的需求。

元数据引擎选型:Redis vs TiKV

起初,我们使用了两个数据引擎:Redis和TiKV。在数据量相对较小,即上千万级别的阶段,我们选择了Redis。当时,由于我们对这些软件并不十分熟悉,我们参考了相关文档,并考虑到Redis的上手难度较低、性能较高,且相关资料丰富,因此决定采用它。然而,随着文件数量的迅速增长,Redis的性能出现了显著的下降。

具体来说,由于我们为Redis设置了RDB持久化,当内存占用量增加时,Redis几乎持续处于RDB备份状态,这导致了性能的明显下滑。另外,我们当时采用了哨兵模式,并启用了主从复制以增加可用性,但这也带来了问题。由于是异步复制,主节点宕机后,从节点的数据一致性难以保证。

此外,我们了解到客户端并不会从Redis的从节点上读取元数据,而是主要依赖主节点进行读写操作。因此,随着文件数量的增加,Redis的性能进一步下降。

随后,我们考虑并测试了TiKV作为元数据引擎的替代方案。从官方文档来看,TiKV的性能仅次于Redis,并且在实际使用中,用户层面的体验与Redis相差不大。在数据量达到5至6亿文件的情况下,TiKV的表现相当稳定。

TiKV的另一个优势在于其负载均衡和冗余存储的能力。我们采用了三台节点的配置,每个节点都具备多个副本,确保了数据的安全性和可用性。对于运维团队来说,这些特性极大地减轻了工作负担,提高了系统的稳定性和可靠性。

元数据迁移:从Redis到TiKV

我们于今年一月份左右进行了系统迁移,并已经稳定使用TiKV近半年,期间未出现宕机或任何重大问题。

在迁移初期,由于Redis难以支撑高达5至6亿文件的元数据负载,我们决定采用导出与导入的方法来实现迁移。具体地,我们使用了特定的命令将Redis中的元数据导出为统一的JSON文件,并计划通过load命令将其加载到全新的TiKV系统中。

然而,在迁移过程中,我们遇到了一个挑战。我们注意到,某个用户的目录由于文件深度过深或其他原因,在导出时遇到了失败。为了解决这个问题,我们采取了一种创新的方法。我们将除问题目录外的其他所有目录分别导出,并手动打开和拼接这些JSON文件,以重新构建完整的元数据结构。

在手动处理这些文件时,我们发现元数据的JSON文件结构清晰,拼接操作相对简便。其嵌套结构与目录结构基本一致,这使得我们能够高效地处理元数据。最终,我们成功地将这些文件导入到TiKV中,完成了迁移过程。

为什么使用对象存储SeaweedFS?

总体而言,我们遵循了SeaweedFS官方文档中推荐的主要组件和基本功能,并未涉及过多新颖或高级特性。在具体部署上,我们采用了CPU节点作为核心,并在其上运行了master服务器。而数据节点则分布运行在不同的物理节点上,每个节点均运行volume服务以处理数据存储。此外,我们在相同的CPU节点上运行了filer服务器,该服务器为JuiceFS提供S3服务接口,负责与JuiceFS进行对接。从目前的运行情况来看,CPU节点的负载并不重,主要的数据读写和处理任务均分散在各个worker节点上。

对象存储架构

在数据冗余和备份方面,我们充分利用了SeaweedFS的冗余特性。逻辑上,我们将数据节点划分为两个Rack。当数据写入时,它会在两个Rack中都进行写入,确保在Rack 0和Rack 1中各有一份备份。只有当两个Rack都成功写入数据时,才视为写入成功。虽然这种策略使得我们的硬盘容量在逻辑上几乎减半(因为每份数据都要写两份),但它确保了系统的高可用性和数据安全性。即使其中一个Rack中的某个节点出现故障或宕机,也不会影响整体的读写操作和数据安全。

SeaweedFS的优缺点

首先,从上手难度来看,SeaweedFS相较于工业界广泛使用的Ceph,显得更为容易操作。其master、volume和filer等核心组件均可通过编写脚本来轻松启动。用户只需浏览对应的命令参数,并根据实际需求进行配置,随后通过脚本即可完成启动过程。尽管SeaweedFS的文档资料相对较少,但其易用性依然值得肯定。

此外,与另一款常用的对象存储系统Minio相比,尽管我们未曾直接使用过Minio,但根据以往案例和介绍,Minio在处理大量小文件时可能存在一定的不足。因此,我们在选择时并未考虑Minio。

SeaweedFS的另一个显著优点在于其安全性和可用性。通过将数据节点划分为两个Rack,系统实现了数据的冗余备份,从而提高了数据安全性。同时,其master服务器具备自动调度数据写入的功能,能够自动将数据分配到各个worker节点上,实现了负载均衡。此外,SeaweedFS的扩容过程也相对简单,只需新增机器并连接到master节点,系统即可自动进行扩容。

此外,SeaweedFS官方还推荐了一款master管理脚本,该脚本配置简单,仅需编写少量配置即可实现定期自动修复数据冗余和平衡数据分布的功能,极大地提高了系统的维护效率。然而,SeaweedFS的缺点也不容忽视。其文档资料相对较少且较为陈旧,这可能会给新用户带来一定的学习难度。尽管我们在使用过程中并未遇到其他明显的缺点,但这也是未来需要改进的地方。

方案实践中遇到的挑战

使用过程中客户端会异常退出

首先,我们曾遭遇客户端异常退出的情况,经过分析发现这是由于内存溢出(OOM)导致的。随着原数据的不断增长,当计算节点未启用
--no-bgjob
选项时,特定节点因执行高内存消耗任务(主要是自动元数据备份)而导致剩余内存不足,进而无法备份原数据,最终引发OOM并导致客户端退出。为了解决这个问题,我们在所有计算节点上添加了
--no-bgjob
选项,并利用闲置的数据节点作为专门的后台任务处理节点。

首次大文件的读取较慢

其次,我们在使用初期发现了一个性能问题。特别是在首次读取大文件时,发现读取速度远低于千兆网络带宽的极限。经过深入调查,我们发现这是由于在测试对象存储性能时,未正确配置JuiceFS命令的参数。我们没有指定
--storage s3
选项,导致测试默认为本地硬盘性能,而非实际的对象存储性能。这一误解导致了我们对对象存储性能的误判。通过进一步检查,我们发现SeaweedFS Filer元数据引擎的性能瓶颈,这主要是因为底层使用的是单个机械硬盘。因此,我们将考虑优化这一环节以提升性能。

解压数据集较慢(大量小文件写入)

最后,我们在日常使用中偶尔需要解压数据集,这涉及到大量小文件的写入操作。我们发现这一过程相较于本地解压明显较慢。在与JuiceFS官方沟通后,他们建议我们尝试使用write-back加速功能。这一功能允许在文件写入后立即返回,而后台则负责将数据上传到对象存储。我们计划在未来实施这一建议以优化解压性能。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号