作者 | Lucien
阿里云高级技术专家车漾老师在 QCon 上海会议上,分享了在 Fluid 项目作为云原生 AI 场景下的数据和任务编排框架,在 AIGC 模型推理工程化落地方面做了许多优化探索的工作,包括简化云原生 AI 场景的分布式缓存管理和运维,降低资源成本;以及优化推理服务读取模型数据的效率,加速模型加载过程。同时也会演示了如何通过 Fluid 将一个 LLM 模型的推理加载速度提升近 7 倍,同时提供缓存弹性的能力,避免资源浪费的实践话题。
首先,大模型推理将在 AI 商业化时代中找到更广泛的应用,这比模型训练更具有竞争力。这是因为技术商业化是 AI 发展的必经道路,而模型的价值主要体现在其能否被广泛应用。我们直观理解,一个大模型只有两种可能的结局:要么它无用,之后就不会有后续;要么我们发现它非常有用,因此将被全球范围的用户所使用。如 OpenAI 和 Midjourney 等公司,用户都是在为每一次推理行为付费。随着时间的推移,我们可以预见,模型训练和模型推理的使用比重可能会达到 3:7,甚至 2:8。可以说,模型推理将是未来的主要战场。
然而,大模型推理带来的挑战主要表现在成本、性能和效率上,其中成本最为关键。随着模型规模的不断增大,所需要的运行资源也日益增多。而模型运行所依赖的 GPU 由于其稀缺,价格高昂,使得每一次模型推理的成本也随之上升。大模型推理的过程就像使用兰博基尼送外卖,速度越快,成本就越高。不过,用户往往只为价值买单,而不愿意为高昂的推理成本买单。因此,降低单位推理的成本成为了基础设施团队的重要任务。
此外,性能是我们的核心竞争力,特别是在面向消费者(ToC)领域的大模型中,更快的推理速度和更好的推理效果都是吸引和保持用户的关键。
随着云原生技术和架构发展,我们明显观察到 IT 架构的变化。传统的企业级应用、Web 应用、微服务等领域都在从传统架构转向云原生架构。互联网应用大多是基于容器、Kubernetes、Prometheus 等云原生技术实现的,追求弹性、灵活性以及最佳性价比。同时,通过标准化交付,提升生产流程的高效闭环。在标准 API 和标准架构的指导下,进一步提高多角色之间的协作和迭代效率。同样地,为了获得更多弹性算力供给、更高稳定性保证以及更快的交付,越来越多 AI 和大数据工作负载也运行在云原生架构上。
右图清晰地显示了这一趋势:最早是从可水平扩展的无状态应用程序(如 Web 应用、移动后端应用)开始;然后是 NoSQL 数据库、关系数据库、TensorFlow、PyTorch、Spark、Flink 等大数据和典型机器学习的 AI 计算任务。都能够在 Kubernetes 容器集群上大规模运行。
三方研究机构的预测,显示出同样的趋势。早在 2019 年,Gartner 就曾预测,到 2023 年,70% 的人工智能应用程序将基于云原生等技术进行开发。IDC 的研究则预测,到 2025 年,接近 50% 的企业内部的数据密集型或性能密集型计算工作负载都将迁移到基于云的架构上,而基于云的架构正是典型的云原生架构。
在 AIGC 推理场景下有个关键的矛盾,就是计算存储分离的架构导致的数据访问高延迟、带宽受限问题和大模型规模不断增长的矛盾,它会同时影响成本、性能和效率。
模型弹性伸缩、按需使用,是控制大模型成本的利器。然而,如上图右所示,以 Bloom-175B 模型(FP16 精度模型大小约 340GiB)为例,模型的扩容耗时为 82 分钟,接近 1 个半小时,为了找到问题的根因,需要对模型启动时间进行拆解,其中主要耗时在于 HPA 弹性、创建计算资源、拉取容器镜像,加载模型。可以看到从对象存储加载一个约 340G 的大模型,耗时大约在 71 分钟,占用整体时间的 85%,这个过程中我们其实可以看到 I/O 吞吐仅有几百 MB 每秒。
要知道在 AWS 上 A100 按量付费的价格每小时 40 美元,而模型启动时刻 GPU 其实是处于空转的时刻,这本身就是成本的浪费。同时这也影响了模型的启动性能和更新频率。
那么,我们有办法解决这个问题吗?一个直观的想法是增加一个缓存层,但是真的增加了缓存层就可以了吗?实践中其实并不是这样的,我们会遇到一系列的问题。
首先就是快的问题:能否用好缓存,如果加了缓存但是速度依旧不快,那么是缓存的规划问题?硬件配置问题?还是软件配置?网络问题?调度问题?
其次就是省:关注成本问题,作为缓存的机器通常是高带宽、大内存、本地盘的机器,这些配置的机器往往并不便宜。如何能够实现性能最大化的同时也有合理成本控制。
接着就是好:用户使用复杂不?用户代码是否需要相应的修改。运维团队工作量大吗?模型会不断更新和同步,如何降低这个缓存集群的运维成本。简化运维团队的负担。
正是在这种对于缓存工程化落地的思考中,诞生了 Fluid 这个项目。
首先,我们来探索 Fluid 的核心概念。Fluid 在 Kubernetes 中负责编排数据及其计算任务,这不仅包括空间编排,还囊括时间编排。空间编排意味着计算任务将优先调度到已缓存数据的节点或近似节点上,以此提升数据密集型应用的性能。时间编排则允许我们同时提交数据操作和任务,但是在任务执行之前,我们需要执行一些数据迁移和预热操作,以确保任务在无人值守的情况下也能顺利进行,从而提升工程效率。
观察 Fluid 的架构图,我们可以看到 Fluid 可以对接各种 AI/ 大数据应用,并且可以与各种异构存储系统进行链接。目前,Fluid 已支持包括 Alluxio、JuiceFS 和阿里内部自研的 JindoFS、EFC 等多种缓存系统。
具体而言,Fluid 可以提供五种核心功能:
那么回到 AIGC 模型推理场景,Fluid 为这个场景带来了许多优化方案。
首先,一种常见的挑战是,分布式缓存的使用复杂度高且运行环境差异大。对于 AI 和大数据应用来说,我们可能需要适配多种运行时,例如已经被广泛使用的 Alluxio、Jindo、JuiceFS,以及最新的 Dragonfly。同时,这些应用可能会在各种运行时环境中执行,包括公共云、私有云、边缘云和 Serverless 云。在这样的背景下,Fluid 提供了一种一键部署的解决方案,使各种环境能够无缝衔接。
第二,AI 和大数据模型推理服务在业务属性上具有很高的灵活性,而 Fluid 则通过提供弹性缓存能力,帮助用户在性能和成本之间实现最大化的权衡。具体来说,当您需要更多的缓存空间时,Fluid 能够自动扩展;而在不需要时,它也能自动缩减。
第三,Fluid 以数据为中心,实现了数据感知调度。这意味着,计算任务会尽可能地调度到离数据最近的地方,提升运算速度,从而极大地提高了应用的性能。
第四,Fluid 提供了数据流编排能力,能够自动化处理复杂的数据消费和模型推理行为,从而降低用户在操作上的难度。
最后,在性能优化方面,Fluid 拥有一种适用于云原生缓存的读取优化方案,可以充分利用节点资源,使处理速度得到进一步提升。
总的来说,Fluid 是一种旨在提高运算性能、简化操作流程,并能适应多种运行环境的工具,无论您的 AI 或大数据应用在何种环境中运行,都能从 Fluid 中获益。
Fluid 是一种分布式数据和计算架构,旨在提供易于使用的、一致的和高效的数据访问。它由两个主要部分构成:Dataset 和 Runtime,它们分别代表需要访问的数据源和对应的缓存系统。下面我们详细解析一下。
首先看到的是 Dataset,它代表需要访问的数据源。在这个例子中,数据源被描述为 OSS 存储桶中的一个子目录,用户通过创建 Dataset 对象来声明他们的数据源。
接下来是 Runtime,代表对应的缓存系统。在这个例子中,缓存系统是 Alluxio,因此,对应的是 AlluxioRuntime 的 CRD。CRD(Custom Resource Definition)是 Kubernetes 提供的一种扩展机制,用于自定义资源。
当用户创建了 Dataset 和对应的 Runtime 后,Fluid 会接管后续的所有工作。首先,Fluid 会自动完成对应的缓存系统的配置,然后,它会自动拉起缓存系统的组件。接着,Fluid 会自动创建一个 PVC(Persistent Volume Claim)。PVC 是 Kubernetes 中的一种资源,它提供了对存储系统的抽象,使得用户无需知道具体的存储系统类型和参数,就可以使用存储。
当这些准备工作完成后,希望访问模型数据的推理应用只需要挂载这个 PVC,就可以从缓存中读取模型数据。与此同时,因为 Fluid 使用的是 Kubernetes 标准的存储方式,所以用户可以以熟悉的方式来访问和操作数据。
可以看到,Fluid 通过提供简洁明了的接口,自动化地完成了数据源的声明、缓存系统的配置和管理,极大地降低了数据访问的复杂度,让用户可以专注于他们的业务逻辑。
AIGC 推理的运行平台非常多样化,包括云服务的 Kubernetes、自建的 Kubernetes、边缘的 Kubernetes 以及 Serverless 形态的 Kubernetes。Serverless 形态的 Kubernetes 由于其易用性、低负担的好处,已经越来越多的成为用户的选择;但是 Serverless 由于安全的考量,没有开放第三方存储接口,所以只支持自身存储,以阿里云为例子,只有 NAS,OSS,CPFS 有限存储。
在 Serverless 容器平台上,Fluid 会将 PVC 自动转换成可以适配底层平台的 sidecar,开放了第三方的配置接口,可以允许并且控制这个 sidecar 容器的生命周期,保证它在应用容器启动前运行,当应用容器结束后自动退出。这样 Fluid 则提供了丰富的可扩展性,可以运行多种分布式缓存引擎。
Fluid 提供的可弹性伸缩的计算侧分布式缓存,对于大规模并发,需要快速高效地读取数据的 AI 应用来说,尤其重要。
只依赖简单的分布式缓存,问题在于当很多服务实例需要拉取数据时,每个实例能获得的带宽将会受到限制。例如,100 个推理服务实例同时启动,并需要从对象存储中拉取数据,那么,由于总的可用带宽是固定的,每个实例能份到的带宽就只有总带宽的百分之一。这可能导致数据拉取的延时显著增加,从而影响性能。
通过弹性伸缩的计算侧分布式缓存,我们将底层存储系统的有限带宽,扩展到了可以弹性扩容的 Kubernetes 集群内。这个集群内的可用带宽取决于分布式缓存的节点数量,从而可以根据业务需求,及时进行伸缩,提供灵活而高效的 I/O 处理。
测试数据也证明了弹性伸缩的计算侧分布式缓存的优势。在 100 个 Pod 并发启动的情况下,使用缓存可以显著加速,如果使用更多的缓存 worker 节点,效果会更好。因为大规模的缓存节点能提供更大的聚合带宽,因此每个 Pod 份到的带宽就越多。同样,当你使用更多的分布式缓存节点时,聚合带宽也会近线性地提升。
归结起来,弹性伸缩的计算侧分布式缓存,不仅能够灵活地应对业务的变化需求,还能根据需求提供充足的带宽,保证了 AI 模型推理服务的性能,这是简单部署分布式缓存所不能实现的。
介绍完如何简单地部署缓存后,接下来考虑的问题就是如何在尽可能节省成本的前提下最大化缓存带来的性能提升,如何在成本和性能间取得平衡实质上是与业务场景的 I/O 访问模式相关的。Fluid 在缓存上暴露的可观测性,配合手动扩缩容、HPA、CronHPA 等 Kubernetes 的扩缩容能力,可以根据业务需求弹性扩容、缩容数据缓存。我们可以举几个具体的例子:
公共云的一个主要优势是其灵活的弹性能力和高可用性,这些都是通过底层的多可用区(Multiple Availability Zones)来实现的。多可用区设计可以为互联网应用带来极高的稳定性,即使牺牲了一些性能。
然而,在 AIGC 大模型场景中,我们发现跨可用区的延时可能会有较大影响,这是因为大模型文件往往体积较大,它传输的数据包就会非常多,这就放大了延时的影响。
因此,缓存和使用缓存的应用之间的亲和性就显得非常重要。亲和性在这里指的是 Kubernetes 的一种调度策略,通过设置亲和性,可以告诉 Kubernetes 调度器,在调度 Pod 时,要尽量将拥有相同亲和性的 Pod 调度到同一台节点上或者尽量不调度到同一台节点上。
Fluid 解决这个问题的方式是提供无侵入性的亲和性调度。无侵入性的亲和性调度是根据缓存的地理位置来调度应用,优先在同一可用区内进行调度。这样就可以避免跨可用区的延时。
与此同时,Fluid 也提供了弱亲和性和强亲和性的可配置性。弱亲和性是指如果条件允许,Kubernetes 会尽量按照亲和性策略进行调度,但如果无法满足,则可以打破这个策略;而强亲和性则是指必须严格按照策略进行调度,不能打破。这两种配置提供了使用缓存的灵活性,可以根据不同的业务需求进行选择。
在传统的 AI 模型推理业务上线过程中,确实存在许多的复杂操作和耗时步骤。例如部署和扩容分布式缓存,预热模型数据以避免 Cache Miss,启动多个服务实例,以及在确认服务无误后缩容缓存以减少成本。这些步骤需要人工介入,并在每一步之间花费大量时间来确认状态,并进行下一步操作。
为解决这一问题,Fluid 将数据消费流程标准化,并通过数据操作抽象以及数据流编排能力,使得这些流程得以自动化进行。换言之,Fluid 将和数据缓存相关的步骤(如数据迁移,预热,以及和业务相关的数据处理等)描述为 Kubernetes 级别的抽象,用户可通过这些抽象去直接描述和控制数据处理的全过程。这在大大简化操作复杂度的同时,也有效缩短了整个业务上线的时间。
总的来说,Fluid 利用 Kubernetes 的能力,将 AI 模型上线的业务流程标准化,并通过提供数据操作的抽象及数据流的编排能力,使得整个流程更为高效,并减少了人工介入的程度,大大提高了业务上线的效率和误操作犯错的可能性。
这些数据操作可以串联成一条数据流。于是刚才我们提到的这个例子,就可以用 5 步数据操作来轻松定义。运维人员只需要一次性提交这条数据流,Fluid 会自动地完成整个 AI 模型推理服务发布的流程,提升使用缓存过程的自动化比例。
那么刚才提到的“用好缓存”的技巧其实都在资源成本和运维效率方面。但实际测试过程中我们发现,服务启动过程使用的带宽远小于这些 GPU 计算实例可用的带宽,这意味着模型的加载效率在客户端上仍然有可以优化的空间。
从节点吞吐情况上可以看到,这些 AI 推理的运行时框架会以单线程的方式去按序读取模型参数,这在非容器环境是没有什么问题的,如果使用本地 SSD 盘存储模型参数,加载吞吐很容易就可以到达 3~4GB/s。但是在计算存储分离架构下,哪怕我们使用了缓存,缓存也需要使用用户态文件系统(也就是 FUSE)这种技术挂载到容器中。FUSE 自身的开销和额外的 RPC 调用,都使得 read 请求的延时变得更高,单线程所能达到的带宽上限也就更低了。
为了最大化发挥分布式缓存提供的巨大 I/O 吞吐,Fluid 可以提供一个 Python 的 SDK,在用户代码中使用多线程读和预读的方式去加速模型加载过程。从我们的测试结果来看,额外使用这种客户端的优化,可以在使用计算侧分布式缓存的基础上,将冷启动耗时缩短一半,做到 1 分钟内拉起一个接近 100G 的大模型。从右下角的这个 I/O 吞吐情况,也可以看出,我们更充分地利用了 GPU 计算节点的带宽资源。
我们首先看一下直接访问 OSS 存储的运行效果。这里我们已经创建好了 OSS 的 PV 和 PVC。
接着,我们定义一个 deployment:deployment Pod 中挂载刚才的 OSS PVC,使用的容器镜像是 TGI 镜像。还有声明使用 1 张 GPU 卡,用于模型推理。接着把 deployment 创建下去。然后我们看下这个服务的就绪时间,这边 5 倍速加速了一下。终于就绪了,可以看到整个过程耗费了 101s,考虑到我们的模型大小仅为 12.55G,这个时间可以说是比较长的。
最后,让我们看看 Fluid 的优化效果。我们需要定义 Fluid 的 Dataset 和 Runtime 资源,并将分布式缓存部署到集群中。定义数据源、节点个数以及缓存数据的存储介质和大小。由于我们是初次部署弹性分布式缓存,这可能需要约 40 秒的时间。缓存准备完成后,我们可以看到一些缓存的监控信息。PVC 和 PV 也会自动创建。然后,我们定义一个新的 deployment,只需要进行几个修改:
观察服务的就绪时间,我们可以看到部署只花了 22 秒。我们还可以尝试对现有的 deployment 进行扩容,观察第二个服务实例的启动时间。由于所需的模型数据已被完全缓存,第二个服务实例只需 10 秒就能准备就绪。这个例子展示了 Fluid 优化的效果,我们成功提升了服务的启动速度约 10 倍。
总结:
综上,可以看到 Fluid 为 AIGC 模型弹性加速提供开箱即用、优化内置的方案,在达到更好性能的同时还可以降低成本,同时还包含端到端的自动化能力;在此基础上使用 Fluid SDK 可以进一步充分发挥 GPU 实例的带宽能力实现极致的加速效果。
活动推荐
目前,极客邦科技正在策划 2024 年 6 月 14-15 日 ArchSummit 架构师峰会深圳站会议,本次会议将围绕“智能进阶. 架构重塑”大主题方向,探讨在 AI 浪潮下,架构设计如何匹配智能化趋势,以适应日益复杂的业务需求。会议上将讨论大模型、AI 运维、数据架构、应用架构、业务架构、微服务、高可用以及数字化转型等多个层面的话题,感兴趣的可以点击阅读原文查看 ArchSummit 会议官网。