从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

当云端仿真任务从万级扩展到百万级,最先触到天花板的往往不是算力,而是数据。带宽买不够、存储买了用不完、自研缓存越来越重——这是大规模仿真平台在数据访问层几乎必然会经历的困境。

本文分享九识智能在这一演进过程中的实际路径:如何用 Alluxio 替代 PFS 和自研缓存,将 IO 带宽从 30GB/s 突破到 100GB/s 以上,同时降低架构复杂度。

关于作者

史正–九识智能-智能平台部仿真平台负责人。多年自动驾驶仿真平台与工具链经验,在大规模 DAG 调度上深耕多年, 支撑百万/每天的云端仿真的稳定运行。

关于九识智能

全球领先的自动驾驶科技公司,凭借成熟的 L4 级全栈自研技术,为万亿级城配市场提供创新解决方案。九识 Zelos 是 RoboVan 第一品牌,运营着全球规模最大的 RoboVan 车队。作为 RoboVan 领域的开创者,九识推出了全球第一台城市机动车道路运营的无人驾驶货车。

业务背景:分布式仿真 pipeline 的数据访问架构

数据驱动仿真就是用车端采回来的真实传感器数据做算法验证。简单来说,车端在路上跑的时候,会持续不断的采集车端传感器的数据,比如相机、激光雷达、毫米波雷达会把这些数据采集成记录文件,上传到对象存储,然后在仿真阶段从对象存储下载记录文件、输入算法回放计算,再将计算后的结果文件写回对象存储。单个文件大小在几百 MB 到几  GB 之间。单任务数据流程非常简单,只有下载、计算、上传三个环节

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

这三个环节在云端调度的时候,会形成三个步骤,在 k8s 的三个 Pod 里进行计算。这种模式在本地仿真是没问题的,但在云端进行仿真,其核心特点是大规模并行,就不太适用了。比如日常并行跑万级乃至百万级仿真任务,每个任务都要从对象存储下载几个 GB 的数据。而且很多任务跑的是同一批数据,不同算法版本反复测试同一份记录文件,带来大量重复下载,不仅造成带宽严重浪费,还很容易被打满从而成为瓶颈。

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

遇到的挑战:自研 Cache + PFS 组合的带宽瓶颈与成本困境

当带宽被打满,计算就需要排队等待数据下载,整体吞吐会无法满足计算需求。为了缓解对象存储的压力,在此阶段,我们自研了缓存服务 ZCache。

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

简单来说当任务运行的时候,会先去检查 ZCache 里面有没有缓存过这个热数据,如果有的话,会从共享存储的 PFS  直接读取;如果数据没有被缓存过,会从对象存储下载数据到自研缓存和 PFS,之后的任务就不需要进行重复的下载,相当于缓存命中。我们自研的缓存服务除了管理查询,还负责数据的淘汰,采用 LRU 算法管理整个缓存盘的空间。在业务规模相对可控的阶段,ZCache 有效缓解了对象存储的压力。

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

但随着并行任务量持续增长,新的瓶颈出现了——这次不在对象存储,而在 PFS 端。PFS 提供数据的读和写,其本身都是在云产品远端进行访问,所有的客户端 Pod 都会通过访问另一个集群的网关去访问 PFS。这个集群的整体带宽受限于通往网关的网络链路上最窄一环的带宽。当并行的 Pod 数越多,带宽就会越不够用。由于云厂商对 PFS 做了带宽限制,当带宽达到上限,我们整体任务处理的读和写就会很慢。这会直接影响到 GPU 节点,在读写原生接口的时候就会卡住,造成整体任务的延迟上升, GPU 利用率下降,吞吐也会不够。

为了解决这个问题,我们只能选择去扩充带宽。但是云厂商提供的 PFS 带宽和存储空间是绑定购买的:当我们去买带宽,存储空间也必须同时购买,即使我们每天用的热数据量并不大,但为了扩充带宽则需要增购存储空间,这样就会造成存储空间的浪费。其次,PFS 的带宽也不是无限可扩的。我们最初用的是普通版的 PFS,带宽最多扩到 30GB/s ,完全不能满足实际业务需求。对于这种情况,我们只能再去购买高性能版的 PFS,而该版本的价格则是普通版的好几倍,对我们来说很不合算。

此外,我们的 pipeline 数据处理模式是单读单写的。虽然  PFS 有一些高级特性,比如数百并发同时读写同一个文件,这种情况我们是没有的,对于 PFS 的强一致性,高级别的快照特性其实也用不上。我们都是上一环节把数据产出到共享存储,下一个环节再去处理,都是单写单读的过程。所以,本质上我们是在花钱买用不完的存储空间,仅仅为了换取所需带宽。

为什么选择 Alluxio:技术选型的核心考量

为了解决这个痛点,在最终选择 Alluxio 之前,我们也调研了其他的一些云产品和云厂商的方案。

首先第一个调研的是 JuiceFS

它在我们的场景中不太适用,原因一是我们对象存储的  bucket 特别多, JuiceFS 没有办法把所有的对象存储都挂载上,而且我们还需要维护多个 JuiceFS 这样的集群,对于我们的可维护性不太好。原因二是我们对象存储里已经积累了大量的数据,而 JuiceFS 要求数据重新按照它的协议去填充一遍,它自己去管理,这样我们前期需要做的工作就比较多,所以选择放弃。

第二个调研的产品是对象存储加速器

我们当时用的是火山的加速器,用对象存储加速器作为缓存,用 NAS 作为写数据交换的媒介。但是对象存储加速器我们有两点担心:第一点是跨云之后不好迁移,比如我们今天在火山云,如果后面需要扩展到其他云就不太合适了。第二点是该产品也是有带宽上限的,默认的规格也不太合适,且架构和实现机制不太透明,所以我们也没有选择。

Alluxio 如何解决两个问题:读缓存层替代自研 Cache + PFS,Cache-Only 写缓存替代 PFS 中间数据交换

基于以上调研,我们在了解了 Alluxio 之后,发现 Alluxio 在我们的大规模仿真场景是非常适合的,主要有三个具体方面:

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

方面一: Alluxio 的性能

Alluxio 是去中心化架构,元数据和数据都分布在 worker 上,而 worker 会部署在现有计算集群的 k8s 节点。Alluxio 在数据缓存的时候会把大文件切成多份,分散在不同的本地存储盘上。比如 1 GB 的数据会拆成 10 份,分散在 10 个节点上。访问数据的时候会根据访问的偏移自动路由到对应的节点。当有大规模运行的 Pod 去访问 Alluxio 时,其实是在大量交叉访问所有节点,而不是访问一个统一的网关,这和 PFS 是不一样的。所以说,Alluxio 可以极大的利用我们整体集群之间所有节点的性能。Alluxio 在使用方面也是非常方便。我们之前自研缓存服务是需要自己去做 SDK,在代码层面做适配,但有了 Alluxio ,我们只需要通过 Alluxio Fuse (POSIX 协议接口) 这种形式做挂载,即可像访问本地文件一样去访问磁盘上的数据,这样可以大量减少代码中的逻辑和很多潜在的 bug,代码也随之更加清爽,我们也不需要投入精力去管理数据的态化和缓存逻辑等。

方面二:Alluxio 的读写特性

我们的数据读写特性是上游写数据,下游读数据,没有大规模、高并发读写同一个文件的需求。这个方面,Alluxio 提供了另一个特性叫写缓存(Cache-Only)。它跟读缓存不同,底层可以不挂载对象存储,所有数据都存储在临时的集群盘里。当我们写数据的时候,Alluxio 也会根据数据大小将之拆成多块数据,分布放在 k8s 的多个节点上,我们的数据直接写到 Alluxio 即可。值得一提的是,Alluxio 写缓存对我们而言还是一个相对新的能力,目前主要用于替代 PFS 承担中间数据的交换通道,生产验证仍在推进中。但从架构设计上看,这个能力的潜力远不止于此。在传统的仿真 pipeline 中,中间数据的写入和读取是串行的——上游写完,下游才能读。而 Alluxio 的写缓存本质上是一个分布式的、基于本地 NVMe 的高速暂存层,数据以分片形式分散在多个节点,读写路径完全本地化。这意味着,当 pipeline 的并行度继续提升,中间数据的交换带宽理论上可以和计算节点数同步线性扩展,而不会像 PFS 那样受制于统一网关的带宽上限。我们对这个方向是有期待的。如果你的仿真或训练 pipeline 同样面临中间数据交换的带宽瓶颈,欢迎和我们交流,或者直接联系 Alluxio 团队——这块我们也在一起探索。

方面三:高可用

为了保证高可用,我们开了两个副本,以应对 k8s 节点偶尔波动带来下线的影响。由于我们的数据特性是要处理的快,所以可以进行重算,数据即使丢失了,也是可以接受,但这样的故障情况在我们采用 Alluxio 之后还没有出现过。

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用以上我们架构演进的三个阶段。第一阶段是从对象存储直接把数据下载下来运行;第二阶段是从对象存储下载之后,加入到我们自研的缓存里;第三阶段是现在我们直接通过  Alluxio 进行数据的读取和管理,数据写入也是通过 Alluxio 写来实现。

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

Alluxio 在我们大规模仿真场景的核心优势是:Alluxio 的 IO  带宽是可以随着业务需要的节点数线性扩展的,因为 Alluxio 的带宽来自每一个 worker 的本地磁盘,加一个节点就多一份带宽。而在 PFS 模式里,由于通往 PFS 网关的带宽是固定的,所以当你节点数越多,每个 Pod 分享到的带宽其实就越少,整体的读写性能就会很慢,延迟也会上去。实际效果是,我们之前 PFS 的集群总带宽大概 30GB/s,切到 Alluxio 之后可以轻松达到 100GB/s 以上,而且还可以继续加节点往上提,迄今为止还没有看到 Alluxio 的带宽上限。

我们在采用 Alluxio 之后的收益是显而易见的:

从 PFS 带宽瓶颈到分布式缓存的演进——Alluxio在九识智能仿真云端调度中的应用

✓IO 带宽方面:我们的带宽受到配额 30GB/s 的硬性限制,没有办法继续提升。在采用 Alluxio 替换 PFS 之后,我们的带宽性能可以随着节点数进行线性扩展,目前达到超过 100 GB/s 的带宽能力。
架构复杂度方面:之前需要对象存储 + ZCache +  PFS  三个系统协同,ZCache 也需要自研进行维护,每次发版都可能存在风险,需要进行诸多测试。在采用 Alluxio 之后,系统之间不再有耦合关系,数据只需要在 Alluxio 安装时配置一次,之后无需再配置,数据由 Alluxio 自动帮我们管理,业务只需要去读取即可,架构也变得非常的清爽。
可扩展性方面:之前扩容会受到 PFS 配额天花板的影响。当达到带宽上限之后只能升级性能版的产品,价格高昂。在采用 Alluxio 之后,我们通过增加节点的方式即可实现扩容。而这两种方案(PFS 普通版 vs  Alluxio)的整体基础设施成本大体相近。相较而言,用同样的钱,Alluxio 可以拿到 3 倍以上的带宽,同时还减少了一套自研系统开发与维护的负担。
总体来说,在我们的大规模仿真场景下,核心诉求是: IO 带宽要随着业务规模线性扩展。Alluxio 通过利用本地磁盘做缓存,实现带宽随节点数线性扩展,同时,Alluxio 的读缓存能力替代了我们自研的 ZCache 服务,写缓存替代了 PFS,将架构从三层系统简化为两层;在同样的成本下,Alluxio 将带宽从 30GB/s 提升到 100GB/s 以上。这些 PFS 做不到,Alluxio 做到了。

 

Alluxio S3 写缓存介绍

借助 Alluxio S3 写缓存,小对象 PUT 延迟降至约 4-6 毫秒,提升幅度达到 5-8 倍;而大对象写入在低延迟且稳定状态下,每个 Alluxio worker 可持续保持 6GB/s 以上速度,随着 Alluxio worker 的增加,性能几乎呈线性扩展。