中山大學的 iSEE 實驗室(Intelligence Science and System) Lab)在進行深度學習任務時,需要處理大量小檔案讀取。在高併發讀寫場景下,原先使用的 NFS 效能較低,常在高峰期導致資料節點卡死。此外,NFS 系統的單點故障問題也導致一旦資料節點當機,該機器上的資料將完全不可用。擴容問題同樣棘手,每增加一臺資料節點,就需要在所有計算節點上進行多次掛載。而新增的資料節點由於資料量較小,並不能有效分擔讀寫壓力。
為解決這些問題,經過初步評估,實驗室選擇了JuiceFS 作為替代的儲存方案。當前,結合TiKV 的 JuiceFS 已成功管理超過 5 億個檔案。新方案顯著提升了在高併發場景下的效能和系統穩定性,確保了深度學習訓練過程中計算節點的連續執行,同時基本解決了單點故障的問題。
此外,JuiceFS 的操作簡便易學,甚至不需要專職的儲存管理人員來維護,這對於主要由 AI 領域學生組成的實驗室叢集管理團隊來說,極大減輕了他們的運維負擔。本文將詳細介紹新方案的選型與實踐歷程。
01 深度學習場景儲存需求
實驗室叢集主要用於深度學習的訓練和方法驗證。在訓練過程中,我們面臨四種主要的讀寫需求。
-
訓練過程中需要讀取大量的資料集檔案。
-
在模型訓練的初始階段,我們需要載入一個 Conda 的環境,這涉及讀取眾多不同大小的庫檔案。
-
在訓練過程中,隨著模型引數的調整,我們需要頻繁地寫入不同大小的模型引數切換檔案,這些檔案的大小可能從幾十兆位元組到數幾位元組不等,具體取決於模型的大小。
-
我們還需要記錄(每次訓練的)訓練日誌,這主要涉及到少量資料的頻繁寫入。
基於上述需求,我們對儲存系統有以下四個主要期望:
-
首要的是,系統在高併發的讀寫環境下應具備出色的穩定性。在確保穩定性的基礎上,我們再追求效能的提升。
-
其次,我們希望系統能夠消除單點故障,即任何節點的故障都不應影響整個叢集的訓練程序。
-
第三,我們期望系統具有友好的運維特性。考慮到我們實驗室團隊成員主要專注於 AI 深度學習,對儲存系統的專業知識瞭解有限,因此我們需要一個運維簡便、維護頻率較低的儲存系統。
-
最後,我們希望系統能夠提供一個 POSIX 介面。由於我們使用 PyTorch 框架進行模型訓練,如果系統支援 POSIX 介面,將極大地降低使用者的學習成本,同時減少對現有程式碼的改動。
實驗室現有的硬體配置
首先,我們的硬體系統主要包括三類裝置。
第一類是資料節點,我們目前擁有大約三四臺這樣的資料節點。這些節點主要由大量機械硬碟構成,每個節點的機械硬碟都搭建RAID6陣列,所有節點加起來的總儲存容量近 700TB。
第二類是我們的計算節點,這些節點主要搭載了 GPU,用於處理計算密集型任務。在儲存方面,由於 JuiceFS 具有快取功能,我們為這些節點配備了 1 至 3 個 SSD 快取盤,每個盤的大小約為 800GB。儘管這些快取盤的大小看起來並不龐大,但它們通常能夠快取多個完整的資料集,前提是資料集的大小適中。
第三類是基於我們使用 TiKV 作為後設資料引擎的考量,我們配置了三個 TiKV 節點,專門用於作為後設資料引擎執行。這些節點的資料盤容量大約為 2TB,記憶體則主要配置了 512GB。根據目前的使用情況,當處理大約 5 至 6 億的檔案量時,每臺節點大約使用了 300 多 GB 的記憶體。
NFS 的儲存解決方案所面臨的挑戰
在初期,當計算節點數量較少時,我們採用了在資料節點上構建單機檔案系統的策略,並透過簡單的 NFS 掛載方式,將這些檔案系統掛載到各個計算節點的目錄上。這樣,當需要新增第二臺資料節點時,我們只需將其掛載到新的目錄上,即可實現資料在所有節點間的共享。此方案在運維上相對簡單,但隨著節點數量的增加,我們逐漸發現基於 NFS 的儲存系統效能顯著下降。
在高併發訓練高峰期,系統常出現明顯的卡頓甚至卡死現象,這極大地影響了我們的工作效率。為了緩解這一問題,我們曾嘗試將叢集劃分為兩個小叢集,以減少每個節點的資料壓力。然而,這一臨時措施並未帶來顯著的改進,反而帶來了新的問題,如不同叢集間資料的互通性受限。
總的來說,我們在處理大量小檔案讀取的場景下,原有的 NFS 儲存方案表現出了較低的讀取效能和較高的當機風險。此外,整個系統缺乏快取機制,而深度學習資料集在訓練過程中需要頻繁讀取,這無疑增加了讀寫壓力。因此,我們需要尋找一種更為高效、穩定的儲存解決方案,以應對這些挑戰。
02 為何選擇 JuiceFS
以下是我們選擇 JuiceFS 的主要原因:
-
JuiceFS 支援 POSIX 介面,這意味著在遷移系統時,使用者幾乎不會感受到任何影響,實現了無感的遷移體驗。
-
JuiceFS 的快取功能對於深度學習模型的訓練尤為重要。儘管在首輪資料載入時可能無法直接命中快取,但隨著訓練的進行,後續輪次幾乎能夠百分之百地利用快取,從而顯著提升訓練過程中的資料讀取速度。
-
JuiceFS 的回收站功能為使用者提供了資料恢復的可能性,我們設定的為期一天的回收站使得誤刪的檔案在一天內得以恢復,這在實際應用中已幫助使用者及時恢復了誤刪的資料。
-
JuiceFS 還配備了一系列特有的檔案管理命令,這些命令不僅方便使用者使用,而且相較於傳統檔案系統,其功能更為快速和高效。
-
值得一提的是,在運維層面,JuiceFS 的表現格外出色。由於我們實驗室沒有專職的人員負責儲存,我們期望的儲存系統應具備簡單性和低運維頻率的特點,而 JuiceFS 恰好滿足了這些需求。它提供了豐富的文件資源,使得我們在上手和解決問題時能夠迅速找到解決方案。JuiceFS 能夠自動備份後設資料,為資料提供了額外的保護層,增強了我們的安全感。JuiceFS 自帶的 Prometheus 和 Grafana 監控功能使得我們能夠方便地在網頁端檢視整個系統的狀態,包括檔案數量的增長情況、檔案使用量以及整個系統的大小等關鍵資訊,這為我們提供了及時的系統監控和管理的便利。
03 搭建基於 JuiceFS 的儲存系統
首先,我們的 JuiceFS 採用了 TiKV 作為後設資料引擎,同時利用 SeaweedFS 作為物件儲存。
總體來看,JuiceFS 分為兩個目錄進行掛載。第一個目錄用於儲存使用者檔案,採用獨立掛載的方式使我們能夠靈活調整掛載引數,以滿足不同目錄的特定需求。在使用者檔案目錄方面,我們主要沿用了預設的掛載引數,以確保穩定性和相容性。
第二個在資料集目錄方面,鑑於其幾乎只讀的特性,我們特別增加了後設資料快取的失效時間。這樣做可以在多次讀取過程中多次命中後設資料快取,從而避免重複訪問原始資料,顯著提升資料訪問的速度。正是基於這一改動,我們選擇了將資料集和使用者檔案分別掛載於兩個不同的目錄。
此外,我們還進行了一項實踐上的最佳化。考慮到計算節點的主要任務是處理計算任務而非後臺任務,我們在一臺相對空閒的節點上配備了較大的記憶體,專門用於處理後臺任務,如備份後設資料。隨著檔案數量的增加,備份後設資料對記憶體的需求也逐漸增大,因此這種分配方式既確保了計算節點的效能,又滿足了後臺任務對資源的需求。
後設資料引擎選型:Redis vs TiKV
起初,我們使用了兩個資料引擎:Redis 和 TiKV。在資料量相對較小,即上千萬級別的階段,我們選擇了 Redis。當時,由於我們對這些軟體並不十分熟悉,我們參考了相關文件,並考慮到 Redis 的上手難度較低、效能較高,且相關資料豐富,因此決定採用它。然而,隨著檔案數量的迅速增長,Redis 的效能出現了顯著的下降。
具體來說,由於我們為 Redis設 置了 RDB 持久化,當記憶體佔用量增加時,Redis 幾乎持續處於 RDB 備份狀態,這導致了效能的明顯下滑。另外,我們當時採用了哨兵模式,並啟用了主從複製以增加可用性,但這也帶來了問題。由於是非同步複製,主節點當機後,從節點的資料一致性難以保證。
此外,我們瞭解到客戶端並不會從 Redis 的從節點上讀取後設資料,而是主要依賴主節點進行讀寫操作。因此,隨著檔案數量的增加,Redis 的效能進一步下降。
隨後,我們考慮並測試了 TiKV 作為後設資料引擎的替代方案。從官方文件來看,TiKV 的效能僅次於 Redis,並且在實際使用中,使用者層面的體驗與 Redis 相差不大。在資料量達到 5 至 6 億檔案的情況下,TiKV 的表現相當穩定。
TiKV 的另一個優勢在於其負載均衡和冗餘儲存的能力。我們採用了三臺節點的配置,每個節點都具備多個副本,確保了資料的安全性和可用性。對於運維團隊來說,這些特性極大地減輕了工作負擔,提高了系統的穩定性和可靠性。
後設資料遷移 :從 Redis 到 TiKV
我們於今年一月份左右進行了系統遷移,並已經穩定使用 TiKV 近半年,期間未出現當機或任何重大問題。
在遷移初期,由於 Redis 難以支撐高達 5 至 6 億檔案的後設資料負載,我們決定採用匯出與匯入的方法來實現遷移。具體地,我們使用了特定的命令將 Redis 中的後設資料匯出為統一的 JSON 檔案,並計劃透過 load 命令將其載入到全新的 TiKV 系統中。
然而,在遷移過程中,我們遇到了一個挑戰。我們注意到,某個使用者的目錄由於檔案深度過深或其他原因,在匯出時遇到了失敗。為了解決這個問題,我們採取了一種創新的方法。我們將除問題目錄外的其他所有目錄分別匯出,並手動開啟和拼接這些 JSON 檔案,以重新構建完整的後設資料結構。
在手動處理這些檔案時,我們發現後設資料的 JSON 檔案結構清晰,拼接操作相對簡便。其巢狀結構與目錄結構基本一致,這使得我們能夠高效地處理後設資料。最終,我們成功地將這些檔案匯入到 TiKV 中,完成了遷移過程。
04 為什麼使用物件儲存 SeaweedFS?
總體而言,我們遵循了 SeaweedFS 官方文件中推薦的主要元件和基本功能,並未涉及過多新穎或高階特性。在具體部署上,我們採用了 CPU 節點作為核心,並在其上執行了 master 伺服器。而資料節點則分佈執行在不同的物理節點上,每個節點均執行 volume 服務以處理資料儲存。此外,我們在相同的 CPU 節點上執行了 filer 伺服器,該伺服器為 JuiceFS 提供 S3 服務介面,負責與 JuiceFS 進行對接。從目前的執行情況來看,CPU 節點的負載並不重,主要的資料讀寫和處理任務均分散在各個 worker 節點上。
在資料冗餘和備份方面,我們充分利用了 SeaweedFS 的冗餘特性。邏輯上,我們將資料節點劃分為兩個 Rack。當資料寫入時,它會在兩個 Rack 中都進行寫入,確保在 Rack 0 和 Rack 1 中各有一份備份。只有當兩個 Rack 都成功寫入資料時,才視為寫入成功。雖然這種策略使得我們的硬碟容量在邏輯上幾乎減半(因為每份資料都要寫兩份),但它確保了系統的高可用性和資料安全性。即使其中一個 Rack 中的某個節點出現故障或當機,也不會影響整體的讀寫操作和資料安全。
SeaweedFS 的優缺點
首先,從上手難度來看,SeaweedFS 相較於工業界廣泛使用的 Ceph,顯得更為容易操作。其 master、volume 和 filer 等核心元件均可透過編寫指令碼來輕鬆啟動。使用者只需瀏覽對應的命令引數,並根據實際需求進行配置,隨後透過指令碼即可完成啟動過程。儘管 SeaweedFS 的文件資料相對較少,但其易用性依然值得肯定。
此外,與另一款常用的物件儲存系統 Minio 相比,儘管我們未曾直接使用過 Minio,但根據以往案例和介紹,Minio 在處理大量小檔案時可能存在一定的不足。因此,我們在選擇時並未考慮 Minio。
SeaweedFS 的另一個顯著優點在於其安全性和可用性。透過將資料節點劃分為兩個 Rack,系統實現了資料的冗餘備份,從而提高了資料安全性。同時,其master伺服器具備自動排程資料寫入的功能,能夠自動將資料分配到各個 worker 節點上,實現了負載均衡。此外,SeaweedFS 的擴容過程也相對簡單,只需新增機器並連線到 master 節點,系統即可自動進行擴容。
此外,SeaweedFS 官方還推薦了一款 master 管理指令碼,該指令碼配置簡單,僅需編寫少量配置即可實現定期自動修復資料冗餘和平衡資料分佈的功能,極大地提高了系統的維護效率。然而,SeaweedFS 的缺點也不容忽視。其文件資料相對較少且較為陳舊,這可能會給新使用者帶來一定的學習難度。儘管我們在使用過程中並未遇到其他明顯的缺點,但這也是未來需要改進的地方。
05 方案實踐中遇到的挑戰
使用過程中客戶端會異常退出
首先,我們曾遭遇客戶端異常退出的情況,經過分析發現這是由於記憶體溢位(OOM)導致的。隨著原資料的不斷增長,當計算節點未啟用 --no-bgjob
選項時,特定節點因執行高記憶體消耗任務(主要是自動後設資料備份)而導致剩餘記憶體不足,進而無法備份原資料,最終引發 OOM 並導致客戶端退出。為了解決這個問題,我們在所有計算節點上新增了--no-bgjob
選項,並利用閒置的資料節點作為專門的後臺任務處理節點。
首次大檔案的讀取較慢
其次,我們在使用初期發現了一個效能問題。特別是在首次讀取大檔案時,發現讀取速度遠低於千兆網路頻寬的極限。經過深入調查,我們發現這是由於在測試物件儲存效能時,未正確配置 JuiceFS 命令的引數。我們沒有指定 --storage s3
選項,導致測試預設為本地硬碟效能,而非實際的物件儲存效能。這一誤解導致了我們對物件儲存效能的誤判。透過進一步檢查,我們發現 SeaweedFS Filer 後設資料引擎的效能瓶頸,這主要是因為底層使用的是單個機械硬碟。因此,我們將考慮最佳化這一環節以提升效能。
解壓資料集較慢(大量小檔案寫入)
最後,我們在日常使用中偶爾需要解壓資料集,這涉及到大量小檔案的寫入操作。我們發現這一過程相較於本地解壓明顯較慢。在與 JuiceFS 官方溝通後,他們建議我們嘗試使用 write-back 加速功能。這一功能允許在檔案寫入後立即返回,而後臺則負責將資料上傳到物件儲存。我們計劃在未來實施這一建議以最佳化解壓效能。
希望這篇內容能夠對你有一些幫助,如果有其他疑問歡迎加入 JuiceFS 社群與大家共同交流。