AI 场景存储优化:云知声超算平台基于 JuiceFS 的存储实践

2022-06-28
吕冬冬
本文作者介绍: 吕冬冬,云知声超算平台架构师,负责云知声大规模分布式机器学习平台架构设计与新功能演进,负责深度学习算法应用优化与 AI 模型加速。研究领域包括大规模集群调度、高性能计算、分布式文件存储、分布式缓存等。云原生开源社区爱好者。

01 背景

云知声是一家专注于语音及语言处理的技术公司。Atlas 超级计算平台是云知声的计算底层基础架构,为云知声在 AI 各个领域(如语音、自然语言处理、视觉等)的模型迭代提供训练加速等基础计算能力。Atlas 平台深度学习算力超过 57 PFLOPS(5.7 亿亿次/秒,是的你没有看错,是亿亿次图片),深度学习算力是衡量一个 AI 平台计算性能的核心指标。除了满足公司内部的业务需求,平台也为外部企业和院校机构提供定制化计算服务。

云知声 Atlas 平台架构图
云知声 Atlas 平台架构图

本文主要分享云知声 Atlas 超算平台(以下简称 Atlas)的存储建设历程以及基于 JuiceFS 建设高效存储的实践。

02 存储建设历程

一个性能卓越的超算平台,不仅需要充足的算力支持,也离不开高效的存储系统。结合 Atlas 上的任务特点和类型,高效存储系统应具备几个特点,如:满足多种类型的结构化与非结构化数据存储需求、兼容 POSIX 接口、海量小文件场景下具有较好的性能等。

在最早期进行 Atlas 超算平台建设的时候,我们尝试部署过 CephFS,开源版的 CephFS 在存储规模达到几千万小文件的时候,开始出现较为严重的性能问题,用户在操作文件时会遇到卡顿甚至在高 IO 的场景下整套存储系统会直接卡死,用户体验不太好。

后期,我们转到了 HPC 领域使用较为广泛的 Lustre 分布式文件存储系统, 构建了多套不同规模的 Lustre 集群,作为平台核心的存储系统,生产环境上目前主要有 40G 以太网与 100G InfiniBand 类型的集群,Lustre 分布式存储支持着用户在 Atlas 超算集群中进行数据处理、模型训练、源码编译与调试、数据归档等一系列数据操作。但是受限于 Lustre 在高并发请求下的性能瓶颈,无法满足对带宽与 IOPS 要求较高的场景需求。因此我们采用 Alluxio + Fluid 进行 IO 加速,分布式缓存给我们带来了 AI 模型训练速度上的提升以及存储系统总带宽的下降。

但是以上方案依然不是我们认为的最终方案,因此我们也在探索新的存储系统。在我们的场景上对这个新存储系统的核心需求是

  • 运维要足够简单:存储研发人员要能够较快的上手,运维人员后期的扩容、故障处理足够简单。Lustre 提供了 IML 一系列的自动化部署与监控工具,在运维方面较为方便。但是由于 Lustre 的软件代码是在内核上运行,如果出现故障,问题定位就显得不那么直观,需要从内核消息这边定位,大部分操作涉及重启操作系统;
  • 数据可靠性:数据是 AI 公司宝贵的资产,算法工程师在存储上的数据要足够稳定与安全。Lustre 目前不支持文件系统级的冗余,只能通过硬件的 RAID 来抵御硬盘故障;
  • 客户端多级缓存功能:构建大规模数据存储系统(PB 量级以上)为了考虑成本数据大部分会存储在 HDD 上,为了自动区分冷热数据,并充分利用我们 GPU 服务器的接近 TB 级的内存与大量的独立 SSD 盘,我们希望具备客户端多级自动缓存功能 ,以应对高密集 I/O 的读写场景;
  • 社区活跃度:社区活跃度也是我们考虑的因素,活跃的社区在功能版本的迭代与 bug 的解决方面能有更快的响应。

03 初识 JuiceFS

云知声团队在 2021 年初了解到了 JuiceFS,并跟 Juicedata 团队进行了早期的方案对接、PoC 测试,目前 JuiceFS 已经上线到生产环境,我们也参与到 JuiceFS 开源社区的建设中。

JuiceFS 的架构与优势

JuiceFS 整体的架构由元数据引擎、对象存储集群以及 JuiceFS 客户端组成,其中元数据引擎与对象存储提供了多种方案供用户选择。通过 JuiceFS 存储的数据将持久化到对象存储(如 Amazon S3)中,相应的元数据可以根据场景和需求持久化到 Redis、MySQL、SQLite 以及 TiKV 等各种数据库引擎中。

JuiceFS 架构图
JuiceFS 架构图

不管是元数据引擎还是对象存储都有很多成熟的方案可以选择,如果是在公有云上使用通常也有全托管的服务开箱即用。JuiceFS 的元数据自动备份、回收站等特性一定程度上保障了数据的可靠性,避免一些意外情况导致数据丢失,当然如果是自己运维元数据引擎和对象存储依然需要做好数据备份。JuiceFS 的本地缓存特性可以自动将频繁访问的数据缓存到内存以及磁盘中,同时也会对文件的元数据进行 缓存

PoC 测试

PoC 测试我们主要是在小规模环境上做可行性验证,关注的点是产品特性、运维方式、与上游调度、业务框架对接是否可行等。

PoC 测试环境我们搭建了一个单节点的 Redis + 3 节点的 Ceph 对象存储集群,在环境搭建方面因为 Redis 跟 Ceph 都比较成熟,部署运维方案可以参考的资料也比较全,而 JuiceFS 客户端能够以较为简单的方式对接元数据引擎跟对象存储。

业务的适配方面,JuiceFS 完全兼容 POSIX 协议,我们上层的业务可以无缝切换与对接,业务使用无感,JuiceFS 也支持以 CSI Driver 这种云原生的方式调度,与我们整个平台的技术栈契合。

在性能测试方面,我们在测试环境进行了文字识别模型的训练,实验环境为:模型采用服务器版中文识别模型 backbone 为 ResNet-18,数据整体总量是 98G 采用 LMDB 格式存储,在 6 张 NVIDIA Tesla V100 进行了三组实验,分别是:

  • 直接在 Lustre 读
  • 在带有 200G 内存缓存的 JuiceFS 读
  • 在带有 960G SSD 缓存的 JuiceFS 上读
耗时对比
耗时对比

JuiceFS 客户端拥有多级缓存功能,因而在性能测试中,在数据读方面有较大性能提升,相比 Lustre 性能有1倍以上的提升,这与我们的业务特点比较契合。

综合考虑运维方式、业务契合度以及性能表现,我们决定将 JuiceFS 带上生产。

04 JuiceFS 在 Atlas 的使用场景与收益

JuiceFS 客户端多级缓存目前主要应用在我们的文字识别、语音降噪以及语音识别场景。由于 AI 模型训练的数据读取特点是读多写少,我们充分利用 JuiceFS 客户端的缓存带来 IO 读取的加速收益。

收益一:加速 AI模型训练

1)语音降噪测试

降噪场景模型的测试中使用的是散文件,每个数据都是 wav 格式的小于 100k 的语音小文件,在降噪场景我们测试了数据 dataload 阶段的 I/O 数据,JuiceFS 客户端节点的内存缓存为 512G,在 500h 规模的数据下、以 40 的 batch size 进行测试。

Lustre vs. JuiceFS 每秒处理数据量对比
Lustre vs. JuiceFS 每秒处理数据量对比

从测试结果来看,单从数据读取效率上,在 wav 小文件方面,JuiceFS 为 6.45 it/s,而 Lustre 为 5.15 it/s,性能提升25%。JuiceFS 有效加速了我们端到端的模型训练,整体缩短了模型的产出时间。

2)文字识别场景

在文字识别场景中,模型为 CRNN backbone 为 MobileNet v2 ,测试环境如下:

模型 CRNN backbone 为 MobileNet v2
深度学习框架 PyTorch
数据规模 3*32*320 的图像生成的 50G LMDB 数据
数据类型 LMDB
GPU Server 6 * NVIDIA Tesla V100 / 512G MEM / 56 CPU Cores
缓存 200G 的内存缓存
Batch size 6*400
Lustre vs. JuiceFS 每个 batch 耗时对比
Lustre vs. JuiceFS 每个 batch 耗时对比

在这个测试中,主要做了 JuiceFS 跟 Lustre 的速度对比,从实验的结果来看从 Lustre 读每个 batch 耗时 1.5s,从 JuiceFS 读每个 batch 耗时为 1.1s,提升36%。从模型收敛的时间来看,从 Lustre 的 96 小时下降到 JuiceFS 的 86 小时,使用 JuiceFS 能够将 CRNN 模型的产出时间缩短 10 小时。

收益二:加速 AI模型开发

模型开发示意图
模型开发示意图

在算法工程师将 AI 模型训练任务正式提交到超算集群之前,其模型需要经过大量的调试,我们为用户提供了调试环境,Dev Node 跟 Atlas 正式训练集群一样都是使用相同的存储,开发节点与训练节点都挂载 JuiceFS 客户端,因此在开发机的修改能够无缝迁移到 Atlas 训练集群。

用户在开发机上可以灵活地选择开发环境,既可以在宿主机搭配 Anaconda 进行远程调试,也可以使用容器的方式运行开发环境。用户的调试模型大部分是 PyTorch 跟 TensorFlow 类型的框架,我们发现在调试的时候需要频繁地 import Python 包,例如 numpy、torch 等,这种包都是大量的小文件组成的,基于旧的存储系统,用户 import 包的耗时需要几秒或者几十秒。算法人员反馈模型调试的效率比较低。作为统一开发环境,伴随着大量的安装包导入、代码编译、日志读写、样本下载,这要求调试机既能有较高的吞吐量,又能快速处理大量小文件。

通过引入 JuiceFS,我们在开发机上挂载了 JuiceFS 客户端,客户端挂载时候使用元数据缓存以及数据读缓存机制。在元数据缓存方面,当 JuiceFS 客户端使用 open() 操作打开一个文件的时候,其文件属性(attribute)就会自动缓存在客户端内存中,只要缓存未失效则随后执行的 getattr() 跟 open() 操作都会从内存缓存中立即返回结果。在执行 read() 操作的时候文件的 chunk 和 slice 信息也会自动缓存在客户端内存。数据缓存方面我们使用内存作为缓存介质,采用该方式用户调试的 Python 包在经过第一次 import 之后会全部缓存在内存上,第二次调试的时候,直接从内存缓存读取文件。相比之前的方式,整体速度有 2-4 倍的提速 ,极大地提高了用户的调试效率,用户的体验也更加好。

05 JuiceFS 在 Atlas 的使用方式

在数据的存放管理方式上,我们采用兼容现有分布式存储系统的管理方式,JuiceFS 集群的节点也都是对接 LDAP,每个节点会通过 LDAP 的客户端与 LDAP Server 集群进行交互认证。

超算平台上的每个组归属于不同的目录,每个目录下是各自组内或者部门内的成员,不同组之间的目录是不可见的。目录的权限是基于 Linux 的权限管控机制。用户在 Atlas 集群提交训练任务的时候,集群的任务提交工具会自动读取系统上用户的 UID 与 GID 信息然后将其注入用户提交的任务 Pod 的 securityContext 字段,则 Atlas 集群上运行的容器 Pod 内所有容器的进程运行的 UID 与存储系统上的信息一致,保证权限不越界。

存储权限认证架构图
存储权限认证架构图

在数据的访问方式上,云知声目前有 2 种使用方式: 1. 一种是通过计算节点的 HostPath 访问数据; 2. 另一种是更加云原生的方式,通过结合 Fluid + JuiceFS 对利用 JuiceFS 客户端为 Atlas 的应用提供数据的访问与加速。

1)HostPath Volume

HostPath Volume 数据读取示意图
HostPath Volume 数据读取示意图

第 1 种还是沿用之前的访问分布式文件存储系统的方式,通过 Kubernetes HostPath 的方式直接访问本地的存储系统客户端,我们在所有的 CPU 与 GPU 计算节点都部署了 JuiceFS 的客户端,用户提交计算任务的时候需要指定 Kubernetes volume 为 HostPath 的方式,将 JuiceFS 的目录映射。这种方式的缓存管理就比较裸,在用户侧是无法对缓存进行管理的。

2) Fluid + JuiceFS

第 2 种方式是结合 Fluid + JuiceFS 的方式,关于如何使用的具体方式可以参考我们之前的文章 ,这里仅对架构做个简单的说明。

基于 Fluid 数据读取示意图
基于 Fluid 数据读取示意图

Fluid 会启动 JuiceFS 相关的组件包括 FUSE 跟 Worker pod,其中 FUSE Pod 提供了 JuiceFS 客户端的缓存能力,Worker Pod 则实现了对缓存生命周期的管理,Atlas 平台的 AI 离线训练任务通过与 FUSE Pod 客户端交互,进行 AI 训练数据的读取,通过 Fluid 提供的缓存调度能力以及数据集的可观测性,平台的用户可以通过亲和调度将缓存部署在特定的计算节点上,同时用户能够直观的看到缓存的使用情况(例如缓存数据集的大小、缓存的百分比、缓存的容量等)。

06 JuiceFS 存储生产环境建设

元数据引擎

目前我们生产环境的元数据引擎采用 Redis,Redis 节点的系统盘做了 RAID1,同时 Redis 持久化的数据会定期同步到另一台备份节点上。Redis 的数据持久化我们采用 AOF + RDB 的方案,每秒进行一次数据持久化,相关配置如下:

appendonly yes
appendfsync everysec
aof-load-truncated yes

由于我们节点采用的是 100G InifiBand,IB 的 网卡驱动 需要用户根据自己的操作系统版本下载对应的 ISO。目前我们的节点是采用 Kernel 5.4 的版本,由于 IB 驱动跟操作系统还有 Kernel 版本有较强的耦合性,当我们 Kernel 升级到 5.4 版本,驱动需要重新编译安装,驱动版本 MLNX_OFED_LINUX-5.5-1.0.3.2-rhel7.6-x86_64.iso 注意 GCC 的版本一定要是 GCC 9 的才行,否则编译过程会出现各种莫名其妙的问题。

# 安装 gcc9
yum --enablerepo=extras install centos-release-scl-rh
yum install devtoolset-9-gcc scl enable devtoolset-9 bash
# 进行 IB 驱动编译
mount /dev/sr0 ib
./mlnx_add_kernel_support.sh -m /root/ib -k (kernel 版本)

对象存储

对象存储采用自建的 Ceph 集群,Ceph 集群采用 Cephadm 进行部署,目前生产环境用的是 Octopus 版本。Cephadm 是随着 Ceph 新版本 v15.2.0(Octopus)发布的安装工具,并且不支持 Ceph 的旧版本,Cephadm 不依赖于外部配置工具,如 Ansible、 Rook 和 Salt,它通过 SSH 将管理器守护进程连接到主机来实现这一点。管理器守护进程可以添加、删除和更新 Ceph 容器。

通过 Cephadm 引导一个单节点的集群,Cephadm 会在执行 bootstrap 引导的节点部署 mgr 跟 mon 服务,当添加其他节点的时候,会自动在其中一台部署 mgr 管理节点,目前我们生产采用 2 个管理节点 3 个监控节点。

在 Ceph 调优方面我们借鉴了社区其他用户分享的方案,感谢携程的工程师在我们调优过程中提供的帮助 (点击此处查看),主要做了以下实践:

服务器层面(参考):

  • 42Cores 256GB 24*18T HDD
  • 系统盘: 2* 960G SAS SSD
  • BlueStore
  • 关闭 NUMA
  • 升级 kernel: 5.4.146 开启 io_uring
  • Kernel pid max,修改 /proc/sys/kernel/pid_max

Ceph 配置方面:

  • Ceph RADOS:直接调用 librados 接口,不走 S3 协议
  • Bucket shard
  • 关闭 pg 的自动调整功能
  • OSD 日志存储(采用 bluestore,建议裸容量配比—— block : block.db : block.wal = 100:1:1,后两者建议采用 SSD 或 NVMe SSD)
  • 3 副本

JuiceFS 客户端

我们环境中 JuiceFS 对接的对象存储是 Ceph RADOS,JuiceFS 采用 librados 与 Ceph 进行交互,因此需要重新编译 JuiceFS 客户端,建议 librados 的版本要跟 Ceph 的对应,例如在我们的环境 Ceph 版本是 Octopus(v15.2.),librados 的版本建议为 v15.2.,CentOS 自带的 librados 版本比较低,因此我们可以在官网自己下载对应的包,我们的环境上只需要下载 librados2-15.2.10-0.el7.x86_64.rpm 和 librados-devel-15.2.10-0.el7.x86_64.rpm。然后运行如下命令安装:

yum localinstall -y librad*

安装 librados 后即可编译 JuiceFS 客户端了(推荐 Go 1.17+ 、GCC 5.4+):

make juicefs.ceph

编译完 JuiceFS 即可创建文件系统并在计算节点进行 JuiceFS 客户端的挂载了。目前 JuiceFS 在我们的生产环境使用还是有一大部分是直接通过 Kubernetes 的 HostPath 进行挂载,因此我们在各个 GPU、CPU 节点中都挂载了 JuiceFS 客户端,并通过 systemctl 管理 JuiceFS 的挂载进程,实现开机自动挂载与故障的恢复。

07 未来展望与规划

最后归纳下 Lustre 与 JuiceFS 的特点与适用场景,企业可以根据自身的业务场景、运维能力以及存储规模做出相应的选择。

  • Lustre 作为老牌 HPC 领域的存储系统,为许多全球最大的超算系统提供动力,具有多年的生产环境经验。其具有符合 POSIX 标准、支持各种高性能低时延的网络,允许 RDMA 访问的优点,适用于传统 HPC 领域的高性能计算,但是在云原生场景的适配上还不够完善,目前只能采用 HostPath Volume 对接,而且其软件运行在 Linux 内核之上,对运维人员要求更高;
  • JuiceFS 是一款云原生领域的分布式存储系统产品,提供了 CSI Driver 以及 Fluid 等方式使用能够更好地与 Kubernetes 进行结合。在运维部署方面为用户提供了更多灵活的选择,用户既可以选择在云上也可以选择私有化部署,在存储扩容运维方面较为简单。完全兼容 POSIX 标准使得深度学习的应用可以无缝迁移,但是由于后端对象存储的特点其在随机写方面会有较高的延迟,在只读的场景可以使用客户端的多级缓存进行加速较为符合我们的业务特点。

Atlas 平台未来与 JuiceFS 相关的规划是:

  • 元数据引擎升级:TiKV 适合在 1 亿以上文件数量(最多可以支撑到百亿级文件),对性能以及数据安全都有较高要求的场景,目前我们已经完成了 TiKV 的内部测试也在积极跟进社区的进展,后续要将元数据引擎迁移到 TiKV。
  • 基于目录(项目)的文件配额:开源版本目前还不支持基于目录的配额,目前我们每个部门是归属在 JuiceFS 的不同的目录下,需要对目录的配额做限制。JuiceFS 社区版已经在规划实现这个特性,会在 v1.0.0 之后的版本正式发布。

感谢 JuiceFS 开源社区在云知声 Atlas 计算平台高效存储建设的过程中提供的技术支持,云知声也在积极地进行内部测试,争取后续将开发的功能以及改进回馈到开源社区。

相关博客

Conda + JuiceFS :增强 AI 开发环境共享能力

2024-12-04
本文简要分享了在多机环境下,如何利用 JuiceFS 复用 Conda 虚拟环境的具体方法与注意事项。希望这篇文章中的技巧和经验能够为读者的日常开发和团队协作提供帮助,并提升工作效率。我们鼓励感兴趣…

海柔仿真系统存储实践:混合云架构下实现高可用与极简运维

2024-11-08
海柔科技仿真平台的数据特征包括:大量小文件、并发写入、跨云架构等。经过对比 Longhorn、Ceph 等多种系统后选择了 JuiceFS

好未来:多云环境下基于 JuiceFS 建设低运维模型仓库

2024-11-06
好未来基于 JuiceFS 开发了一个模型仓库,支持用户训练过程存储 checkpoint,并且控制面支持用户从各个云环境上传并统一管理模型。通过 JuiceFS CSI 组件,好未来将模型仓库挂载…

vivo 轩辕文件系统:AI 计算平台存储性能优化实践

2024-10-25
在早期阶段,vivo AI 计算平台使用 GlusterFS 作为底层存储基座。随着数据规模的扩大和多种业务场景的接入,开始出现性能、维护等问题。为此,vivo 转而采用了自研的轩辕文件系统,该系统…