在早期階段,vivo AI 計(jì)算平臺(tái)使用 GlusterFS 作為底層存儲(chǔ)基座夯尽。隨著數(shù)據(jù)規(guī)模的擴(kuò)大和多種業(yè)務(wù)場(chǎng)景的接入袱箱,開始出現(xiàn)性能、維護(hù)等問題式曲。為此妨托,vivo 轉(zhuǎn)而采用了自研的軒轅文件系統(tǒng),該系統(tǒng)是基于 JuiceFS 開源版本開發(fā)的一款分布式文件存儲(chǔ)方案吝羞。
本文將介紹 vivo 軒轅文件系統(tǒng)在 JuiceFS 基礎(chǔ)之上開發(fā)的新特性兰伤。以及 vivo 針對(duì)一些關(guān)鍵場(chǎng)景,如樣本數(shù)據(jù)讀取速度慢和檢查點(diǎn)寫入環(huán)節(jié)的優(yōu)化措施钧排。此外敦腔,文章還將介紹 vivo 的技術(shù)規(guī)劃包括 FUSE、 元數(shù)據(jù)引擎及 RDMA 通信等方面恨溜,希望能為在大規(guī)模 AI 場(chǎng)景使用 JuiceFS 的用戶提供參考與啟發(fā)符衔。01 計(jì)算平臺(tái)引入軒轅文件存儲(chǔ)的背景
01 計(jì)算平臺(tái)引入軒轅文件存儲(chǔ)的背景
最初,vivo 的 AI 計(jì)算平臺(tái) 使用 GlusterFS 糟袁,并由該團(tuán)隊(duì)自行維護(hù)判族。在使用過程中,團(tuán)隊(duì)遇到了一些問題项戴。一是處理小文件時(shí)速度變得非常緩慢形帮;二是當(dāng)需要對(duì) GlusterFS 進(jìn)行機(jī)器擴(kuò)容和數(shù)據(jù)平衡時(shí),對(duì)業(yè)務(wù)產(chǎn)生了較大的影響。
隨后沃缘,由于早期集群容量已滿且未進(jìn)行擴(kuò)容躯枢,計(jì)算團(tuán)隊(duì)選擇搭建了新的集群则吟。然而槐臀,這導(dǎo)致了多個(gè)集群需要維護(hù),從而增加了管理的復(fù)雜度氓仲。此外水慨,作為平臺(tái)方,他們?cè)诖鎯?chǔ)方面的投入人力有限敬扛,因此難以進(jìn)行新特性開發(fā)晰洒。
他們了解到我們互聯(lián)網(wǎng)部門正在研發(fā)文件存儲(chǔ)解決方案,經(jīng)過深入交流和測(cè)試啥箭。最終谍珊,他們決定將其數(shù)據(jù)存儲(chǔ)遷移至我們的軒轅文件存儲(chǔ)系統(tǒng)。
軒轅文件系統(tǒng)基于 JuiceFS 開源版急侥,進(jìn)行了二次開發(fā)砌滞,支持多種標(biāo)準(zhǔn)訪問協(xié)議,包括 POSIX坏怪、HDFS 以及 Windows 上的 CIFS 協(xié)議贝润。此外,我們還提供了文件恢復(fù)功能铝宵,該功能參考了商用解決方案打掘,能夠按照原路徑進(jìn)行數(shù)據(jù)恢復(fù)。
同時(shí)鹏秋,我們的系統(tǒng)支持客戶端熱升級(jí)尊蚁,這一功能在開源版本中也已經(jīng)實(shí)現(xiàn)。另外侣夷,我們還支持用戶名權(quán)限管理枝誊,默認(rèn)使用本地 uid/gid 進(jìn)行鑒權(quán)。在此基礎(chǔ)上惜纸,我們還參考 JuiceFS 企業(yè)版實(shí)現(xiàn)了用戶名鑒權(quán)功能叶撒。
下圖是軒轅文件系統(tǒng)的架構(gòu)圖,與 JuiceFS 類似耐版。在底層基座方面祠够,我們使用 TikV 存儲(chǔ)元數(shù)據(jù),而數(shù)據(jù)則存儲(chǔ)在我們自研的對(duì)象存儲(chǔ)系統(tǒng)中粪牲。特別值得一提的是古瓤,在 Windows 場(chǎng)景下,我們?cè)?Samba 中開發(fā)了一個(gè)插件,該插件直接調(diào)用 JuiceFS API落君,從而為用戶提供了一個(gè)在 Windows 上訪問我們文件存儲(chǔ)的通道穿香。
目前的 AI 計(jì)算平臺(tái)存儲(chǔ)流程如下:首先獲取原始數(shù)據(jù)并通過一個(gè)包含 4 萬個(gè)批處理任務(wù)的系統(tǒng)進(jìn)行處理,生成樣本庫绎速。這些樣本庫隨后在 GPU 上訓(xùn)練皮获,產(chǎn)生模型文件,這些模型文件被傳輸至在線系統(tǒng)用于推理纹冤。原始數(shù)據(jù)及處理后的樣本庫直接存儲(chǔ)在軒轅文件系統(tǒng)中洒宝,由于其兼容 HDFS API,Spark 可以直接處理這些數(shù)據(jù)萌京。模型文件也保存在軒轅中雁歌,并通過其提供的CSI插件,使在線推理系統(tǒng)能直接掛載并讀取這些文件知残。
02 存儲(chǔ)性能優(yōu)化
訓(xùn)練階段涉及存儲(chǔ)的主要有兩個(gè)重要方面:樣本讀和訓(xùn)練過程中的檢查點(diǎn)( checkpoint) 保存靠瞎。
環(huán)節(jié)1:加速樣本讀
為了提升樣本加載的速度,我們開發(fā)了一個(gè)分布式讀緩存層求妹。在訓(xùn)練模型前乏盐,我們借助JuiceFS 提供的 warm up 功能,優(yōu)先將本次訓(xùn)練所需的數(shù)據(jù)預(yù)加載至讀緩存層扒最。通過這種方式丑勤,訓(xùn)練數(shù)據(jù)可以直接從讀緩存層獲取,而無需從對(duì)象存儲(chǔ)系統(tǒng)中拉取吧趣。通常情況下法竞,直接從對(duì)象存儲(chǔ)中讀取數(shù)據(jù)需要花費(fèi)十幾至幾十毫秒,但通過讀緩存層則可將讀取時(shí)間縮短至 10 毫秒以內(nèi)强挫,從而進(jìn)顯著提高了數(shù)據(jù)加載到 GPU的 速度岔霸。
環(huán)節(jié)2:檢查點(diǎn) (Checkpoint) 寫入
在檢查點(diǎn)寫入方面,我們參考了百度的方案俯渤。具體而言呆细,檢查點(diǎn)數(shù)據(jù)首先被寫入一個(gè)臨時(shí)緩存區(qū)域(我們稱之為“協(xié)管”區(qū)域,但此處可能指的是某種形式的中間緩存或暫存區(qū))八匠,然后再逐步刷新到對(duì)象存儲(chǔ)中絮爷。在這個(gè)過程中,我們也采用了單副本模式梨树,因?yàn)闄z查點(diǎn)本身就是每隔一段時(shí)間保存的坑夯,即使某個(gè)時(shí)間段的檢查點(diǎn)丟失,對(duì)整體訓(xùn)練的影響也是有限的抡四。當(dāng)然柜蜈,我們也制定了一些策略來確保關(guān)鍵數(shù)據(jù)的安全性仗谆,并非所有數(shù)據(jù)都會(huì)進(jìn)入這個(gè)中間緩存區(qū)域。通常淑履,只有檢查點(diǎn)文件和訓(xùn)練階段的日志文件會(huì)被寫入隶垮。如果訓(xùn)練中斷,檢查點(diǎn)文件可以從這個(gè)中間緩存區(qū)域中讀取秘噪。
此外狸吞,當(dāng)數(shù)據(jù)被寫入并刷新到對(duì)象存儲(chǔ)中時(shí),我們并不會(huì)立即從檢查點(diǎn)緩存中清除這些數(shù)據(jù)缆娃。因?yàn)橛?xùn)練過程中隨時(shí)可能中斷捷绒,如果此時(shí)檢查點(diǎn)緩存中的數(shù)據(jù)被清除瑰排,而需要從對(duì)象存儲(chǔ)中重新拉取贯要,將會(huì)耗費(fèi)較長(zhǎng)時(shí)間。因此椭住,我們?cè)O(shè)置了一個(gè) TTL(生存時(shí)間)機(jī)制崇渗。例如,如果檢查點(diǎn)數(shù)據(jù)每小時(shí)刷新一次到對(duì)象存儲(chǔ)中京郑,我們可以將 TTL 設(shè)置為 1.5 小時(shí)宅广。這樣,即使訓(xùn)練中斷些举,我們也能確保檢查點(diǎn)緩存中有一個(gè)最新的備份可供使用跟狱。
在開發(fā)寫緩存的過程中,我們遇到了一個(gè)挑戰(zhàn)户魏。由于我們的客戶端與寫緩存之間的通信采用 gRPC 協(xié)議驶臊,該協(xié)議在數(shù)據(jù)反序列化時(shí)會(huì)重新申請(qǐng)內(nèi)存以存儲(chǔ)解析后的數(shù)據(jù)。在特定時(shí)間段內(nèi)叼丑,如果寫操作非常集中(例如在幾十秒內(nèi))关翎,會(huì)導(dǎo)致大量的內(nèi)存申請(qǐng)和釋放。由于我們使用的是 Go 語言開發(fā)鸠信,其垃圾回收(GC)機(jī)制在這種情況下表現(xiàn)較慢纵寝,可能會(huì)導(dǎo)致寫緩存的內(nèi)存耗盡。
為了解決這個(gè)問題星立,我們調(diào)研了其他數(shù)據(jù)反序列化的方案爽茴。最終,我們采用了 Facebook 的 flatterbuffer 方案绰垂。與 gRPC 的 Pb 反序列化不同室奏,flatterbuffer 在反序列化后可以直接使用數(shù)據(jù),無需額外的解析步驟辕坝。通過這種方式窍奋,我們減少了內(nèi)存的使用,與 Pb 相比,內(nèi)存節(jié)省達(dá)到了 50%琳袄。同時(shí)江场,我們也對(duì)寫性能進(jìn)行了測(cè)試,發(fā)現(xiàn)使用 flatterbuffer 后窖逗,寫性能提升了20%
環(huán)節(jié)3:在線推理址否,模型加載流量大
在用戶進(jìn)行在線推理時(shí),我們注意到模型下載產(chǎn)生的流量極大碎紊,有時(shí)甚至?xí)紳M對(duì)象存儲(chǔ)網(wǎng)關(guān)的帶寬佑附。深入分析這個(gè)場(chǎng)景后,我們發(fā)現(xiàn)存在眾多實(shí)例仗考,每個(gè)實(shí)例都會(huì)獨(dú)立地將完整模型加載到內(nèi)存中音同,并且這些實(shí)例幾乎是同時(shí)開始加載模型的,這一行為造成了巨大的流量壓力秃嗜。
為解決此問題权均,我們借鑒了商業(yè)解決方案,采用了在 Pod 中實(shí)施邏輯分組的方法锅锨。在這種策略下叽赊,每個(gè)分組僅從底層存儲(chǔ)讀取一份完整模型,而分組內(nèi)的各個(gè)節(jié)點(diǎn)則讀取模型的部分文件必搞,并通過節(jié)點(diǎn)間的數(shù)據(jù)共享(類似于 P2P 方式)來減少總體流量需求必指。這種方法顯著降低了對(duì)底層對(duì)象存儲(chǔ)帶寬的占用,有效緩解了流量壓力恕洲。
03 技術(shù)規(guī)劃
libc 調(diào)用繞過 FUSE 內(nèi)核塔橡,提升讀寫性能 下面這份圖表來源于 ACM 期刊中的一篇論文。文中指出研侣,在使用 FUSE 掛載時(shí)谱邪,請(qǐng)求的處理流程會(huì)先從用戶態(tài)轉(zhuǎn)移到內(nèi)核態(tài),然后再返回用戶態(tài)庶诡。在這個(gè)流程中惦银,上下文切換所帶來的消耗是相當(dāng)巨大的。
柱狀圖較高的部分代表原生的 FUSE末誓,而柱狀圖較低的部分則代表經(jīng)過優(yōu)化的方案扯俱。
- 小文件場(chǎng)景:原生的 FUSE 相較于優(yōu)化方案,其上下文次數(shù)切換的數(shù)量差距達(dá)到了 1000 倍喇澡;
- 大文件場(chǎng)景:原生的 FUSE 與優(yōu)化方案之間的上下文次數(shù)切換的數(shù)量差距約為 100 倍迅栅;
- 混合負(fù)載場(chǎng)景:同樣顯示出了巨大的上下文次數(shù)切換的數(shù)量差異。
在論文中提到晴玖,鏈路消耗的主要來源是上下文切換读存。因此为流,我們計(jì)劃在 FUSE 這一層進(jìn)行優(yōu)化,主要針對(duì)元數(shù)據(jù)和小文件場(chǎng)景让簿。目前敬察,我們正在進(jìn)行方案選型工作。
自研元數(shù)據(jù)引擎尔当,文件語義下沉
我們還計(jì)劃開發(fā)一個(gè)自己的元數(shù)據(jù)引擎莲祸。當(dāng)前,我們使用的元數(shù)據(jù)引擎是基于 TiKV 的椭迎,但 TiKV 并不具備文件語義锐帜,所有的文件語義都是在客戶端實(shí)現(xiàn)的。這給我們的特性開發(fā)工作帶來了極大的不便畜号。
同時(shí)缴阎,當(dāng)多個(gè)節(jié)點(diǎn)同時(shí)寫入一個(gè) key 時(shí),事務(wù)沖突也會(huì)非常頻繁弄兜。近期药蜻,我們還遇到了進(jìn)程會(huì)突然卡住的問題瓷式,持續(xù)時(shí)間從幾分鐘到十幾分鐘不等替饿。這個(gè)問題一直未能得到解決。
另外贸典,TiKV PD 組件為主節(jié)點(diǎn) Active 模式视卢,請(qǐng)求上 10 萬后,時(shí)延上升明顯廊驼,PD 節(jié)點(diǎn)(112核)CPU 使用率接近飽和据过。因此,我們正在嘗試一些方案來降低主節(jié)點(diǎn)的 CPU 利用率妒挎,以觀察是否能改善耗時(shí)問題绳锅。我們參考了一些論文,如百度的 CFS 論文酝掩,將所有的元數(shù)據(jù)操作盡量變成單機(jī)事務(wù)鳞芙,以減少分布式事務(wù)的開銷。
緩存層實(shí)現(xiàn) RDMA
通信關(guān)于我們機(jī)房的 GPU 節(jié)點(diǎn)期虾,它們目前使用的是 RDMA 網(wǎng)絡(luò)原朝。與緩存層的通信仍然使用 TCP 協(xié)議。我們有規(guī)劃開發(fā)一個(gè)基于 RDMA 的通信方式镶苞,以實(shí)現(xiàn)客戶端與緩存之間的低延遲喳坠、低 CPU 消耗的通信。
通過觀察客戶端的火焰圖茂蚓,我們發(fā)現(xiàn) RPC 通信的耗時(shí)仍然非常明顯壕鹉。雖然寫緩存的處理數(shù)據(jù)只需要一兩毫秒剃幌,但客戶端將數(shù)據(jù)上傳到整個(gè)鏈路的耗時(shí)可能達(dá)到五六毫秒,甚至十毫秒晾浴。在客戶端 CPU 非常繁忙的情況下锥忿,這個(gè)時(shí)間可能會(huì)達(dá)到二三十毫秒。而 RDMA 本身并不怎么消耗 CPU怠肋,內(nèi)存消耗也比較少敬鬓,因此我們認(rèn)為這是一個(gè)值得嘗試的解決方案。