隨著大模型流行,GPU 算力資源正變得日益稀缺,傳統的“算力跟著儲存跑”的策略需要轉變為“儲存跟著算力跑”。為了確保資料一致性和管理的便捷性,企業通常在特定地區的公有云上選擇物件儲存作為所有模型資料的集中儲存點。當進行計算任務排程時,往往需要人工介入,手動進行資料複製和遷移方法不僅成本高昂,還存在管理和維護的複雜性,包括許可權控制等問題都極為棘手。
JuiceFS 企業版的 “映象檔案系統” 功能允許使用者從一個地區自動複製後設資料到多個地區,形成一對多的複製模式。在多雲架構下,該功能在確保資料一致性的同時,大幅降低人工運維的工作量。
最新的 JuiceFS 企業版 5.1 中, 映象檔案系統除了支援讀取,還新增了可直接寫入的功能。本文將探討映象檔案系統的讀寫實現原理。
01 為什麼需要映象檔案系統
讓我們設想這樣一個場景,某使用者的檔案系統部署在北京,但北京地區的 GPU 資源供給不足,而該使用者在上海還有可用的 GPU 資源。這時使用者想在上海執行模型訓練任務,有兩個簡單的方案:
- 直接在上海掛載北京的檔案系統。理論上來說,只要北京與上海之間的網路連線順暢,上海的客戶端確實就能訪問資料以進行訓練。然而實際情況是,檔案系統的訪問通常涉及到頻繁的後設資料操作,而由於兩地的網路延遲較大,效能結果往往都無法達到預期。
- 在上海建立新的檔案系統,在訓練前複製所需資料集到上海。這樣做的優點是可以保證上海訓練任務的效能。但缺點也是很明顯的,一方面構建新檔案系統需要較高的硬體成本,另一方面每次訓練前同步資料也提高了運維的複雜性。
綜上所述,這兩個簡單的方案都無法令人滿意。為此 JuiceFS 企業版提供了映象檔案系統功能。它允許使用者為已有檔案系統建立一個或多個完整的映象,這些映象會自動從源端同步後設資料,這樣在映象區域的客戶端可以就近訪問檔案系統,來得到高效能的體驗。由於可以只映象後設資料,並且同步過程是自動的,因此相較於之前提到的方案二而言,映象檔案系統在成本與運維複雜性上都有明顯的優勢。
02 映象檔案系統原理
JuiceFS 企業版的架構與社群版相似,都包括客戶端、物件儲存以及後設資料引擎。區別在於社群版的後設資料引擎通常採用第三方資料庫如 Redis、TiKV、MySQL 等,而企業版則配備了自研的高效能後設資料服務,其中的後設資料引擎由一個或多個 Raft 組組成,其架構圖如下:
得益於後設資料與資料分離的架構設計,使用者在建立映象檔案系統時可以獨立選擇是否映象後設資料和是否映象資料。兩者皆配置映象的架構如下:
此時,映象的後設資料服務其實跟源端的後設資料服務同屬一個 Raft 組,只是它們的角色是 learner。在源端發生後設資料更新時,服務會自動推送變更日誌到映象端,並在映象服務中進行回放。這樣,映象檔案系統的存在並不會影響源端檔案系統的效能表現,只是映象的後設資料版本會略落後一點點。
資料的映象也是採用非同步複製的方式,由指定配置的節點進行自動同步。不同的是,對映象區域的客戶端而言,它僅訪問本區域的後設資料,但是可以同時訪問兩個區域的物件儲存。實際讀取資料時,客戶端優先從本區域讀取;如果查詢不到所需的物件,再嘗試從源端區域讀取。
一般而言,資料本身的體量較大,再複製一份的成本也比較高,因此另一種更推薦的方式是僅映象後設資料,並且在映象區域構建一套分散式快取組來提升讀取資料的速度,示意如下:
JuiceFS 映象檔案系統推薦使用方法:兩區域共用同一個物件儲存,映象區域搭建分散式快取組來提升效能
這種使用方式尤其適合模型訓練等可以提前準備資料集的場景。使用者在執行訓練任務前,先透過 juicefs warmup 命令將所需資料物件拉取到映象區域的快取組中,接下來的訓練就能在映象區域內完成,且效能與在源端(假設也配置了類似的分散式快取組)基本一致。
03 實驗性新功能:可寫映象檔案系統
在之前的版本中,映象客戶端預設為只讀模式,因為映象後設資料本身只支援讀取,所有的修改操作必須在源端執行。然而,隨著使用者需求的增加,我們注意到一些新的使用情況,例如在資料訓練過程中產生的臨時資料。使用者希望避免維護兩個不同的檔案系統,並期望映象端也能支援少量寫操作。
為了滿足這些需求,我們在 5.1 版本中引入了 “可寫映象檔案系統” 功能。在設計這項功能時,我們主要考慮三個方面:首先是系統的穩定性,這是必須保證的;其次是兩端資料的一致性;最後是寫入的效能。
最初,我們探索的一種直接方案是允許後設資料映象也能處理寫操作。然而,在開發中我們發現,當需要將兩端的後設資料更新進行合併時,會面臨非常複雜的細節處理和一致性問題。因此我們還是維持 “僅源端後設資料可寫” 的設計。為了處理映象客戶端的寫請求,有兩個可選的方案:
方案一:客戶端將寫請求傳送至映象的後設資料服務,然後由其轉發到源端。源端接收到請求後開始執行操作,並在完成後將後設資料同步回映象端,並最終返回。這個方法的優點是客戶端操作簡單,只需傳送請求並等待響應。然而,這樣會使後設資料服務的實現變得複雜,因為需要管理請求的轉發和後設資料的同步。此外,由於鏈路較長,任何環節的錯誤都可能導致請求處理出錯。
方案二:客戶端不僅連線映象的後設資料服務,還直接連線源端的後設資料服務。客戶端內部進行讀寫分離,讀請求仍然傳送至映象端,但將寫請求傳送至源端。這種方法雖然使客戶端的處理邏輯複雜化,但簡化了後設資料服務的實現,讓它們僅需做很小的適配改動即可。對整個系統而言,這樣的做法穩定性也更高。
考慮到服務的簡潔性和可靠性,我們最終選擇了方案二,具體如下圖所示。相較於原來的架構而言,這個方案主要多了一條映象客戶端傳送寫請求到源端後設資料服務的流程。
以下將以建立一個新檔案(create 請求)為例對此流程進行詳細的介紹。假設源端和映象端的後設資料服務分別是 A 和 B,映象客戶端為 C,請求的完成大致分為 5 步:
- 客戶端傳送寫請求:C 首先將建立檔案的 create 請求傳送至 A。
- 源端服務響應:A 在處理請求後,傳送 create OK 告知 C 檔案已成功建立,並在響應中附帶 A 的後設資料版本號(假設為 v1)。
- 變更日誌推送:A 在傳送回覆給客戶端的同時,也會立即生成一條變更日誌,並將其推送給 B。
- 客戶端傳送等待請求:C 接收到源端的成功回覆後,會檢查自己的映象後設資料快取,看其版本是否也達到了 v1。如果沒有,客戶端會傳送一條 wait 訊息給 B,並附上版本號 v1。
- 映象端服務響應:B 收到等待訊息後,檢查自己的後設資料版本。如果已經達到 v1,則立即回覆 wait OK 給 C;否則的話就將請求放入內部佇列,等自己的版本號更新到 v1 以後再傳送回復。
C 在第 4 步確認映象版本已經達到 v1,或者第 5 步收到 wait OK 後返回給上層應用。無論哪種情況,都表示 B 已經包含了本次 create 的修改,因此後續 C 在讀取時,就能訪問到最新的後設資料。另外,由於步驟 2 和 3 幾乎是同時發生的,所以大部分情況下 wait 訊息都能被立即處理並返回。
映象客戶端的讀操作也有類似的檢查版本的機制。具體而言,C 在傳送讀請求前,會先比較其快取中源端服務和映象端服務的後設資料版本號;如果源端的版本號更新,則會先傳送 wait 訊息給 B,等到其版本也更新上來後再處理原來的讀請求。遺憾的是,C 快取的源端版本號並不一定是最新的(比如其長時間未傳送過寫請求的情況),也就是說該機制只是儘可能地讓 C 能讀到較新的資料,但並不保證其一定是最新的(可能會有小於 1 秒的滯後,與原有的只讀映象相同)。
最後,我們透過一個稍複雜些的讀寫混合的例子,來簡要說明使用 JuiceFS 映象檔案系統給使用者帶來的直接收益。
需求是客戶端 C 希望在 /d1/d2/d3/d4
目錄下建立一個新檔案 newf
。按照檔案系統的設計,C 需要逐級查詢路徑上的每一個目錄和檔案,並在確認檔案不存在後再傳送建立請求。現假設 C 到 A 和 B 的網路延遲分別是 30ms 和 1ms,C 尚未建立後設資料快取,並且忽略 A 和 B 的請求處理時間。
使用映象檔案系統的情況:C 的讀請求都由 B 處理,只有最後的建立檔案請求需要發往 A。總耗時大概需要 1 * 2 * 6(mirror lookup) + 30 * 2(source create) + 1 * 2(mirror wait) = 74ms。
沒有使用映象檔案系統的情況:如果直接在映象區域掛載原始檔系統,C 的每個請求都需要跟 A 互動,那麼總耗時就需要 30 * 2 * 6(source lookup) + 30 * 2(source create) = 420ms,是前者的 5 倍還多。
04 小結
在 AI 研究中,由於 GPU 資源的成本極高,多雲架構已成為眾多企業的標配。透過使用 JuiceFS 映象檔案系統,使用者可建立一個或多個完整的檔案系統映象,這些映象會自動從源端同步後設資料,使得映象區域的客戶端能夠就近訪問檔案,從而提供高效能並減少運維工作量。
在最新的 JuiceFS 5.1 版本中,我們對映象檔案系統進行了重要的最佳化,新增了允許寫入的功能,使得企業能夠在任何資料中心透過統一的名稱空間訪問資料。同時在保證資料一致性的前提下,享受就近快取的加速效果。希望透過這篇文章分享的實現思路與嘗試,為使用者提供一些見解與啟發。