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

在AI场景中,文件存储中面临一些新的变化:非结构化数据、大量的小文件、训练数据读多写少等。这些变化,给存储带来新的挑战。今天我们邀请到“云知声”,一家从事语音及语言处理的AI企业,为大家分享他们的存储优化实践

背景

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

640

云知声 Atlas 平台架构图


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

存储建设历程

一个性能卓越的超算平台,不仅需要充足的算力支持,也离不开高效的存储系统。结合 Atlas 上的任务特点和类型,高效存储系统应具备几个特点,如:满足多种类型的结构化与非结构化数据存储需求、兼容 POSIX 接口、海量小文件场景下具有较好的性能等。
在最早期进行 Atlas 超算平台建设的时候,我们尝试部署过 CephFS,开源版的 CephFS 在存储规模达到几千万小文件的时候,开始出现较为严重的性能问题,用户在操作文件时会遇到卡顿甚至在高 IO 的场景下整套存储系统会直接卡死,用户体验不太好。
后期,我们转到了 HPC 领域使用较为广泛的 Lustre 分布式文件存储系统, 构建了多套不同规模的 Lustre 集群,作为平台核心的存储系统,生产环境上目前主要有 40G 以太网与 100G InfiniBand 类型的集群,Lustre 分布式存储支持着用户在 Atlas 超算集群中进行数据处理、模型训练、源码编译与调试、数据归档等一系列数据操作。但是受限于 Lustre 在高并发请求下的性能瓶颈,无法满足对带宽与 IOPS 要求较高的场景需求。因此我们采用 Alluxio + Fluid 进行 IO 加速,分布式缓存给我们带来了 AI 模型训练速度上的提升以及存储系统总带宽的下降。
但是以上方案依然不是我们认为的最终方案,因此我们也在探索新的存储系统。在我们的场景上对这个新存储系统的核心需求是:

初识 JuiceFS

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

JuiceFS 的架构与优势

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

640 (1)

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 进行了三组实验,分别是:

640 (2)

耗时对比


JuiceFS 客户端拥有多级缓存功能,因而在性能测试中,在数据读方面有较大性能提升,相比 Lustre 性能有1倍以上的提升,这与我们的业务特点比较契合。
综合考虑运维方式、业务契合度以及性能表现,我们决定将 JuiceFS 带上生产。

JuiceFS 在 Atlas 的使用场景与收益

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

收益一:加速 AI模型训练

1)语音降噪测试

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

640 (3)

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


从测试结果来看,单从数据读取效率上,在 wav 小文件方面,JuiceFS 为 6.45 it/s,而 Lustre 为 5.15 it/s,性能提升25%。JuiceFS 有效加速了我们端到端的模型训练,整体缩短了模型的产出时间。
2)文字识别场景
在文字识别场景中,模型为 CRNN backbone 为 MobileNet v2 ,测试环境如下:

WX20220805-150550@2x

640 (4)

Lustre vs. JuiceFS 每个 batch 耗时对比


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

收益二:加速 AI模型开发

640 (5)

模型开发示意图


在算法工程师将 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 倍的提速 ,极大地提高了用户的调试效率,用户的体验也更加好。

JuiceFS 在 Atlas 的使用方式

在数据的存放管理方式上,我们采用兼容现有分布式存储系统的管理方式,JuiceFS 集群的节点也都是对接 LDAP,每个节点会通过 LDAP 的客户端与 LDAP Server 集群进行交互认证。
超算平台上的每个组归属于不同的目录,每个目录下是各自组内或者部门内的成员,不同组之间的目录是不可见的。目录的权限是基于 Linux 的权限管控机制。用户在 Atlas 集群提交训练任务的时候,集群的任务提交工具会自动读取系统上用户的 UID 与 GID 信息然后将其注入用户提交的任务 Pod 的 securityContext 字段,则 Atlas 集群上运行的容器 Pod 内所有容器的进程运行的 UID 与存储系统上的信息一致,保证权限不越界。

640 (6)

存储权限认证架构图


在数据的访问方式上,云知声目前有 2 种使用方式:

1)HostPath Volume

640 (7)

HostPath Volume 数据读取示意图


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

2) Fluid + JuiceFS

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

640 (8)

基于 Fluid 数据读取示意图

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

JuiceFS 存储生产环境建设

元数据引擎

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

appendonly yes
appendfsync everysec
aof-load-truncated yes

</code></pre>

由于我们节点采用的是 100G InifiBand,IB 的 网卡驱动[3] 需要用户根据自己的操作系统版本下载对应的 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 的才行,否则编译过程会出现各种莫名其妙的问题。
<pre><code>

# 安装 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 版本)

</code></pre>

对象存储

对象存储采用自建的 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 调优方面我们借鉴了社区其他用户分享的方案,感谢携程的工程师在我们调优过程中提供的帮助 (点击此处查看),主要做了以下实践:
服务器层面[4]

Ceph 配置方面:

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。然后运行如下命令安装:

<code><pre>

yum localinstall -y librad*

</code></pre>

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

<code><pre>

make juicefs.ceph

</code></pre>

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

未来展望与规划

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

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

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

想进一步了解,我们可以安排 1:1 会议为你介绍和演示