云原生場(chǎng)景下如何利用Ray快速構(gòu)建分布式系統(tǒng)
一、分布式系統(tǒng)復(fù)雜性
1、一個(gè)AutoML的case
首先從一個(gè)實(shí)例開(kāi)始,上圖是我們最近構(gòu)建的AutoML的例子,搭建自動(dòng)分布式機(jī)器學(xué)習(xí)的服務(wù)。
圖中虛線框起來(lái)的就是這個(gè)自動(dòng)機(jī)器學(xué)習(xí)的服務(wù),服務(wù)中有如下幾個(gè)角色:proxy是一個(gè)常駐服務(wù),在整個(gè)AutoML集群中負(fù)責(zé)提供服務(wù)的入口,會(huì)根據(jù)不同的AutoML的任務(wù)創(chuàng)建小方框的訓(xùn)練集群;小方框中又有兩個(gè)角色,trainer和worker,trainer 是類似協(xié)調(diào)器的角色,協(xié)調(diào)一個(gè)完整的AutoML任務(wù),在協(xié)調(diào)過(guò)程中不斷地創(chuàng)建worker來(lái)完成整個(gè)AutoML的計(jì)算。
集群外部通過(guò)client接入AutoML服務(wù),將AutoML需要的models和data組合發(fā)到proxy,proxy根據(jù)用戶請(qǐng)求創(chuàng)建trainer,當(dāng)然在這個(gè)過(guò)程當(dāng)中需要通過(guò)K8S管理資源來(lái)創(chuàng)建trainer的Pod或者process,trainer會(huì)通過(guò)解析client 端的這個(gè)models和data計(jì)算需要多少個(gè)worker,同樣通過(guò)K8S創(chuàng)建出對(duì)應(yīng)的worker,開(kāi)啟整個(gè)訓(xùn)練任務(wù)。
每個(gè)worker訓(xùn)練完成后,trainer收集結(jié)果,檢測(cè)整個(gè)AutoML任務(wù)是否完成,最后把結(jié)果返回給proxy,返回結(jié)果后,trainer和worker會(huì)被銷毀。
這是一個(gè)比較完整的AutoML Service服務(wù),可以看到其特點(diǎn)為:云原生、多角色、高彈性、動(dòng)態(tài)化、有狀態(tài)、頻通信。高彈性主要體現(xiàn)在proxy和trainer兩個(gè)角色中,他們都會(huì)在runtime的過(guò)程中不斷地去申請(qǐng)資源。
2、技術(shù)棧分析
接下來(lái)探討一下如果在云原生環(huán)境實(shí)現(xiàn)這個(gè)case,需要考慮哪些事情?下面從兩個(gè)方面進(jìn)行分析。
從技術(shù)棧角度,假設(shè)當(dāng)前是AI應(yīng)用場(chǎng)景,主要編程語(yǔ)言是Python。首先需要開(kāi)發(fā)單體應(yīng)用,包括proxy、trainer、worker幾個(gè)角色。在單體應(yīng)用的編寫中:
- 調(diào)度方面,選擇協(xié)程asyncio管理進(jìn)程的event loop來(lái)構(gòu)建整個(gè)單體應(yīng)用。
- 通信方面,選擇使用protobuf 定義各個(gè)組件的之間的通信協(xié)議,如 proxy、trainer和worker 之間需要傳遞哪些字段,通過(guò) gRPC 完成整個(gè)通信過(guò)程。
- 存儲(chǔ)方面,需要考慮應(yīng)用以及業(yè)務(wù)邏輯設(shè)計(jì)角色的內(nèi)存結(jié)構(gòu),如果業(yè)務(wù)邏輯比較復(fù)雜還需要引入本地存儲(chǔ)如RocksDB,更復(fù)雜的情況還需要引入遠(yuǎn)程分布式存儲(chǔ)如HDFS。
- 部署方面,為了部署分布式系統(tǒng),需要考慮Docker,需要定制各個(gè)組件的運(yùn)行時(shí)環(huán)境,需要了解Kubernetes,知道如何在 Kubernetes 環(huán)境創(chuàng)建這些應(yīng)用的實(shí)例。更復(fù)雜的情況下,如果中間需要一些彈性的過(guò)程,需要開(kāi)發(fā)K8s標(biāo)準(zhǔn)的 operator來(lái)輔助完成整個(gè)分布式系統(tǒng)的構(gòu)建。
- 監(jiān)控方面,系統(tǒng)運(yùn)行以后,需要接入監(jiān)控系統(tǒng),常規(guī)選擇Grafana+Prometheus的組合來(lái)實(shí)現(xiàn)系統(tǒng)的可監(jiān)控性、可運(yùn)維性,達(dá)到生產(chǎn)要求。
3、編程語(yǔ)言分析
從編程語(yǔ)言角度,為了實(shí)現(xiàn)上面的分布式系統(tǒng),需要應(yīng)用下面的編程語(yǔ)言:
- Python,是必不可少的,用于實(shí)現(xiàn)應(yīng)用的內(nèi)部邏輯。
- Protobuf,通過(guò)proto語(yǔ)言定義應(yīng)用的通信協(xié)議,通過(guò)codegen的方式生成各種語(yǔ)言的SDK library,實(shí)現(xiàn)通信 gRPC 的接入。
- Docker,通過(guò)Dockerfile定義應(yīng)用中的每個(gè)組件的差異化需求,比如worker可能需要一個(gè)可以跑GPU 的Python 環(huán)境,可能需要定制Python環(huán)境的版本以及依賴包,還可能需要定制更加native 的環(huán)境,比如library 或者操作系統(tǒng)版本的定制。
- Go,對(duì)于比較復(fù)雜的分布式系統(tǒng),原生運(yùn)維方面的開(kāi)發(fā)也是比較重的,比如我們的case中,需要開(kāi)發(fā)一個(gè)K8s operator,operator的業(yè)務(wù)邏輯需要用 go 語(yǔ)言編寫。
- YAML,原生部署中,YAML代碼的使用是非常多的。系統(tǒng)中最終部署的參數(shù),比如container 的規(guī)格、pod 的規(guī)格、其他一些 label 都需要通過(guò) YAML 的形式定制。
以上就是常規(guī)思路實(shí)現(xiàn)分布式系統(tǒng)需要考慮的技術(shù)棧以及編程語(yǔ)言。
4、分布式系統(tǒng)通用能力
我們可以看到整個(gè)云原生環(huán)境下分布式系統(tǒng)的構(gòu)建是非常復(fù)雜的。但是經(jīng)過(guò)分析可以看到,很多需要研發(fā)和維護(hù)的邏輯是通用的分布式系統(tǒng)的邏輯,跟實(shí)際的業(yè)務(wù)邏輯沒(méi)有直接的關(guān)系。比如上面提到的AutoML的case,業(yè)務(wù)或者系統(tǒng)的開(kāi)發(fā)同學(xué)更關(guān)注AutoML本身的邏輯。那么,是否有一個(gè)系統(tǒng),能夠?qū)⒎植际较到y(tǒng)的通用能力全部解決,使得系統(tǒng)或者業(yè)務(wù)的研發(fā)團(tuán)隊(duì)能夠?qū)W⒂跇I(yè)務(wù)邏輯本身呢?Ray的出現(xiàn)就是為了解決這個(gè)問(wèn)題。
二、Ray簡(jiǎn)介
在正式分享如何利用Ray來(lái)構(gòu)建分布式系統(tǒng)之前,先對(duì)Ray做一個(gè)簡(jiǎn)要的介紹。
1、About Ray
首先從Ray在github上star數(shù)的發(fā)展來(lái)看,從2016年中開(kāi)源到現(xiàn)在經(jīng)歷了兩個(gè)里程碑,左上圖中紅線是Ray的star數(shù)歷史數(shù)據(jù),在2021年Ray的star 數(shù)已經(jīng)超過(guò)了Flink,2023年5月迎來(lái)了另一個(gè)里程碑:超過(guò)了Kafka,目前距離Spark 的star數(shù)還有一定的距離,從star數(shù)的角度看,Ray的發(fā)展非常迅速。
右上是一張 Google 的PATHWAYS論文的截圖,PATHWAYS在 Google 內(nèi)部實(shí)際被看作是下一代 AI 的基礎(chǔ)架構(gòu),在這篇論文中多次提到了Ray,認(rèn)為Ray實(shí)際上非常有可能成為該基礎(chǔ)架構(gòu)中的分布式計(jì)算框架。
右中是最近被大家熟知的大語(yǔ)言模型公司OpenAI的信息,OpenAI在今年公開(kāi)了GPT3.5和GPT4的分布式訓(xùn)練的部分細(xì)節(jié),底層也是通過(guò)Ray來(lái)構(gòu)建整個(gè)分布式系統(tǒng)的。
左下是2022年Ray社區(qū)做的偏向大數(shù)據(jù)領(lǐng)域的一項(xiàng)工作,利用云上的資源做數(shù)據(jù)排序,應(yīng)用shuffle的能力,這套排序系統(tǒng)打破了世界記錄,首次達(dá)到每TB 小于$1的成本(0.97$)。
右下是Ray社區(qū)近期在離線推理Batch Inference方面的一項(xiàng)工作,對(duì)比了Ray 和其他現(xiàn)有的方案,通過(guò)Ray進(jìn)行數(shù)據(jù)處理,實(shí)現(xiàn)一個(gè)pipeline流程給訓(xùn)練系統(tǒng)提供數(shù)據(jù),相比Spark,在Throughput方面有兩到三倍的提升。
通過(guò)上面的信息,大家可以對(duì)Ray有一個(gè)high level的認(rèn)知。
2、Ray是什么
Ray 是由加州大學(xué)伯克利分校RISELab實(shí)驗(yàn)室發(fā)起的一個(gè)開(kāi)源項(xiàng)目,RISELab實(shí)驗(yàn)室在業(yè)界非常知名,Spark 也發(fā)源于這個(gè)實(shí)驗(yàn)室。
Ray是一個(gè)通用的分布式計(jì)算引擎,由于Ray的通用性,很可能會(huì)成為新一代的計(jì)算技術(shù)設(shè)施。在分布式領(lǐng)域,由于Ray的整個(gè)生態(tài),在 AI 領(lǐng)域更是可能成為統(tǒng)一的編程框架,這部分會(huì)在后面的開(kāi)源生態(tài)部分做進(jìn)一步介紹。
3、通用分布式編程API
Ray 的通用性體現(xiàn)在哪里呢?實(shí)際上通過(guò)查閱Ray Core的API,可以看出Ray的設(shè)計(jì)思想是不綁定任何計(jì)算模式,把單機(jī)編程中的基本概念分布式化。
從API 的設(shè)計(jì)可以看出,Ray并不是一個(gè)大數(shù)據(jù)系統(tǒng),尤其是Ray Core這一層沒(méi)有任何大數(shù)據(jù)相關(guān)的算子,而是從單機(jī)編程的基本概念進(jìn)行分布式化的。
具體如何分布式化?我們?cè)趩螜C(jī)編程中經(jīng)常用到兩個(gè)非常核心的概念,一個(gè)叫Function,一個(gè)叫Class,在面性對(duì)象的編程語(yǔ)言里面,基本上大家會(huì)圍繞這兩個(gè)概念進(jìn)行代碼開(kāi)發(fā),在Ray中會(huì)將這兩個(gè)基本概念進(jìn)行分布式化,對(duì)應(yīng)到分布式系統(tǒng)就叫Task和Actor。
4、通用分布式編程API:無(wú)狀態(tài)計(jì)算單元Task
首先解釋一下Task。Task是對(duì)單機(jī)編程中的Function進(jìn)行分布式化,是一個(gè)無(wú)狀態(tài)的計(jì)算單元。
上圖是一個(gè)例子,有一個(gè)叫 heavy_compute的Function,它是一個(gè)CPU 密集型的運(yùn)算,在單機(jī)內(nèi),如果要對(duì)它進(jìn)行1萬(wàn)次運(yùn)算,比如在單核里面會(huì)有左邊的代碼,一個(gè)簡(jiǎn)單的for loop;如果需要用到多核的能力,就需要用到多線程或者多進(jìn)程。而在Ray里面,如果想利用多機(jī)的能力,要將function 進(jìn)行分布式化,整個(gè)流程非常簡(jiǎn)單,只需要三步, 如右圖所示。
- 首先給單機(jī)的function加一個(gè)decorator(@ray.remote),標(biāo)注該function是可以遠(yuǎn)程執(zhí)行的。
- 然后在調(diào)用該function 時(shí),同樣加一個(gè).remote以及需要的參數(shù),這樣該function就會(huì)被調(diào)度到遠(yuǎn)程節(jié)點(diǎn)的某個(gè)進(jìn)程中執(zhí)行。
- 最后可以通過(guò)ray.get獲取最終的運(yùn)算結(jié)果。
可以看出,整個(gè)分布式的過(guò)程非常簡(jiǎn)單,而且編程的整個(gè)框架和流程也沒(méi)有打破單機(jī)編程的習(xí)慣,這也是Ray整個(gè)核心 API 的一個(gè)核心能力,給編程者提供最大的便利性。
5、通用分布式編程API:分布式object
下面看一下Ray中object的概念。講object的原因是想讓大家理解一下為什么Ray可以把heavy_compute Function運(yùn)行到另外一個(gè)節(jié)點(diǎn)并且可以把結(jié)果拿回來(lái),這依賴于Ray底層的分布式object store。整體流程如上圖左側(cè)所示。
我們?cè)贜ode 1運(yùn)行heavy_compute function,這個(gè) function 會(huì)使用remote通過(guò)Ray底層的調(diào)度系統(tǒng)調(diào)度到Node 2, Node 2會(huì)執(zhí)行這個(gè)function,執(zhí)行完成后,把結(jié)果put到本地的object store中,object store 是Ray中的一個(gè)核心組件,最終結(jié)果返回到Caller端是通過(guò)Ray底層的 object store之間的object傳輸,把結(jié)果返回來(lái)給Caller端。
從整個(gè)的流程看, heavy_compute.remote 返回的是一個(gè)ObjectRef,并不是最終的結(jié)果。ObjectRef類似于單機(jī)編程中的future,只不過(guò)它是分布式的future,可以通過(guò)ray.get獲取最終結(jié)果。
Ray的分布式 object store是非常核心的組件,完美支撐了Ray整套分布式API 的設(shè)計(jì),其特點(diǎn)如下:
- 可以實(shí)現(xiàn)多節(jié)點(diǎn)之間object 傳輸。
- 同節(jié)點(diǎn)內(nèi)是基于shared memory的設(shè)計(jì),在此基礎(chǔ)上,分布式系統(tǒng)的online傳輸,如果發(fā)生在單機(jī)兩個(gè)進(jìn)程之間的話,理論上可以達(dá)到 Zero Copy 的效果。
- Ray object store 有一套比較完整的自動(dòng)垃圾回收機(jī)制,可以保證分布式系統(tǒng)運(yùn)算過(guò)程中一旦有ObjectRef在系統(tǒng)中沒(méi)有引用的時(shí)候,會(huì)觸發(fā)對(duì)該object 進(jìn)行GC。
- Object store有object spilling 的功能,可以自動(dòng)將內(nèi)存中的object spill到磁盤上,從而達(dá)到擴(kuò)容整個(gè)分布式系統(tǒng)存儲(chǔ)的目的。
6、通用分布式編程API:有狀態(tài)計(jì)算單元Actor
上面講解了Task 和object,接下來(lái)介紹一下Actor。Actor 也是非常簡(jiǎn)單的,是將單機(jī)編程的Class概念進(jìn)行分布式化。
左圖有一個(gè)Counter 類,是一個(gè)有狀態(tài)計(jì)算單元,將它進(jìn)行分布式化,如右圖所示。
- 首先加一個(gè)decorator,把它變成可以在遠(yuǎn)程部署的Actor;
- 通過(guò).remote,把該Actor進(jìn)行調(diào)度部署,在另外一臺(tái)機(jī)器的另外一個(gè)節(jié)點(diǎn)上面去實(shí)例化這個(gè)class;
- 調(diào)用時(shí)與Task一致,可以直接調(diào)用這個(gè)Actor的某個(gè)方法,通過(guò).remote實(shí)現(xiàn)遠(yuǎn)程調(diào)用。
三、利用Ray快速構(gòu)建分布式系統(tǒng)
以上介紹了Ray的幾個(gè)核心概念,接下來(lái)看一下剛剛講的case,怎么利用Ray來(lái)構(gòu)建這個(gè)分布式系統(tǒng)。
1、AutoML Service——部署Ray集群
Ray 是一個(gè)集群化服務(wù),有兩種部署方式。
- 第一種是一鍵云上部署,通過(guò)ray up命令,填入一些配置,在任何一個(gè)主流云廠商或者標(biāo)準(zhǔn)的Kubernetes環(huán)境或者Hadoop Yarn環(huán)境都可以進(jìn)行一鍵部署。部署完成后還可以自動(dòng)對(duì)云上資源進(jìn)行彈性調(diào)度,根據(jù)計(jì)算的runtime的情況做Autoscaling,盡可能利用云上資源完成整個(gè)分布式系統(tǒng)的計(jì)算。
- 第二種是自定義部署,如果是非標(biāo)環(huán)境,則需要自定義部署,可以使用 ray start 命令分別啟動(dòng)head節(jié)點(diǎn)和worker節(jié)點(diǎn),完成手動(dòng)的組網(wǎng)。
2、AutoML Service
有了Ray集群以后,我們回到之前AutoML的架構(gòu)圖,這里已經(jīng)加入了Ray系統(tǒng),利用Kubernetes+Ray,如何實(shí)現(xiàn)這個(gè)分布式系統(tǒng)呢?
在用戶看來(lái),實(shí)際上已經(jīng)看不到K8s這層資源了,通過(guò)剛才的介紹大家很容易想到實(shí)現(xiàn)的思路,就是利用Ray的Actor 和Task去分析一下整個(gè)系統(tǒng)哪些是有狀態(tài)計(jì)算單元,哪些是無(wú)狀態(tài)計(jì)算單元,然后對(duì)它進(jìn)行分布式化。
- proxy和 trainer 是兩個(gè)有狀態(tài)的計(jì)算單元,需要用Ray的Actor 進(jìn)行實(shí)現(xiàn)。
- worker在跑完一個(gè)任務(wù)之后就退出了,所以沒(méi)有狀態(tài),使用Ray的Task進(jìn)行實(shí)現(xiàn)。
- client由于在Ray集群之外,可以使用Ray的client 工具接入整個(gè) Ray的集群服務(wù)。
下面簡(jiǎn)要講解一下,上面架構(gòu)圖從右到左整個(gè)Service系統(tǒng)是怎么實(shí)現(xiàn)的。
3、AutoML Service—worker (Ray Task)
worker是一個(gè)Task,所以需要封裝一個(gè)function。function train_and_evaluate的主要邏輯是拿到model、訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集,完成單機(jī)的訓(xùn)練和評(píng)估,上面是已經(jīng)抽象成單機(jī)的計(jì)算。然后通過(guò)ray.remote 把它變成一個(gè)Task。
4、AutoML Service—Trainer (Ray Actor)
Trainer需要對(duì)多個(gè) worker進(jìn)行調(diào)度,通過(guò)把不同任務(wù)調(diào)到不同 worker 上面并收集結(jié)果完成單個(gè)AutoML請(qǐng)求的計(jì)算過(guò)程。
首先有一個(gè)Trainer 類封裝整個(gè)Trainer的業(yè)務(wù)邏輯。然后通過(guò)ray.remote 把它變成一個(gè)Actor,Trainer的train的方法通過(guò)兩層loop實(shí)現(xiàn)對(duì)多個(gè)worker的調(diào)度,實(shí)際上是對(duì)上面實(shí)現(xiàn)的worker的train_and_evaluate function的remote 執(zhí)行,這樣就能在分布式系統(tǒng)中實(shí)現(xiàn)并發(fā)計(jì)算,并發(fā)計(jì)算完成后,Trainer收集結(jié)果并返回給proxy。
5、AutoML Service—Proxy (Ray Actor)
proxy是對(duì)外服務(wù)的入口,定義兩個(gè)function:
- 一個(gè)是do_auto_ml,用戶的請(qǐng)求調(diào)度到該function上會(huì)觸發(fā)部署一個(gè)Trainer的Actor,然后再調(diào)用該Actor 的 train方法來(lái)觸發(fā)整個(gè)AutoML的計(jì)算。
- 另一個(gè)是 get_result,可以方便客戶端Ray Client查詢計(jì)算結(jié)果。
這里值得注意的是,在部署 proxy 的時(shí)候需要設(shè)置一個(gè)name,用于服務(wù)發(fā)現(xiàn)。
6、AutoML Service—Client
用Ray Client 接入的時(shí)候,任何一個(gè)AutoML用戶拿到Client,先通過(guò)ray.get_actor傳入上面的actor name就可以獲得proxy的句柄,然后可以通過(guò)proxy的句柄調(diào)用proxy方法從而實(shí)現(xiàn)AutoML的接入,最后通過(guò)調(diào)用proxy 的get_result拿到最終的運(yùn)算結(jié)果。
7、AutoML Service—定制資源
接下來(lái)介紹一些細(xì)節(jié)。
第一個(gè)細(xì)節(jié)是資源定制。
在純?cè)圃膶?shí)現(xiàn)思路中,如果沒(méi)有Ray,資源定制是寫到 yaml 里邊的。比如說(shuō)訓(xùn)練需要多少GPU 或者計(jì)算節(jié)點(diǎn)需要多少CPU,都是在 yaml 中定制 container 的規(guī)格。
Ray提供了另外一個(gè)選擇,完全無(wú)感知的代碼化的配置,用戶可以在 runtime 的時(shí)候,或者在Ray的Task 或 Actor 的decorator 中加一個(gè)參數(shù),就可以通過(guò)Ray系統(tǒng)的調(diào)度能力分配相應(yīng)的資源,達(dá)到整個(gè)分布式系統(tǒng)資源定制的目的。
Ray的資源定制除了支持GPU、CPU、Memory 之外,還可以插入自定義資源。然后Ray的調(diào)度還有一些高級(jí)功能,比如資源組,或者親和性和反親和性的調(diào)度,目前都是支持的。
8、AutoML Service—運(yùn)行時(shí)環(huán)境
第二個(gè)細(xì)節(jié)是運(yùn)行時(shí)環(huán)境。
在分布式系統(tǒng)中,往往不同分布式系統(tǒng)的組件對(duì)環(huán)境的要求是不一樣的。如果使用常規(guī)思路,就需要把環(huán)境固化到image里面,通過(guò) Dockerfile 去定制環(huán)境。
Ray實(shí)現(xiàn)了更靈活的選擇,也是代碼化的,可以在runtime創(chuàng)建Task或Actor之前的任意時(shí)刻定制指定計(jì)算單元的運(yùn)行時(shí)環(huán)境。上圖中給worker 的 Task 設(shè)定一個(gè)runtime_env,定制一個(gè)專屬的Python版本,并在該版本里面裝入一些pip包,完成面向Python的隔離環(huán)境的定制。這時(shí)Ray集群內(nèi)部會(huì)在創(chuàng)建這個(gè)Task之前去準(zhǔn)備該環(huán)境,然后將該Task調(diào)度到該環(huán)境執(zhí)行。
Ray的運(yùn)行時(shí)環(huán)境是插件化的設(shè)計(jì),用戶可以根據(jù)自己的需求實(shí)現(xiàn)不同的插件,在Ray中原生支持了一些插件如Pip、Conda、Container等,只要是跟環(huán)境相關(guān),不只是代碼依賴,也可以是數(shù)據(jù)依賴,都可以通過(guò)插件去實(shí)現(xiàn)。
右下圖從運(yùn)行時(shí)環(huán)境這個(gè)角度看,以Python為例,隔離性的支持力度有如下幾個(gè)維度,一個(gè)是 Process 級(jí)別的隔離,第二是 Virtual env 級(jí)別的隔離,第三是 Conda 級(jí)別的隔離,最后是 Container級(jí)別隔離。
從隔離性來(lái)說(shuō),從右到左是由弱到強(qiáng)的,Process 的隔離性是非常弱的,Container 隔離性是更強(qiáng)的。
從用戶體驗(yàn)來(lái)說(shuō),環(huán)境定制上 Container 是更重的而Process 是更輕的。
所以在Ray中用戶可以根據(jù)自己的環(huán)境定制的需求選擇需要定制的環(huán)境的粒度。有些人需要完全的Container 級(jí)別的隔離,有些人Process 級(jí)別的隔離就足夠了,可以根據(jù)自己的需求進(jìn)行選擇。
9、AutoML Service—運(yùn)維與監(jiān)控
第三個(gè)細(xì)節(jié)是運(yùn)維與監(jiān)控。
Ray提供了 Ray Dashboard。Ray dashboard實(shí)現(xiàn)了整個(gè)Ray集群包括Ray Nodes、Ray Actors等各種維度信息的透出;另外,還有集群內(nèi)的Logs和events,比如某個(gè)Actor的某個(gè)方法執(zhí)行異常,Ray會(huì)把堆棧通過(guò) event收集到dashboard中,方便迅速定位問(wèn)題;除此之外還有profiling 工具,Ray dashboard 可以支火焰圖,還可以一鍵看到任意一個(gè)Actor或Task的進(jìn)程狀態(tài)或者堆棧。
除了Ray dashboard,Ray還提供了黑屏化的Ray State Client,同樣可以通過(guò) Ray State Client 去 query 整個(gè)集群的狀態(tài)。
在監(jiān)控方面Ray集成了Metrics的框架,用戶可以直接調(diào)用Ray的metrics 的接口寫入metric,然后在Ray dashboard中通過(guò)iframe的形式嵌入了Grafana來(lái)做一些簡(jiǎn)單的監(jiān)控。
10、Ray的架構(gòu)
下面介紹一下Ray的架構(gòu)。Ray在架構(gòu)上與很多大數(shù)據(jù)系統(tǒng)類似,有一個(gè)主節(jié)點(diǎn)head節(jié)點(diǎn),其他是 worker 節(jié)點(diǎn)。
在主節(jié)點(diǎn)里有GCS角色(Global Control Service),GCS主要負(fù)責(zé)整個(gè)集群的資源調(diào)度和節(jié)點(diǎn)管理,類似于Hadoop架構(gòu)中Yarn里邊的 Resource Manager。
Ray的worker節(jié)點(diǎn)主要有Raylet角色。除了做單機(jī)的進(jìn)程管理和調(diào)度之外,比較關(guān)鍵的還有剛剛講過(guò)的分布式的object store,是集成到Raylet進(jìn)程里面的。
11、小結(jié)
上圖是我們做的一個(gè)實(shí)驗(yàn),除了Ray+云原生的實(shí)現(xiàn)方式,我們也寫了一套代碼以云原生的方式來(lái)實(shí)現(xiàn)相同邏輯。代碼已經(jīng)放在上圖下方GitHub 的repo上面,大家有興趣可以查閱。
這里介紹一下實(shí)驗(yàn)評(píng)估結(jié)果:
- 從研發(fā)效率看,基于純?cè)圃绞脚cRay+云原生相比從15 人天降到了2人天;
- 從代碼來(lái)看,云原生方式需要寫5種編程語(yǔ)言代碼,并且隨著業(yè)務(wù)復(fù)雜性的增大,代碼量會(huì)越來(lái)越大;通過(guò)Ray,用260行純Python代碼就實(shí)現(xiàn)了這個(gè)case,可以證明利用Ray開(kāi)發(fā)分布式系統(tǒng)是非??焖偾腋咝У?。
- 從系統(tǒng)特點(diǎn)看,Ray是單語(yǔ)言即可實(shí)現(xiàn),只有一個(gè)main函數(shù),整個(gè)的編程相當(dāng)于應(yīng)用中心化編程的思想,整個(gè)分布式系統(tǒng)只有一個(gè)入口,其他角色的實(shí)現(xiàn)都是通過(guò)Ray Actor和Task,應(yīng)用、運(yùn)維部署、配置融為一體;云原生方式則需要多語(yǔ)言實(shí)現(xiàn),多編程入口,應(yīng)用、運(yùn)維部署、配置解耦。
四、Ray開(kāi)源生態(tài)
最后來(lái)介紹一下Ray的開(kāi)源現(xiàn)狀。
1、活躍度
從Ray的活躍度來(lái)看,Ray從 2016 年開(kāi)源至今,活躍度持續(xù)穩(wěn)定增長(zhǎng)。目前社區(qū)有超過(guò) 800個(gè)Contributor,Star數(shù)超過(guò)26K。
2、Ray中文社區(qū)
Ray在中國(guó)有由螞蟻長(zhǎng)期維護(hù)的中文社區(qū)。
- 中文社區(qū)的公眾號(hào),可以掃描上圖的二維碼,公眾號(hào)會(huì)經(jīng)常發(fā)表中文社區(qū)同學(xué)寫的基礎(chǔ)文章或者活動(dòng)。
- 中文社區(qū)的論壇中有一些問(wèn)答和技術(shù)分享。
- 中文社區(qū)的交流群可以方便大家圍繞Ray系統(tǒng)上的應(yīng)用進(jìn)行溝通。
3、Ray forward 2023
Ray forward已經(jīng)在國(guó)內(nèi)舉辦了五屆,2023年7月2日螞蟻剛剛舉辦了最新一屆的Ray forward。從五屆Ray forward可以感受到一個(gè)趨勢(shì),在最開(kāi)始的兩年,大部分的talk都是螞蟻和加州大學(xué)伯克利分校RISELab實(shí)驗(yàn)室的人員分享,而近兩年已經(jīng)有越來(lái)越多的國(guó)內(nèi)公司來(lái)Ray forward分享他們自己的議題,今年是議題最多的一次。
4、Ray 2.0—Ray AIR
剛剛講的從Ray的概念還有整個(gè)case來(lái)看,實(shí)際上是Ray底層Core的核心能力。
Ray的生態(tài)花費(fèi)了非常大的精力在 AI 領(lǐng)域,上圖是Ray 2.0的核心概念,叫Ray AIR(Ray AI Runtime)。Ray AIR的設(shè)計(jì)思想是在AI pipeline 的各個(gè)處理流程中去集成各種各樣主流的工具,比如數(shù)據(jù)處理、訓(xùn)練、Tune、Serve等。
如果利用 Ray去構(gòu)建一個(gè)AI的pipeline,在數(shù)據(jù)處理方面可以選擇Spark,也可以選用Mars或Dask等Python的科學(xué)計(jì)算工具,也可以選擇Ray原生的Ray Dataset;在訓(xùn)練方面,可以根據(jù)業(yè)務(wù)需求選擇PyTorch, TensorFlow 等訓(xùn)練框架。
Ray AIR定位是一個(gè)可擴(kuò)展的統(tǒng)一的機(jī)器學(xué)習(xí)工具集,最終可以幫助用戶實(shí)現(xiàn)一個(gè)腳本就能夠?qū)⒄麄€(gè)AI的pipeline構(gòu)建起來(lái),這是一種融合計(jì)算的思路。
在沒(méi)有Ray之前,整個(gè)pipeline會(huì)有多個(gè)系統(tǒng)串聯(lián)起來(lái),有了Ray之后,底層會(huì)有統(tǒng)一的 runtime 來(lái)完成編排和調(diào)度。
5、生態(tài)系統(tǒng)
上圖是一張比較老的圖,主要是為了展示Ray的生態(tài)。主要分為兩部分library,一個(gè)是Ray的Native Libraries,一個(gè)是Third Party Libraries。
- Native Libraries中包括Ray的強(qiáng)化學(xué)習(xí)庫(kù)RLlib,還有應(yīng)用比較廣泛的Ray Serve等。
- Third Party Libraries中包括剛剛提到的在 AI 處理流程中比較常用的引擎。
6、企業(yè)應(yīng)用
Ray的企業(yè)應(yīng)用是比較廣泛的。
除了剛剛講到的大語(yǔ)言模型場(chǎng)景下的OpenAI之外,上圖列出了已經(jīng)集成Ray很長(zhǎng)時(shí)間的一些企業(yè)。整體來(lái)看,目前國(guó)外的發(fā)展更多一點(diǎn),國(guó)內(nèi)相對(duì)少一點(diǎn),國(guó)外的很多大廠,包括一些傳統(tǒng)企業(yè),都在利用Ray來(lái)構(gòu)建他們底層的分布式系統(tǒng)。
7、大模型訓(xùn)練
上圖匯總了目前做大模型訓(xùn)練的一些已經(jīng)集成了Ray的開(kāi)源框架。
- 第一個(gè)項(xiàng)目是Alpa,是 Google 和UC Berkeley大學(xué)共同研發(fā)的面向大模型并行訓(xùn)練和服務(wù)的框架,框架利用Ray Core進(jìn)行GPU的管理與運(yùn)行時(shí)編排。
- 第二個(gè)項(xiàng)目是Colossal-AI,也是比較火的分布式訓(xùn)練框架,框架將Ray Core的 能力集成到RLHF流程中,就是基于人類反饋的強(qiáng)化學(xué)習(xí)中。
- 第三個(gè)項(xiàng)目是trlX,目前也集成了Ray的能力,將Ray Train和Ray Tune集成到RLHF流程中。
8、大模型訓(xùn)練——Alpa on Ray
上圖是Alpa的詳細(xì)架構(gòu),可以看到Ray主要在中間層。Alpha 項(xiàng)目可以自動(dòng)做到層間和層內(nèi)兩個(gè)角度并行化,從整個(gè)創(chuàng)新角度看是比較領(lǐng)先的分布訓(xùn)練框架。
9、其他開(kāi)源項(xiàng)目集成
上圖是除了 AI 以外的集成了Ray的能力的開(kāi)源框架。
- 第一個(gè)項(xiàng)目是GeaFlow也就是TuGraph,TuGraph的流圖計(jì)算引擎底層集成了Ray Core進(jìn)行動(dòng)態(tài)資源調(diào)度。
- 第二個(gè)項(xiàng)目是隱語(yǔ),是螞蟻開(kāi)源的一個(gè)隱私計(jì)算框架,隱語(yǔ)深度應(yīng)用了Ray Core的能力,目前還用到了在Ray project 中的另外一個(gè)項(xiàng)目Ray Fed。Ray Fed可以完成在隱私計(jì)算領(lǐng)域多個(gè) party不同的集群之間的數(shù)據(jù)傳輸與調(diào)度。
- 第三個(gè)項(xiàng)目是Mars,Mars是阿里開(kāi)源的一個(gè)科學(xué)計(jì)算框架,可以實(shí)現(xiàn)分布式pandas、分布式scikit-learn的能力。在這個(gè)項(xiàng)目中不僅集成了Ray Core的能力,還深度集成了Ray Object Store的能力,從性能上看有不錯(cuò)的結(jié)果。
五、答疑
A1:Ray 目前使用場(chǎng)景有哪些?支持實(shí)時(shí)流計(jì)算場(chǎng)景嗎?
Q:Ray的使用場(chǎng)景其實(shí)還是蠻多的,尤其在螞蟻內(nèi)部基于Ray構(gòu)建了很多框架,比如剛剛提到的GeaFlow,一個(gè)流圖計(jì)算框架,是一個(gè)比較大的方向;另外剛剛提到的隱私計(jì)算是另外一個(gè)方向。那除此之外,螞蟻內(nèi)部還有類似于函數(shù)計(jì)算系統(tǒng),函數(shù)計(jì)算也可以是基于Ray來(lái)構(gòu)建,從Ray的API可以看出Ray做函數(shù)計(jì)算還是非常方便的;除此之外還有科學(xué)計(jì)算、在線機(jī)器學(xué)習(xí),最近我們也在探索搜推引擎能不能基于Ray來(lái)構(gòu)建。
AI 方面在螞蟻內(nèi)部用得比較多的是AI在線服務(wù),怎么應(yīng)用一個(gè)或多個(gè)模型或大模型提供推理服務(wù),整個(gè)從外圍的資源編排調(diào)度,包括failover都可以利用Ray Serve做在線服務(wù)的支持。Ray在AI 方面的應(yīng)用比較廣泛,包括剛剛提到的Ray AIR涉及到的從 AI 的數(shù)據(jù)處理、到訓(xùn)練、到Ray Tune,以及最近的大模型場(chǎng)景,都是利用Ray來(lái)完成底層分布式底盤的。
A2:Ray支持存算分離么?Node 2 計(jì)算完成,把結(jié)果存到本地的 object store等待其他節(jié)點(diǎn)獲取結(jié)果,它要等到結(jié)果被獲取完才能釋放嗎?
Q:是的,如果是基于Ray原生的object store,需要把結(jié)果 put 到Node 2 里面,在 put 完之后,Node 2 異常退出了,那數(shù)據(jù)就可能丟了。當(dāng)然用戶可以通過(guò)Ray原生的血緣的能力,或者用戶自己實(shí)現(xiàn)的failover能力去進(jìn)行恢復(fù),如果需要保證不丟數(shù)據(jù),則需要實(shí)現(xiàn)高可用,對(duì)object store做一個(gè)擴(kuò)展。目前來(lái)說(shuō),Ray object store的面向場(chǎng)景是做計(jì)算引擎中間結(jié)果的存儲(chǔ),它并不需要做持久化存儲(chǔ)。
A3:是否在大部分場(chǎng)景下Ray都可以直接替代 Spark 使用,還是說(shuō)兩者互不沖突?
Q:我覺(jué)得是互不沖突,從Ray的設(shè)計(jì)思想來(lái)看,不對(duì)標(biāo)任何一個(gè)大數(shù)據(jù)系統(tǒng)。剛剛也提到在Ray的整個(gè)生態(tài)中,用戶也可以把 Spark跑在Ray上面,甚至還有一些項(xiàng)目在做Ray on Spark。在數(shù)據(jù)處理領(lǐng)域Spark有它的非常核心的能力。如果想在訓(xùn)練之前做簡(jiǎn)單的數(shù)據(jù)預(yù)處理,不需要牽扯到復(fù)雜的算子,這種情況下可以直接用Ray,但是如果計(jì)算場(chǎng)景比較復(fù)雜,比較偏向于大數(shù)據(jù)處理,并且用到比較復(fù)雜的shuffle邏輯或者比較復(fù)雜的算子,還是可以利用 Spark進(jìn)行處理,然后再對(duì)接 Ray生態(tài),用戶根據(jù)自己的計(jì)算場(chǎng)景來(lái)進(jìn)行技術(shù)選型。
A4:同為 Actor model,可以對(duì)比一下Ray和Akka分布式計(jì)算框架嗎?
Q:我對(duì)Akka的API是什么樣子印象不是很深了,我理解它應(yīng)該跟Ray的API 還是有很大區(qū)別的。Ray主要是從編程的角度出發(fā),有Task和Actor,Ray中的Actor model 跟傳統(tǒng)的Actor model 的概念還是有一點(diǎn)區(qū)別的。Ray的Actor更偏分布式,目前沒(méi)有面向單機(jī)線程間交互的場(chǎng)景。
A5:Ray是如何容錯(cuò)的?
Q:我們認(rèn)為容錯(cuò)有兩個(gè)維度。
第一個(gè)維度是粗粒度進(jìn)程級(jí)別的容錯(cuò),因?yàn)镽ay交付的是一個(gè)進(jìn)程,無(wú)論是Task還是Actor。Task/Actor的failover,首先是Task/Actor所在進(jìn)程的一個(gè)探活,識(shí)別其是否異常退出,這是分布式系統(tǒng)基礎(chǔ)能力,在Ray里面是完全由Ray來(lái)實(shí)現(xiàn)的;其次是進(jìn)程維度的failover,當(dāng)Task尤其是Actor異常退出后,把異常退出實(shí)體重新調(diào)度起來(lái)進(jìn)行新的實(shí)例化,也是由Ray負(fù)責(zé)的。也就是說(shuō),粗粒度的恢復(fù)是可以完全由Ray來(lái)負(fù)責(zé)。
第二個(gè)維度是細(xì)粒度,主要是進(jìn)程的狀態(tài),因?yàn)镽ay實(shí)際上是不去規(guī)定內(nèi)部計(jì)算邏輯的,內(nèi)部可以跑流批計(jì)算、可以跑AI 訓(xùn)練、可以跑任何的一個(gè)function ,Ray并不知道內(nèi)部代碼在做什么事情,所以狀態(tài)恢復(fù)方面,目前Ray主流的用法還是把狀態(tài)恢復(fù)交給用戶的業(yè)務(wù)代碼自己負(fù)責(zé)。當(dāng)然用戶可以通過(guò)Ray接口知道當(dāng)前是否被重啟了,已經(jīng)重啟了多少次?用戶之前狀態(tài)需要自己做一些checkpoint,可以存到存儲(chǔ)里面或者存到 Ray底層的object store 里面。
用戶可以根據(jù)不同的failover的可靠性要求做具體方案。但總體來(lái)說(shuō),一般的用法是粗粒度的failover由Ray托管,細(xì)粒度的狀態(tài)恢復(fù)是由Ray上的應(yīng)用自己來(lái)做恢復(fù)。