常見開源分散式文件系統架構對比
- 2021 年 9 月 29 日
- 筆記
什麼是文件系統?
文件系統是電腦中一個非常重要的組件,為存儲設備提供一致的訪問和管理方式。在不同的作業系統中,文件系統會有一些差別,但也有一些共性幾十年都沒怎麼變化:
- 數據是以文件的形式存在,提供 Open、Read、Write、Seek、Close 等API 進行訪問;
- 文件以樹形目錄進行組織,提供原子的重命名(Rename)操作改變文件或者目錄的位置。
文件系統提供的訪問和管理方法支撐了絕大部分的電腦應用,Unix 的「萬物皆文件」的理念更是凸顯了它的重要地位。文件系統的複雜性使得它的可擴展性未能跟得上互聯網的高速發展,極大簡化了的對象存儲及時填補了空缺得以快速發展起來。因為對象存儲缺乏樹狀結構也不支援原子重命名操作,跟文件系統有很大的差別,本文暫不討論。
單機文件系統的挑戰
絕大多數文件系統都是單機的,在單機作業系統內為一個或者多個存儲設備提供訪問和管理。隨著互聯網的高速發展,單機文件系統面臨很多的挑戰:
- 共享:無法同時為分布在多個機器中的應用提供訪問,於是有了 NFS 協議,可以將單機文件系統通過網路的方式同時提供給多個機器訪問。
- 容量:無法提供足夠空間來存儲數據,數據只好分散在多個隔離的單機文件系統里。
- 性能:無法滿足某些應用需要非常高的讀寫性能要求,應用只好做邏輯拆分同時讀寫多個文件系統。
- 可靠性:受限於單個機器的可靠性,機器故障可能導致數據丟失。
- 可用性:受限於單個作業系統的可用性,故障或者重啟等運維操作會導致不可用。
隨著互聯網的高速發展,這些問題變得日益突出,湧現出了一些分散式文件系統來應對這些挑戰。
下面介紹幾個我了解過的分散式文件系統的基本架構,並比較不同架構的優點和局限。
GlusterFS
GlusterFS 是由美國的 Gluster 公司開發的 POSIX 分散式文件系統(以 GPL 開源),2007年發布第一個公開版本,2011年被 Redhat 收購。
它的基本思路就是通過一個無狀態的中間件把多個單機文件系統融合成統一的名字空間(namespace)提供給用戶。這個中間件是由一系列可疊加的轉換器(Translator)實現,每個轉換器解決一個問題,比如數據分布、複製、拆分、快取、鎖等等,用戶可以根據具體的應用場景需要靈活配置。比如一個典型的分散式卷如下圖所示:
Server1 和 Server2 構成有 2 副本的 Volume0,Server3 和 Server4 構成 Volume1,它們再融合成有更大空間的分散式卷。
優點:
數據文件最終以相同的目錄結構保存在單機文件系統上,不用擔心 GlusterFS 的不可用導致數據丟失。
沒有明顯的單點問題,可線性擴展。
對大量小文件的支援估計還不錯。
挑戰:
這種結構是相對靜態的,不容易調整,也要求各個存儲節點有相同的配置,當數據或者訪問不均衡時沒法進行空間或者負載調整。故障恢復能力也比較弱,比如 Server1 故障時,Server2 上的文件就沒辦法在健康的 3 或者 4上增加拷貝保障數據可靠。
因為缺乏獨立的元數據服務,要求所有存儲節點都會有完整的數據目錄結構,遍歷目錄或者做目錄結構調整時需要訪問所有節點才能得到正確結果,導致整個系統的可擴展能力有限,擴展到幾十個節點時還行,很難有效地管理上百個節點。
CephFS
CephFS 始於 Sage Weil 的博士論文研究,目標是實現分散式的元數據管理以支援 EB 級別數據規模。2012年,Sage Weil 成立了 InkTank 繼續支援 CephFS 的開發,於 2014年被 Redhat 收購。直到 2016 年,CephFS 才發布可用於生產環境的穩定版(CephFS 的元數據部分仍然是單機的)。現在,CephFS 的分散式元數據仍然不成熟。
Ceph 是一個分層的架構,底層是一個基於 CRUSH(哈希)的分散式對象存儲,上層提供對象存儲(RADOSGW)、塊存儲(RDB)和文件系統(CephFS)三個API,如下圖所示:
用一套存儲系統來滿足多個不同場景的存儲需求(虛擬機鏡像、海量小文件和通用文件存儲)還是非常吸引人的,但因為系統的複雜性需要很強的運維能力才能支撐,實際上目前只有塊存儲還是比較成熟應用得比較多,對象存儲和文件系統都不太理想,聽到一些使用案例用過一段時間後就放棄了。
CephFS 的架構如下圖所示:
CephFS 是由 MDS(Metadata Daemon) 實現的,它是一個或者多個無狀態的元數據服務,從底層的 OSD 載入文件系統的元資訊,並快取到記憶體中以提高訪問速度。因為 MDS 是無狀態的,可以配置多個備用節點來實現 HA,相對比較容易。不過備份節點沒有快取,需要重新預熱,有可能故障恢復時間會比較長。
因為從存儲層載入或者寫入數據會比較慢,MDS 必須使用多執行緒來提高吞吐量,各種並發的文件系統操作導致複雜度大大上升,容易發生死鎖,或者因為 IO 比較慢導致的性能大幅下降。為了獲得比較好的性能,MDS 往往需要有足夠多的記憶體來快取大部分元數據,這也限制了它實際的支撐能力。
當有多個活躍的 MDS 時,目錄結構中的一部分(子樹)可以動態的分配到某個MDS並完全由它來處理相關請求,以達到水平擴展的目的。多個活躍之前,不可避免地需要各自鎖機制來協商對子樹的所有權,以及通過分散式事務來實現跨子樹的原子重命名,這些實現起來都是非常複雜的。目前最新的官方文檔仍然不推薦使用多個 MDS(作為備份是可以的)。
GFS
Google 的 GFS 是分散式文件系統中的先驅和典型代表,由早期的 BigFiles 發展而來。在 2003 年發表的論文中詳細闡述了它的設計理念和細節,對業界影響非常大,後來很多分散式文件系統都是參照它的設計。
顧名思義,BigFiles/GFS 是為大文件優化設計的,並不適合平均文件大小在 1MB 以內的場景。GFS的架構入下圖所示:
GFS 有一個 Master 節點來管理元數據(全部載入到記憶體,快照和更新日誌寫到磁碟),文件劃分成 64MB 的 Chunk 存儲到幾個 ChunkServer 上(直接使用單機文件系統)。文件只能追加寫,不用擔心 Chunk 的版本和一致性問題(可以用長度當做版本)。這個使用完全不同的技術來解決元數據和數據的設計使得系統的複雜度大大簡化,也有足夠的擴展能力(如果平均文件大小大於 256MB,Master 節點每 GB 記憶體可以支撐約 1PB 的數據量)。放棄支援 POSIX 文件系統的部分功能(比如隨機寫、擴展屬性、硬鏈接等)也進一步簡化了系統複雜度,以換取更好的系統性能、魯棒性和可擴展性。
因為 GFS 的成熟穩定,使得 Google 可以更容易地構建上層應用(MapReduce、BigTable等)。後來,Google 開發了擁有更強可擴展能力的下一代存儲系統 Colossus,把元數據和數據存儲徹底分離,實現了元數據的分散式(自動 Sharding),以及使用Reed Solomon 編碼來降低存儲空間佔用從而降低成本。
HDFS
出自 Yahoo 的 Hadoop 算是 Google 的 GFS、MapReduce 等的開源Java實現版,HDFS 也是基本照搬 GFS 的設計,這裡就不再重複了,下圖是HDFS的架構圖:
HDFS的可靠性和可擴展能力還是非常不錯的,有不少幾千節點和 100PB 級別的部署,支撐大數據應用表現還是很不錯的,少有聽說丟數據的案例(因為沒有配置回收站導致數據被誤刪的除外)。
HDFS 的 HA 方案是後來補上的,做得比較複雜,以至於最早做這個 HA 方案的 Facebook 在很長一段時間(至少 3 年)內都是手動做故障切換(不信任自動故障切換)。
因為 NameNode 是 Java 實現的,依賴於預先分配的堆記憶體大小,分配不足容易觸發 Full GC 而影響整個系統的性能。有一些團隊嘗試把它用 C++ 重寫了,但還沒看到有成熟的開源方案。
HDFS 也缺乏成熟的非 Java 客戶端,使得大數據(Hadoop等工具)以外的場景(比如深度學習等)使用起來不太方便。
MooseFS
MooseFS 是來自波蘭的開源分散式 POSIX 文件系統,也是參照了 GFS 的架構,實現了絕大部分 POSIX 語義和 API,通過一個非常成熟的 FUSE 客戶端掛載後可以像本地文件系統一樣訪問。MooseFS 的架構如下圖所示:
MooseFS 支援快照,用它來做數據備份或者備份恢復等還是恢復方便的。
MooseFS 是由 C 實現的,Master 是個非同步事件驅動的單執行緒,類似於 Redis。不過網路部分使用的是 poll 而不是更高效的 epoll,導致並發到 1000 左右時 CPU 消耗非常厲害。
開源的社區版沒有HA,是通過 metalogger 來實現非同步冷備,閉源的收費版有 HA。
為了支援隨機寫操作,MooseFS 中的 chunk 是可以修改的,通過一套版本管理機制來保證數據一致性,這個機制比較複雜容易出現詭異問題(比如集群重啟後可能會有少數 chunk 實際副本數低於預期)。
JuiceFS
上面說的 GFS、HDFS 和 MooseFS 都是針對自建機房這種軟硬體環境設計的,將數據的可靠性和節點可用性合在一起用多機多副本的方式解決。但是在公有雲或者私有雲的虛擬機里,塊設備已經是具有三副本可靠性設計的虛擬塊設備,如果再通過多機多副本的方式來做,會導致數據的成本居高不下(實際上是 9 個拷貝)。
於是我們針對公有雲,改進 HDFS 和 MooseFS 的架構,設計了 JuiceFS,架構如下圖所示:
JuiceFS 使用公有雲中已有的對象存儲來替換 DataNode 和 ChunkServer,實現一個完全彈性的 Serverless 的存儲系統。公有雲的對象存儲已經很好地解決了大規模數據的安全高效存儲,JuiceFS 只需要專註元數據的管理,也大大降低了元數據服務的複雜度(GFS 和 MooseFS 的 master 要同時解決元數據的存儲和數據塊的健康管理)。我們也對元數據部分做了很多改進,從一開始就實現了基於 Raft 的高可用。要真正提供一個高可用高性能的服務,元數據的管理和運維仍然是很有挑戰的,元數據是以服務的形式提供給用戶。因為 POSIX 文件系統 API 是應用最最廣泛的 API,我們基於 FUSE 實現了高度 POSIX 兼容的客戶端,用戶可以通過一個命令行工具把 JuiceFS 掛載到 Linux 或者 macOS 中,像本地文件系統一樣快速訪問。
上圖中右邊虛線部分是負責數據存儲和訪問的部分,涉及用戶的數據隱私,它們是完全在客戶自己的帳號和網路環境中,不會跟元數據服務接觸。我們(Juicedata)沒有任何方法接觸到客戶的內容(元數據除外,請不要把敏感內容放到文件名里)。
小結
簡要介紹了下我所了解的幾個分散式文件系統的架構,把他們按照出現的時間順序放在下面的圖裡(箭頭表示後參考了前者或者是新一代版本):
上圖中上部分藍色的幾個文件下主要是給大數據場景使用的,實現的是 POSIX 的子集,而下面綠色的幾個是 POSIX 兼容的文件系統。
他們中以 GFS 為代表的元數據和數據分離的系統設計能夠有效平衡系統的複雜度,有效解決大規模數據的存儲問題(通常也都是大文件),有更好的可擴展性。這個架構下支援元數據的分散式存儲的 Colossus 和 WarmStorage 更是具有無限的可擴展能力。
JuiceFS 作為後來者,學習了 MooseFS 實現分散式 POSIX 文件系統的方式,也學習了 Facebook 的 WarmStorage 等元數據和數據徹底分開的思路,希望為公有雲或者私有雲等場景下提供最好的分散式存儲體驗。JuiceFS 通過將數據存儲到對象存儲的方式,有效避免了使用以上分散式文件系統時的雙層冗餘(塊存儲的冗餘和分散式系統的多機冗餘)導致的成本過高問題。JuiceFS 還支援所有的公有雲,不用擔心某個雲服務鎖定,還能平滑地在公有雲或者區之間遷移數據。
推薦閱讀: