当前位置:首页|资讯|OpenAI

OpenAI 背书的计算引擎迎里程碑:蚂蚁集团成功部署百万核心计算平台

作者:InfoQ发布时间:2024-01-11

原标题:OpenAI 背书的计算引擎迎里程碑:蚂蚁集团成功部署百万核心计算平台

作者 | 蚂蚁 Ray 团队

Ray是一个通用的分布式计算引擎,最初由 UC Berkeley RISELab 在 2016 年开源。创建该项目的团队是 RISELab 的 Ion Stoica 教授领导的技术团队,该团队曾经在早些年创建过另外一个颠覆性的计算引擎——Spark。相比 Spark,Ray 的接口设计源于更加底层的抽象,这使得 Ray 的应用场景更加通用。Ray 从诞生开始即全面拥抱 Python 生态,并在多年的发展中深耕 AI 分布式计算领域,形成了丰富的 AI 生态。在大模型引领的 AI 浪潮爆发之际,Ray 也在近一年迎来了高速的发展,吸引了业内大量的关注。很多业内人士了解 Ray 是从 OpenAI 对 Ray 的应用开始,但从 9 月底举办的 Ray Summit 可以看出,Ray 已经广泛应用于国内外的互联网巨头、传统软件行业、硬件厂商以及大模型创业公司。

蚂蚁集团从 2017 年开始深度参与 Ray 社区的建设,并在内部探索 Ray 的新计算场景,是最早将 Ray 应用于生产环境的企业。多年来,蚂蚁集团在 Ray 项目上持续投入,并坚定不移地走“开源路线”,与 RISELab 和 Anyscale(Ray 创始团队创办的公司)维持了多年的开源合作。去年,随着业务的高速发展,Ray 在蚂蚁迎来了新的里程碑:生产规模达到 100 万 CPU 核。本文将以此为背景,介绍 Ray 在蚂蚁的发展概况、Ray 规模化背后的技术挑战与实践、以及 Ray 在业界发展的现状和趋势。

蚂蚁集团 Ray 生产规模增长趋势

背景介绍

9 月下旬,Ray 社区一年一度的盛会「Ray Summit」在美国旧金山顺利举办。本次 Ray Summit 全面拥抱 AI 时代重大变革,将会议主题定为「THE LLM AND GENERATIVE AI CONFERENCE FOR DEVELOPERS」,足以说明 Ray 社区在大模型方向的重视与投入。作为 Ray 中文社区的运营团队,蚂蚁 Ray 团队也受邀参与本次会议。

在本次峰会上,我们披露了蚂蚁集团 Ray 生产规模的现状。首先在 Keynotes 上,UC Berkeley 的 Ion Stoica 教授在分享 Ray 相关增长数据时,披露了蚂蚁集团目前具有 50 万 CPU,4K GPU的在线服务规模。

Ion Stoica Keynotes

其次在关于《How Ray Empowered Ant Group to Deliver a Large-Scale Online Serverless Platform》的主题分享中,蚂蚁 Ray 团队开源负责人宋顾杨和李冲揭露了蚂蚁集团整体的 Ray 生产规模已经达到了 100 万 CPU 核

蚂蚁 Ray 团队在 Ray Summit 的分享

100 万核的 Ray 生产环境,是蚂蚁 Ray 团队在多年的研发投入、业务探索和开源共建后,达到的新的里程碑。这一里程碑标志着 通用分布式计算引擎 Ray 已经在蚂蚁内部实现了大规模落地,成为公司内部重要的分布式基础设施,为数字支付、数字金融和数字科技等多个方向的业务持续赋能。

Ray 在蚂蚁的发展概况

Ray 在蚂蚁的发展历程

蚂蚁集团 Ray 发展历史

蚂蚁 Ray 团队成立于 2017年中旬,也就是在 Ray 开源后的一年。当时 Ray 的版本跟现在差距还比较大,架构设计上也有很多需要优化的。经常有社区的同学会问:“为什么蚂蚁在这么早期就决定投入到 Ray 这个开源项目中?是什么原因可以让团队下定决心?” 从笔者的角度来看,我们最初是被 Ray 清爽的 API 设计所吸引了。Ray 是一个基础设施层的引擎和编程框架,面向的主要是程序员群体。Ray 清爽、简洁的 API 设计可以说是分布式系统或者应用开发者的福音。Ray 核心的 API 从设计之初到现在,基本上没有改变过,也从侧面证明 Ray 创始团队对这套 API 优秀的设计。

Ray 在设计之初主要面向强化学习场景,因此只有 Python API 的支持。但考虑到蚂蚁内部的应用场景大部分是 Java 体系,蚂蚁 Ray 团队在早期做的一项比较重大的工程就是让 Ray 支持 Java API。其实说 Java API 并不准确,因为它并不像封装一套 API 那么简单,实际上是 Java 分布式编程框架。有了这套框架支持,我们支持了公司内部第一个更上层框架:流图计算 Geaflow。Geaflow 利用 Ray 灵活的 Java API 实现了一套流计算和图计算一体的融合计算框架,2018年在蚂蚁的安全风控场景落地,取得了不错的效果。这也打响了 Ray 在蚂蚁集团发展的第一枪。

2019年,我们落地了另外一个重要的融合计算场景,即在线学习。在线学习引擎融合了流计算、模型训练和模型部署三个场景,主要面向实时推荐场景,在支付宝首页推荐中落地后取得了不错的效果。在线学习引擎是一个多语言的框架,流计算通过 Ray 的 Java API 实现,模型训练通过 Ray 的 Python API 实现。为了实现多语言的融合引擎,我们在 Ray 中还支持了另外一个重要的功能:跨语言调用。简单的说,就是你可以在 Ray 的应用程序中,通过 Python 创建或者调用 Java 的 Task 或者 Actor,反之亦可。

2020年,经过几年的发展,Ray 在蚂蚁内部已经初具规模。但当时我们的运维模式是单租户架构,随着业务越来越多,系统的问题慢慢显现出来:集群越来越多,Ray 版本越来越难收敛,作业启动成为越来越多的业务瓶颈,资源共享困难等。经过 Ray 团队深度的讨论之后,我们决定对整个架构进行升级,走多租户路线。多租户会给 Ray 集群带来诸多挑战,其中一个比较重要的问题就是资源隔离。因此在当时,我们做的一个比较重要的工作就是在一个大的 Ray 集群中支持划分虚拟集群。同年,运筹优化、科学计算、在线推理等场景也在蚂蚁落地。

随着业务的发展,多年来我们持续不断地对 Ray 进行完善和增强,也沉淀出了一批批新的 feature 和优化点,不断地贡献到了 Ray 社区主分支。到了 2021年,蚂蚁对开源 Ray 内核的代码贡献已经超过了 26%。总结起来,贡献主要是以下方向:Java 分布式编程框架,C++ 分布式编程框架,跨语言调用,Actor task 流控,多租户,新版本 Ray Dashboard,Core Worker 架构,GCS Service 架构,GCS 调度,Actor Direct Call 等。

2022年,我们将 Ray 的应用领域拓展到隐私计算领域,与蚂蚁隐私计算团队合作推出了开源框架“隐语”。后期随着 Ray 在隐私计算场景的探索,我们还孵化出了一个新的项目 RayFed。同年,Ray 也在公司内部函数计算场景落地,同时也在南网、浦发、中信等企业实现商业化输出。

时间来到 2023年,我们正在做的事情是统一 AI 服务框架,希望通过基于 Ray 的一套引擎,同时支持传统推理、大模型推理和搜索引擎等重要场景的 AI 服务。

Ray 在蚂蚁的架构体系

历经多年的社区共建打磨,Ray 已经发展成为蚂蚁集团的计算基础设施底盘(作为高性能计算的分布式研发框架)。Ray 提供了灵活易用的分布式编程 API 及任务调度、编排、服务发现、多语言、跨语言、故障恢复、自动扩缩容、无感迁移、云原生等能力,使得上层计算引擎或分布式应用开发者只需关注计算 pattern 内的事情,专注于核心价值,极大地缩短了分布式应用从原型设计到生产上线的研发周期,提高了研发效率。

Ray 在蚂蚁的架构体系如下图:

从整体上看,在最底层我们依托于 Kubernetes 提供服务。基于 Kubernetes 的 Pod 交付能力,我们将 Ray 打造成了一套通用计算底座,为上层提供通用分布式编程 API、运维组件和可观测组件。基于通用计算底座,我们提供了一系列基础库,除了社区原生库如 Ray Serve、Ray Data 等以外,还有许多自研的基础库,如 RayFed、Realtime 和视频算子库等。基于基础库,Ray 在蚂蚁内部衍生出了多种计算引擎并服务了多种业务场景,如:

  • 在线服务(Ray Serving):在线推理引擎,EventBus,金融核心。
  • 图计算(GeaFlow):安全、数金 、图谱、社交、IOT。
  • 在线学习(Realtime):智能营销、首页 Feed 流、Tab3。
  • 金融计算(Mars):罗马、灯火。
  • 运筹优化(RayOR):支付宝资金调度、欧拉。
  • 函数计算(FC):推荐平台、数字科技、生态质量、算法中台、智能对话。
  • 隐私计算(SecretFlow/Fair/Morse):微贷、阿里云医疗、国际。
  • 强化学习(RLlib):Tab3、运筹。 Ray 规模化背后的技术挑战与实践

Ray 规模化落地的挑战

最近在支付宝 Tab3 生活频道刷到了罗翔老师的一个短视频,他说非常欣赏一位法官说的这样一段话:“贪腐就像蝙蝠一样,只在黑暗中翩翩起舞;而正义就像鲜花一样,只有在阳光下,才能看到它的美”,说的很有哲理,但这位法官最后因贪腐被双规了。所以他感慨道:“世界上最遥远的距离不是马里亚纳海沟到珠穆朗玛峰,而是「知道」和「做到」”。

作为挨踢工程师,我相信很多同学在工作中都遇到或参与过从 0 到 1 的分布式应用或系统研发,当你面对一个这样一个从 0 到 1 的分布式应用 / 系统构建时,你首先要做的事情就是技术选型,你知道组件通信可以用 GRPC 或 BRPC,你知道通信协议可以用 ProtoBuffer 或 FlatBuffer,你知道数据存储形式可以用 DB、Redis 或者自己裸写内存池,你也知道代码写完后要用 k8s 云原生来部署,但当你将要准备着手时,你会发现系统模块之间的衔接、集成、调试与部署有多么复杂,你会发现将所了解的知识进行系统性的转化是多么无力。

Ray 的出现拉近了「知道」与「做到」之间的距离,甚至将「不知道」到「做到」变为可能。

Ray 是一个通用的计算引擎,它提供了一套灵活易用的分布式编程 API,能快速构建各种分布式系统且不绑定计算范式,同时屏蔽分布式系统的底层细节,它提供的基础能力有:

  • 组件通信:通过一个简单的 ray.remote 屏蔽所有底层 RPC 框架及协议的细节。
  • 资源调度:通过在类上进行简单的 @ray.remote 注解或者 .option(...) 调用就能指定任务执行所需要的资源或者调度策略。
  • 数据存储:通过简单的 put、get 及 wait 等接口就能管理任务的依赖及输出。
  • 多语言:支持 python、java 及 c++ 三种主流语言,同时每种语言之间可以相互调用,能很容易的将多种语言所组成的系统有机的衔接融合在一起。
  • 故障恢复:通过在类上简单的表注 @ray.remote 或者执行 .option(...) 调用就能指定实例 Failover 或者任务的重试次数。
  • 部署:通过 kuberay 与 k8s 深度集成,提供高效的云原生部署的效率。

利用 Ray 提供的基础能力可以很容易地构建出你的系统原型。在之前的文章中我们分享过一个 Case:我们尝试利用 Ray 所提供的 Actor、Task、资源管理、调度与运行时环境等能力构建了一个 AutoML Service Case ,与云原生开发方式相比,发现 Ray 对整体研发效率上提升非常明显。

AutoML Service Case

上面 Case 仅是单个作业,对应社区的用法是一个 Ray 集群。蚂蚁目前总体 100 万核规模、实时长跑作业 8k+,那么面对这样规模的业务体量,面临的挑战有哪些呢?主要的挑战在以下几方面:

Ray 规模化落地中的挑战

集群管理:面向上万个离在线作业,如何做好集群管理和服务是 Ray 团队面临的最严峻的挑战。多租户下会暴露 Ray 引擎诸多问题,如集群资源抢占、节点内物理资源隔离、定制化运行环境等。如何权衡隔离性、效率与利用率之间的关系往往是超大集群管理中的难题。并且随时 Ray 本身的迭代,如何做好版本管理和版本升级也是集群管理中重要的工作。一旦版本无法收敛,就需要研发团队同时维护多套代码,增加研发成本。

调度:从调度模式上看,Ray 原生的分布式调度在大规模 Actor 并发调度场景下存在效率问题(分布式决策冲突引起);另外在 Java 业务中,JVM 的启动会成为 Actor 调度的瓶颈;在大规模 scale up 和集群重启场景,Pod 申请开销会占用大部分作业的启动时间,对时延敏感的在线业务不友好;原生的调度从策略上也很难满足所有用户的需求,需要更加丰富的亲和性和反亲和性调度策略;Ray 原生的逻辑资源调度会忽略真实的资源占用情况,引起节点间真实资源利用率的失衡。

运维:计算引擎集群的运维工作往往是件耗时的事情,一旦整个体系不完善,会导致运维工作占用研发团队大部分的精力,产生投入失衡。Ray 上的运维操作主要有:集群创建、人工扩缩容、版本升级、批量作业运维、故障节点处理等。随着业务的增长,运维操作逐渐变得更加频繁,Ray 团队值班工作繁重。如何降低运维成本和提高运维效率是需要重点关注的问题。

稳定性:随着业务的发展,蚂蚁 Ray 生产环境承接了越来越多的核心业务,这部分业务对稳定性要求更高,往往需要 Ray 提供 99.99% 的服务稳定性和调度稳定性。引擎本身的稳定性是基础,除此之外还需要完善的集群和应用自愈体系,以及完善的告警与应急体系。

为了解决以上问题,蚂蚁 Ray 团队在 Ray 的“基础能力”之外,从“多租户”、“多元调度”、“智能运维”和“稳定性”几个方向打造了 Ray 在蚂蚁的“规模化能力”。通过“基础能力”+“规模化能力”共同支撑超 100W 核的业务。

多租户

多租户是指在一个分布式系统中,多个用户或组织可以共享同一套系统实例,并且彼此之间具备一定的相互隔离的能力。通过使用多租户架构能够有效提高资源利用率和复用率、灵活扩展系统、简化系统管理。蚂蚁内部 Ray 集群的多租户主要靠 Virtual Cluster 和 Runtime Env 两套框架来保障,其中 Virtual Cluster 用来从节点维度切分 Ray 集群,而 Runtime Env 用来解决节点内的环境隔离和物理资源隔离。

Virtual Cluster

Ray 在社区的定位主要局限于单个 Ray 集群仅服务于单个应用,大部分的场景是集群随着作业而拉起,随着作业下线而销毁。即作业模式:

蚂蚁内部早期采用的也是作业模式,但随着业务量的增加,各种弊端逐渐显露出来。比如每个作业无论大小都需要冗余一份 Head 及 Redis 元数据缓存资源;多个作业之间有数据依赖时无法直接使用 Ray 的 API 来交互,必须要引入一个公共服务或中间件来间接完成数据交互;成百上千的作业版本冗杂,维护比较困难。

面对这些问题,我们推出了集群模式,用户的多个作业可以提交至同一 Ray 集群,集群支持异构节点部署,异构节点通过 work group 来划分(如下图:有红、绿、蓝三种 work group,每一种异构节点仅隶属于一个 work group),集群内的作业共享同一套系统组件(GCS、Dashboard、Raylet、TBase 等),作业之间可以直接通过 Ray API 完成数据交互。

切换到集群模式后,我们面临的第一个问题是集群内的资源抢占问题。由于 Ray 的调度是动态化并且在运行时随机发生的,集群内会发生不可预知的资源抢占,更棘手的情况会在多个作业之间发生资源死锁。特别是对于在线服务类业务而言,作业重启中资源被其他业务抢占后会导致业务恢复时间不可控,进而会引起严重的服务质量降级。如何才能保证“不同类型业务”之间的资源隔离性及稳定性?为此,我们引入了 Virtual Cluster 概念, 每一个 Virtual Cluster 可以承载多种 work group 的节点(如下图 VirtualCluster1 划分了绿、蓝两类 work group 的若干节点)。针对同类型业务内“不同作业”的隔离性诉求,我们提供了「混部」和「独占」两类 Virtual Cluster(分别如下图 VirtualCluster1、VirtualCluster2),前者单个节点可以同时执行多个作业的 Task,后者单个节点同时只能执行 1 个作业的 Task。在集群模式下,通过这两种模式的有机结合能覆盖大部分业务场景,用户可以根据自己的需求来权衡适合的模式,以达到理想的性能和稳定性。

Runtime Env

Virtual Cluster 从集群的维度提供了节点资源切分与隔离的能力,但在单节点内部,Ray 的运行时也较为复杂。不同于 K8S 的交付对象 Pod,Ray 的交付对象仅仅是一个进程。因此在 Ray 的单节点内部,无法避免地会发生多个进程共享节点去执行 Task 或者 Actor 的情况。如下图所示,无论对于「混部」还是「独占」的 Virtual Cluster,节点内都会发现多个 Task 同时运行。

节点内同时运行多个 Task,该场景主要存在两方面的隔离问题:代码运行环境和物理资源。

首先,代码运行环境隔离。随着 Ray 上的业务越来越复杂,不可避免地会出现两种复杂的场景:同一 Ray 集群,不同作业之间,存在差异化的代码运行环境要求;同一作业内,不同 Actor 或 Task 之间,存在差异化的代码运行环境要求。而 Ray 节点的基础环境独立于任何作业而存在,所以不可能将所有的环境依赖预装到 Ray 节点的基础镜像里。无论以上哪一种情况,都需要一套细粒度的(Actor 和 Task 维度)运行时环境框架来支持。

其次,物理资源隔离。Ray 中的调度为逻辑资源调度,即分配给 Actor 或 Task 的 CPU、Memory 等资源仅仅在调度器中记账,但实际上不限制进程的物理资源使用。当 Worker 进程的物理资源使用超出实际申请的资源时,会在很大程度上影响同节点的其他 Task。因此对于稳定性要求较高的 Task,需要考虑引入物理资源隔离机制。

Runtime Env 框架即解决了以上两方面的问题。Runtime Env 框架是蚂蚁 Ray 团队在开源社区共建开发的一套面向一站式解决多租户场景下运行时环境构建问题的框架,经过多次架构重构后,目前已经成为 Ray Core 中关键的组件。Runtime Env 框架有如下特点:

  • 细粒度的环境定制:同时支持粗粒度的 Job 级环境定制和细粒度的 Actor/Task 级环境定制;在触发条件上支持 eager 模式和 lazy 模式,满足不同的场景需求。
  • 插件化设计:内置丰富的插件实现(如 pip、conda、py_modules 等),外置插件可独立于 Ray 版本维护。
  • 跨语言设计:覆盖应用环境构建的全场景。无论是多语言还是跨语言,都可以实现“只开发一个 Python 插件”即可使用。

蚂蚁内部的 Runtime Env 框架在社区版本基础上进行了增强。以 Python Task 运行环境为例,我们为 Python 用户提供了不同维度的隔离化运行环境:最简单的是 VirtualEnv,即通过继承 Ray 节点中的 Python 基础环境再加上独立安装的用户依赖来运行 Task,这种方式隔离了 Task 之间的 Python 包依赖。然后是 Conda。相比 VirtualEnv,Conda 环境除了可以隔离 Python 包依赖,还可以隔离整个 Python 环境,做 Python Executable 和一些 Native Libraries 的定制。最后是 Container,这也是云原生场景下标准的隔离手段。通过定制 image,将 Task 运行在特点的 Container 里,可以实现 rootfs 级别的代码运行环境定制与隔离;同时由于 Container 集成了 cgroup 的能力,这种方式可以同时实现物理资源隔离。

如下图,从右到左看,三种方式的隔离性是从弱到强,但使用体验是由轻到重。我们认为,Ray 上的应用对隔离性有着不同的要求,不同的业务可以根据自身情况去权衡隔离性和使用体验,选择最适合的方式。这相比 K8S 给了用户更多的选择。

Runtime Env

多元调度

调度是分布式计算引擎非常核心的能力,对于 Ray 来说也不例外。随着业务越来越多样,随之而来的会是越来越多的差异化调度需求。蚂蚁 Ray 团队根据内部的业务特点,分别从调度模式、调度策略、调度效率和调度质量四个维度构建了一套多元调度体系,这套体系可以稳定地支撑蚂蚁的 Ray 生产环境。

调度模式

Ray 原生的调度方式为分布式调度,即每个节点的 Raylet 具有全局资源视图,在调度 Task 和 Actor 的时候是 Raylet 之间的点对点调度。这种调度方式在低并发的场景中可以实现高效的调度,可以满足大部分社区用户的需求。但在蚂蚁内部,我们遇到了很多大规模并发调度 Actor 的场景,比较典型的场景是集群重启后的 Job 并发提交。在此场景下,由于分布式调度中各 Raylet 节点间的资源视图无法快速同步,会导致调度冲突,调度请求 reject 的概率增高,反而影响调度效率。

因此,我们增加了新的调度模式,基于 GCS 的集中式调度。经过多年的实践经验,我们形成了“Raylet 调度 Task,GCS 调度 Actor”的最佳实践。

调度策略

策略方面,首先我们有基于水位线的平铺 + 堆叠混合调度策略。平铺的优势是更加负载均衡,堆叠的优势是资源碎片更少。不同业务可以根据自己的业务特点来定制水位线,权衡这两种策略。

面向复杂的自定义编排场景,我们支持了 Ray 的 Placement Group(PG)。用户可以通过 PG 预留单节点或跨多节点的资源组,一方面可以实现 Actor 和 Task 的 gang-scheduling,另一方面可以通过不同的策略(PACK 和 SPREAD)实现亲和性和反亲和性调度。

有了 PG 之后,用户可以实现各种复杂的调度需求,但增加了应用程序开发的成本,毕竟用户需要自己管理资源组。有些业务的调度需求并没有那么复杂,因此我们参考 K8S 的设计,引入了基于 Label 的调度。基于 Label 的调度接口简单,同时可以满足大部分亲和性和反亲和性的调度需求。

在实际生产中,往往存在一些少量珍贵的资源,我们通常称为稀缺资源。比较典型的稀缺资源有两种:GPU 和超大内存。对于稀缺资源,我们往往不希望它们被无关的 Task 占用,需要把资源留给真正需要它的 Task。因此,我们新增了稀缺资源调度功能。调度器会优先将 Task 调度到非稀缺资源的节点上,最大程度去保障稀缺资源的利用率。

调度效率

效率方面,我们除了通过上文提到的 GCS 集中式调度来提高调度效率外,还做了一些特殊的优化。

蚂蚁内部存在着大量的 Ray Java 应用。对于 Java 应用来说,一个明显的瓶颈就是 JVM 启动速度,它占用了大量的 Java Actor 的交付时间。针对这个问题,我们支持了“线程化”Actor,即多个 Java Actor 可以共享一个 Java 进程。JVM 复用后大大提升了 Java Actor 的交付效率,该功能在蚂蚁内部得到了广泛的应用。

Ray 是一个高弹性的计算引擎,集群在运行中会频繁地发生扩缩容,即 Pod 被反复创建和销毁。Pod 的创建和销毁还会发生在集群重启的场景,如配置修改和版本更新。Pod 的创建是一个相对较重的过程,而且在实践过程中,还会经常遇到不稳定因素导致 Pod 创建被长时间 Pending。Pod 的交付会直接影响 Ray 上 Actor 和 Task 的调度。针对这个问题,我们在 Ray 底层支持了 Pod 缓存(预留)机制,减少 Pod 的反复销毁和创建,提升 Ray 的交付效率和稳定性。

调度质量

Ray 原生的调度是一种逻辑资源调度,即节点资源的计算仅仅根据用户填写(申请)的资源进行扣除。这样的方式往往需要用户对每一个 Actor 和 Task 的资源进行精准的控制。但在实践过程中,往往会出现资源申请量和实际使用量的偏差,这样会带来两个弊端:

  1. 申请量大于实际使用量时,节点上被分配的 Actor/Task 太少,资源利用率会偏低。这也是我们在生产环境中经常发现的问题。
  2. 申请量小于实际使用量时,节点上容易被分配过多的 Actor/Task, 导致 OOM 等问题。

因此,我们实现了基于真实资源的调度。在节点端,我们会周期性地采集所有 worker 进程的真实资源使用情况,经统计后更新到 GCS。在此基础上,上文提到的调度策略就可以基于集群的真实资源情况来执行。

在蚂蚁的在线学习等业务集群上,我们已经大规模启用了真实资源调度,并收获了显著的资源利用率提升和稳定性保障。

智能运维

如上文提到,大规模的业务会带来大量的运维工作,我们需要不断提升 Ray 运维的智能化水平,提升运维质量,解放运维带来的人力投入。因此在智能运维方向,我们做了以下三方面的工作:自动弹性、无感迁移和滚动升级。

自动弹性

上文提到,为了支持多租户架构,蚂蚁内部引入了虚拟集群(Virtual Cluster)。因此相比社区版本的 Ray,蚂蚁内部 Ray 的弹性架构更加复杂,主要分三层:作业弹性、虚拟集群弹性和物理集群弹性。对于作业来说,需要根据负载调整自己的实例数量和规格,这个实例落到 Ray 上就是 Task、Actor、PG 或者 Serve 的 Deployment。作业的弹性会首先反映到虚拟集群的弹性上,虚拟集群需要根据用户实例和集群空闲资源情况决定是否要扩容或者缩容,而虚拟集群弹性的资源池是真正的 Ray 集群。最后,Ray 集群的弹性会反映到基于 K8S 的 autoscaling 上。

为了支持多级的自动弹性,我们接入了公司内部的端到端弹性优化系统:Cougar。Cougar 是蚂蚁内部 AI 和大数据领域统一的弹性服务,可以根据应用和引擎的离线数据和实时数据构建资源画像,然后提供运行时的横向和纵向两个维度的弹性决策。具体到 Ray 中,我们会通过 Ray 的 Dashboard 透出作业、虚拟集群、物理集群三个维度的数据给 Cougar 系统,然后通过 Cougar 系统反馈的决策做最终的弹性(扩容或者缩容)。

无感迁移

在大规模集群长跑过程中,我们往往会遇到机器故障或大促腾挪等场景,因此 Ray worker 节点 (Pod) 被下线是不可避免的。但在此过程中,经常会导致业务在一定程度上被中断,从而引入非常繁杂的人工运维。为了缓解此类问题,我们实现了无感迁移功能,可以使 Pod 下线操作对上层业务透明,保证业务可用性不受影响。

无感迁移的挑战有:

  • 长、短周期作业并存,无法简单的设置 timeout 来强杀;
  • DAG 类型作业要求 FO 频次尽可能少;
  • 迁移时机不一。

因此,我们将无感迁移的管控功能实现在 Ray 集群对应的 K8S Operator 上。当 Operator 探测到 Pod 下线需求时,会根据作业的特性进行 batching,再选择适当的时机通知 GCS (并补充新节点用于后续的 Actor 迁移)。GCS 根据待下线 Pod (Worker 节点) 上的 Actors,会向相应的作业通知哪些 Actors 需要迁移。基于我们提供的 SDK, 用户可以预先自定义收到 Actor 迁移通知时的处理逻辑。由于该处理逻辑独立于业务代码,因此,Actor 迁移操作可以做到对业务无感,从而极大地缓解人工介入的成本。

滚动升级

伴随软件版本的迭代,Ray 引擎中的 Bug 会得到修复,我们也会引入性能提升和一些新特性,这些都促使用户对集群进行引擎版本升级。然而,在一个长期运行的集群中,一方面用户期待在线服务的业务不会受到升级影响而中断,另一个方面用户的资源(尤其是当下热门的 GPU 资源)大概率是有限的,这两点制约因素都要求引擎提供滚动升级的能力,即分批次地升级集群中已有的节点。

滚动升级带来了两点新的挑战:第一是滚动升级的过程中,集群处于一种多个软件版本的共存的状态,这就需要一套协议来确保版本之间的兼容性;第二是当某个节点被升级时,节点上的所有业务进程都会被杀死,引擎要和用户达成业务进程迁移的协议,在升级前通知业务代码做好准备并提供必要的资源。

为了实现滚动升级,Ray 团队充分利用 Ray 的核心能力,开发了滚动升级作业来协调整个集群的有序升级。在升级过程中,我们首先升级系统组件,包含 Head 节点和 SystemJob 节点,这一过程是对节点内的容器进行原地升级重启,而 Ray 的核心组件 GCS 会进行一次自动故障恢复,从 Tbase 中恢复集群状态。Worker 节点的升级由一个滚动升级作业来完成,这个作业根据用户指定的规则,选择需要升级的节点,并通知业务作业迁移节点上的任务,在任务迁移完成后,作业发起对节点内容器的原地升级。这一过程主要利用了 Ray 引擎的弹性扩缩容来为业务作业迁移提供足够的资源,并借助无感迁移协议确认任务全部安全迁移后再进行节点的升级。

稳定性

自愈能力建设

分布式系统是由多个节点组成的复杂网络,每个节点都可能由于硬件故障、软件错误或网络问题而导致故障。良好的自愈能力是分布式系统高可用性、稳定性、可靠性、容错性的必要前提。

作为快速构建分布式应用的编程框架,Ray 为上层应用提供了丰富的自愈能力。同时 Ray 集群本身也是一个分布式系统,所以 Ray 对节点、自身系统组件异常等情况也做了多方面的考虑。下面将从“集群自愈”和 “应用自愈”两方面进行介绍。

Ray 集群自愈

Ray 自身就是一个分布式系统,为了保证给上层应用提供稳定的服务,我们首先对 Ray 自身的自愈能力进行了建设。先回顾下 Ray 的集群架构:

Ray 集群分为 Head 和 Worker 两种角色的节点。

  • Head 节点一般一个 Ray 集群只有一个。里面有 GCS(Gloabl Control Store)、Dashboard 等进程。Head 节点负责整个 Ray 集群的元数据管理和中心化服务。
  • Worker 节点上有 Raylet 进程主要负责节点内的 Actor 和 Task 管理以及分布式调度;Dashboard agent 进程负责运行时环境构建、Metrics 和实时信息采集等。

从以上架构可以看出,Ray 集群能够正常服务的前提是 Head 节点及其元数据的可靠性。在蚂蚁内部,我们在以下两方面来保证 Head 节点的高可用。首先是 Head 元数据高可用。蚂蚁生产环境中的每个 Ray 集群都会配置一套独立的 Tbase 集群来做元数据的存储。Tbase 集群接口兼容 Redis,同时可以在故障时自动进行存储节点的主备切换。GCS 重启的场景也可以自动从 Tbase 中恢复故障前的元数据。其次是 Head 节点高可用。Ray 本身的所有节点都依赖 Ray Operator 和 K8S 进行故障恢复,即具备 Pod 重新调度的能力。但对于 Head 节点而言,Pod 恢复期间整个调度和作业接入服务不可用,对业务影响较大。因此,为了实现 Head 节点的秒级恢复,我们以 standby 的方式为所有高保的 Ray 集群同时启动了 2 个 Head 节点,他们通过 Tbase 进行选主,确定主备关系。在主 Head 节点出现故障后,备用 Head 节点会经过选主协议升主,继续为整个集群提供服务,实现快速恢复。

除此之外,Head、Worker 节点上的 GCS/Dashboard/Raylet 等关键进程,都具备本地自愈能力。进程偶发性异常退出会触发本地自愈功能,这种方式相比 Pod 重建效率更高。连续高频的进程异常退出会回退到 Pod 维度的自愈做兜底。

Ray 应用自愈

Ray 本身无法感知到应用内部的状态和复杂的业务逻辑,因此无法提供无感知的应用自愈。但 Ray 提供了多维度的自愈的原子能力,方便用户在集成过程中快速构建一套具备自愈能力的应用。主要有以下几个方面:

  • Actor 自愈:Ray 的 Actor 相当于用户的一个应用进程,如果 Actor 因为用户自己业务代码 bug crash 了,Ray 会认为这个 Actor 是异常 crash 了,会重启调度拉起这个 Actor。Ray 还提供了 max_restarts 参数给予用户灵活设置最大的重启次数。如果设置为 0 或是重启次数超过 max_restarts,Ray 就不会再次拉起这个 Actor 了, 而且将其标记为 Dead。所以用户可以灵活设置自己的 Failover 策略。同时 Ray 还 max_task_retries 参数让用户灵活设置 actor task 是 at-most-once 语义还是 at-least-once 语义。
  • Task 自愈:Ray 的普通 Task 具备血缘回溯的能力,即当一个 Task 依赖的 Object 丢失时,可以通过血缘找到 Object 对应的 Task 并重新执行,以此恢复 Object。- 作业局部自愈:Ray 的用户基本是提交一个 Ray 作业就是一个完整的应用。一个 Ray 作业里可能同时创建了几百上千的 Actor。Ray 同时还提供了 Attached/Dettached Actor 的能力来帮忙用户管理具有层级关系的 Actor 的 Failover。此特性在大数据计算中很实用,在计算 workflow 中某个算子故障 failover 后,可以自动触发下游节点的 failover。
  • 作业全局自愈(L1FO):L1FO 是专门为某些特殊场景开发的特性。此特性可以做到一个 Ray 作业中某个 Actor 发生 crash 后。同时触发整个作业 所有 Actor 的 Failover。让整个作业重启自愈。

告警体系建设

最后是监控告警体系建设,很多系统里也叫 Observability。对于计算引擎来说,这部分工作一般需要结合各公司的基础设施来进行集成。

在 Ray 的监控告警体系中,主要有三种数据源:Log、Event 和 Metric。

Log:Ray 的 logs 相对较为复杂,logs 文件多,数量不固定,并且文件名里会有多个变量。我们将 Ray 的 logs 对接到蚂蚁内部的 SLS 服务中,实现了 logs 的集中化收集和 logs 无盘化。同时在 Ray Dashboard 中提供 standalone 的 logs 查询能力,方便服务简单的查询场景。复杂的 logs 查询与分析场景会引导用户直接去 SLS 平台。

Event:早些年,我们在 Ray 的内核中开发了 Event 框架。events 可以看作特殊的 logs,具有结构化的数据。在分布式系统中,经常会发现一些难定位的分布式故障,这种故障往往涉及到多个角色和组件。我们通过将集群内关键的日志转化为结构化的 events,大大提高了分布式系统故障定位的效率,也为告警提供了可靠的数据源。

Metric:Metric 几乎是任何一个服务系统必不可少的数据。同样的,Ray 在蚂蚁内部接入了公司内部的 Metric 基础设施,并通过集群级和作业级两个维度来建设 Metric 埋点。通过已有的 Metric 埋点,我们构建了多维度的监控大盘,这些大盘同时服务集群的运维人员和应用的运维人员。

有了 Log、Event 和 Metric 三种数据源后,我们就可以建设 Ray 的告警体系和应急体系。另外特色的,我们还构建了一套根因分析系统,通过一套规则引擎自动分析分布式系统中各种异常日志和事件之间的关系,自动分析故障的根因,提高故障定位的效率,降低故障处理的人力成本。

展望

尽管蚂蚁 Ray 团队在努力打造通用分布式基座以及支持上层分布式应用 / 框架方面取得了诸多进展,但在应对如此庞大的业务体量和复杂的业务场景时,仍然存在很大的改进空间。主要体现在以下几点:

资源利用率。目前 Ray 的总业务资源占用超过 100 万 CPU 核,总 CPU 平均利用率 10%~20%,总内存平均利用率在 30%~40%。导致利用率低下的因素有很多,如:

  • 自定义镜像:Ray 所提供的基础镜像能覆盖大部分场景,但仍然存在一部分业务作业依赖较为复杂,期望通过定制基础镜像创建独立集群的方式来加速作业运行时依赖下载的开销,不同基础镜像的集群可能存在各种依赖冲突无法合并进行作业混布,导致利用率低下。
  • 集群灾备:蚂蚁内部存在多个机房,一些在线类业务往往配备了多个 Ray 集群以降低集群或机房内基础设施故障带来的可用性风险,每个机房之间的资源碎片不能相互整合也会影响整体利用率的提升。
  • 业务类型差异:基于 Ray 通用分布式计算底座构建的上层引擎 / 框架较多,每类引擎 / 框架都会配置不同的 Ray 集群参数以定制适合于自己的集群。“RayServing- 在线服务”集群定制了 Runtime Env 及自定义资源能力;“GeaFlow- 图计算”集群定制了独占型虚拟集群、全局 Failover,Actor 线程化等能力;“Mars- 科学计算”集群定制了亲和性调度、自定义编排、Object Store 大对象依赖传输等能力;“Realtime- 在线学习”集群定制了真实资源调度能力;“FaaS- 函数计算”集群定制了三方 sidecar 部署及 cgroup 资源限制能力。不同类型业务所定制的集群所提供的能力可能存在冲突,导致集群不宜合并。如:“Mars”需要的 Object Store 大对象依赖传输能力需要预留足够的共享内存,挤压了业务运行时可调度的内存资源,而这是其它类型业务不期望的;“FaaS”需要的 sidecar 部署能力来启动 Service Mesh 系统的中间件,挤压用户资源的同时也会拉低 Pod 的交付时间,而这也是其它类型业务所不期望的。
  • 历史版本迁移困难:早期版本的 Ray 集群采用的是作业模式,这种模式的好处是针对“离线短周期任务”执行完后能及时释放资源,弊端是作业环境及依赖相对独立,很难及时跟随大版本升级,随着时间的推移管控及运维越来越难。现阶段,内部仍然存在相当一部分历史版本需要升级整合。

节点迁移实效性。现有的无感迁移基于“优先增补节点”为前提,即优先保障新节点补进后再触发迁移逻辑,以保障迁移过程资源可靠性,但在 K8S 资源池紧张情况下,很有可能没有新的节点能够补进,进而影响整体的迁移实效性。除此之外,Ray 所触发的迁移事件亦依赖于上层引擎 / 框架的迁移行为,上层引擎 / 框架在收到迁移事件可能会因为各种因素(如:Bug、迁移规则等)影响,导致迁移进程受阻,影响整体的迁移实效性。近期我们设计了无感迁移 2.0 方案,结合弹性伸缩从“优先增补节点”到“仿真资源缺口”转变,从被动依赖上层引擎 / 框架迁移行为到主动提供多种迁移策略转变,以多方位提升迁移实效性。

滚动升级兼容性。由于 Ray 的各个组件依赖众多,系统的 API 也极为丰富,在版本迭代过程中,难免会产生版本间的不兼容性。不兼容的版本会在滚动升级中引起不可预期的问题,最终仍然要回退到原始的集群升级方式(即全局重启)。

开闭源系统差异性。鉴于内外业务及发展定位差异性等因素的影响,蚂蚁内部基于不同业务场景研发的 Features 推入社区周期较长,社区一些新的迭代回合也有一定滞后,开闭源研发节奏和侧重点的差异导致了代码的差异也逐步扩大。随着大模型浪潮在全球范围内的兴起,社区在 AI 领域的研发节奏也逐步加快,蚂蚁内部用户对 Ray 在 AI 方面的产品生态(如:RayData、Stream Generator 等功能)也越来越关注。因此,我们将逐步与社区最新版本同步,以支撑内部业务在 AI 方面的新发展。

Ray 在业界发展的现状和趋势

介绍完蚂蚁的情况,让我们从项目本身的角度出发,看看 Ray 在业界的发展现状和趋势。

谁在用 Ray

首先是大家最关心的第一个问题:哪些企业在用 Ray。我们汇总了部分企业:

从上图可以看出,Ray 在企业应用领域的普及程度越来越高。除了众所周知的 OpenAI,国外的科技巨头(如 Google、AWS、Microsoft、Meta)也都在积极拥抱 Ray。传统企业(Adobe、Vmware、IBM)和硬件厂商(NVIDIA、Intel)也在积极融入 Ray 的生态圈,国内企业蚂蚁集团、网易、字节跳动、华为等也都在 Ray 上持续发力。此外,许多知名创业公司也选择了 Ray,其中原阿里云计算平台负责人贾扬清在去年投入创业后,也选择将 Ray 集成到他们最新的开源项目 Lepton AI 中实现分布式计算。

一些数据

接下来再看一些有关 Ray 开源社区发展的数据

Github Stars 数方面。从 2016 年开源至今,Ray 已经积累了 28k+ stars。比较有意思的是,Ray 的增长速度在近期达到了跟 Spark 同期的水平。另外一个里程碑是在去年年中,Ray 的 stars 总量超过了 Kafka。

Contributors 数方面。Ray 在社区有不错的数据和活跃度,近一年内(2022.09~2023.09),Ray 的 Contributors 数量从 700 增长至 870(最新的数据是 890),增长幅度 25%。

Ray Clusters 数方面。据 Anyscale 统计,从去年 1 月到 9 月,全球范围 Ray 集群数量在去年已经增长了 6 倍。

这几个维度的数据从一定程度上反映了 Ray 在开源社区良好的增长趋势。

为什么用 Ray

另一个大家比较关心的问题就是:为什么使用 Ray

我们收集了一些会话:

首先是 OpenAI 的 Co-founder John Schulman,他在访谈中分享了一些使用 Ray 的经历。事实上,他是 Ray 最早一批用户,在读 PhD 期间就在尝试使用 Ray。他提到,在 OpenAI 内部有一个专门做分布式训练的 library,Ray 作为这个 library 重要的一部分负责通信工作。他强调 Ray 是一个非常 solid 的组件。

John Schulman, Co-founder, OpenAI

LinkedIn 的 VP,Ya 分享了他们使用 Ray 构建 ML 应用的感受。为了支持拥有多模型的复杂应用,他们非常依赖 Ray 做在线应用的编排。具体来说,他们通过 Ray Serving 将整个推理图切分成多个原子层,同时优化 CPU 和 GPU 的混合计算场景。

Ya Xu, VP of Engineering, Head of Data and AI, LinkedIn

Adobe 和 Niantic 也分别分享了他们使用 Ray 的感受。Adobe 的 Ersin Yumer 总结了他们喜欢 Ray 的三个原因:Flexibility,Extensibility 和 Scalability。Niantic 的 Brian McClendon 分享了 Ray 在开发上带给他们的便利性:Ray 可以减少 85% 用于编排任务的代码,加速生产化应用。

应用案例

接下来的问题:用户是如何使用 Ray 的。我们在这里分享三个案例:

第一个案例是 Spotify。Spotify 作为流媒体平台,拥有超过 5 亿的用户。如下图所示,他们充分利用 Ray 构建自己的 AI 平台 Hendrix。在该平台中,最底层利用 Ray 提供了计算基础设施的通用能力:GPU 管理,网络,资源调度,监控,运行时环境等。上层通过 Ray + Pytorch 封装 MLOps SDK,提供标准的 ML 能力:Features,Training,Serving 等。对于特殊的需求,通过 Ray 进行 OSS Ecosystem 的扩展,实现相应的功能(如 Embedding Evaluation,Graph Learning 等)。这是一个非常典型的利用 Ray 来构建一体化 AI 平台的案例。

Spotify

第二个案例是 Pinterest。Pinterest 是全球知名的社交媒体平台,拥有超过 4 亿月活用户。他们分享了内部训练场景中最后一公里的数据处理架构演进。在先前的架构中,他们通过集成多进程的 Pytorch data loader 到 GPU 节点进行训练前的数据处理,如下图。这种架构的主要问题有:(1)仅利用单机多进程处理数据,scale 能力有限。(2)CPU 运算和 GPU 运算在同节点,GPU 利用率低,无法实现高效的异构计算。

Last-Mile data processing by Pytorch data loader

为了解决以上问题,他们进行了重构,利用 Ray Cluster 管理和调度异构集群,如下图。他们通过 Ray Dataset 对训练前的数据进行统一的预处理,可以利用多 CPU 节点实现并行化;处理后的数据灌入 GPU 节点进行训练。他们还优化了数据转换和拷贝的链路,使整个流程更加高效。重构后的系统,相比之前 Pytorch data loader 的方案,在复杂场景下可以降低 40% 的训练耗时。

Last-Mile data processing by Ray Dataset

第三个案例是 Uber。下图是 Uber 内部支持 LLMOps 的架构。他们选择了流行的开源技术栈:NCCL 和 Torch 作为基础库,Deepspeed + Huggingface Transformers 训练 SOTA LLMs,通过 Ray 的 TorchTrainer 调度和编排训练中的进程组。这也是一个比较典型的 Ray 在 LLM 基础架构中应用的案例,可以直观地看出 Ray 与其他 LLM 生态框架的集成关系。

LLM Training Architecture

发展趋势

最后,分享一些我们从 Ray Summit 上看到的发展趋势。

首先,开源模型越来越好。随着 Llama2 等开源模型的发布,我们可以看到,整个大模型领域已经形成了开源模型与闭源模型共同发展的局面。据 Anyscale 研究显示,被 fine-tuned 的 Llama-2-7B 可以在效果上超过 GPT-4。两个模型的量级相差甚远,但 fine-tune 的效果显著。所以目前一个比较明显的方向就是通过 fine-tune 开源模型来实现自主可控的 LLM 应用技术栈。

Fine-tuned Llama-2-7B vs GPT-4

其次,LLM API 价格越来越低。在 Ray Summit 上,Anyscale 重磅发布了新产品 Anyscale Endpoints。这是一款 LLM API 产品,经过一系列成本优化之后,Llama-2-70B 的 API 价格可以做到 $1/million tokens,是目前市面上最低的价格之一。自从 LLM 火爆全球以后,推理架构和性能的优化一直是除预训练以外最火热的方向之一。相信后续的 API 成本可以做得越来越低,普惠中小企业和大众用户。

The price of Anyscale Endpoint

除此之外,从 Infra 的角度来看,LLM 开源技术栈已经呈现出比较典型的架构。如下图所示(From @Meta),整个架构从底层到上层分别是:云原生服务(如 Kubernetes,云厂商等),以 Ray 为代表的 ML 编排层,以 Pytorch、OpenXLA 为代表的神经网络前后端框架,最上层是主流的开源模型(如 Llama2)。

另一块比较明显的发展趋势是 AI 算力在 Ray 上的原生支持。在 Ray Summit 中,Ray 宣布了与四家持有专属加速硬件的厂商进行合作,它们分别是:Nvidia,AWS,Intel 和 Google。这意味着在更高版本的 Ray 中,无需特殊的适配,即可在训练和推理任务中开箱即用主流的 AI 加速硬件。在大模型时代,AI 算力显然至关重要。未来,Ray 将不断优化新硬件的插件化接口,实现最低成本接入包括国产硬件在内的新型加速硬件。

Nvidia + Ray

AWS + Ray

Intel + Ray

Google + Ray

值得一提的是,最近华为公司也与 Ray 社区进行了深度的合作,晟腾 NPU 已经在 Ray 的最新版本实现了原生支持,欢迎大家试用。

总结起来,随着 LLM 和生成式 AI 的飞速发展,已经在分布式计算领域深耕多年的 Ray 也迎来了高速发展的一年,越来越多的企业正在使用或者正在尝试使用 Ray,Ray 的潜力也在被不断地挖掘中。对于 Ray 本身,基于核心的调度与编排能力之上,在处理复杂的 Data + AI 场景中 Ray 更能够发挥易用性和性能的优势。随着大模型行业的不断发展和变迁,Ray 将深度融合 AI 算力,赋能更高效和开箱即用的 AI 计算。

今年向量数据库“杀疯了”,但纯向量数据库“凉”了?| 盘点

金融业采用大模型,是“用大炮轰蚊子”吗?| 盘点

马斯克被“逼疯”、OpenAI 上演连续剧、QQ 选型遭群嘲|InfoQ 年度最受欢迎文章排行榜

发布 Vue3 让尤雨溪吃尽苦头:犯了3个错,每一个都需开发者警惕


Copyright © 2024 aigcdaily.cn  北京智识时代科技有限公司  版权所有  京ICP备2023006237号-1