從 Hadoop 到雲原生, 巨量資料平臺如何做存算分離

2022-09-14 12:00:56

Hadoop 的誕生改變了企業對資料的儲存、處理和分析的過程,加速了巨量資料的發展,受到廣泛的應用,給整個行業帶來了變革意義的改變;隨著雲端計算時代的到來, 存算分離的架構受到青睞,企業開開始對 Hadoop 的架構進行改造。

今天與大家一起簡單回顧 Hadoop 架構以及目前市面上不同的存算分離的架構方案,他們的利弊各有哪些,希望可以給正在存算分離架構改造的企業一些參考和啟發。

Hadoop 存算耦合架構回顧

2006 年 Hadoop 剛釋出,這是一個 all-in-one 的套裝,最早有三個核心的元件:MapReduce 負責計算,YARN 負責資源排程,HDFS 分散式檔案系統,負責存資料。

在這三個元件中,發展最迅速和多元的是計算元件這一層,最早只有一個 MapReduce,但業界很快在計算層上面各顯神通,造出了一大堆的輪子,包括有 MapReduce,Tez,Spark 這樣的計算框架,Hive 這類資料倉儲,還有 Presto、Impala 查詢引擎,各種各樣的元件。配合這些元件的,還有像 scoop 這樣的資料流轉採集的元件也很豐富,一共有幾十款。

底層儲存經過了大概 10 年左右的時間,一直是 HDFS 一枝獨秀,帶來的一個結果就是它會成為所有計算元件預設的設計選擇。上面提到的這些巨量資料生態裡發展出來的各種元件,都是面向HDFS API 去做設計的。有些元件也會非常深入的利用 HDFS 的一些能力,比如深入看 Hbase,在寫 WAL log 的時候就直接利用了HDFS 的一些很核心的能力,才能達到一個低時延的寫入;比如說像最早的 MapReduce 和 Spark 也提供了資料親和性(Data Locality)的能力,這些都是HDFS 提供的一些特殊的 API。

這些巨量資料元件面向 HDFS API 設計的做法, 為後續資料平臺上雲帶來了潛在的挑戰

下面是一個簡化的區域性的架構圖,通過這張圖快速理解 Hadoop 存算耦合架構。在這張圖有三個節點,每個節點裡面它都承載了 HDFS DataNode 的存資料的角色,但同時 YARN 也會在這裡佈一個 Node Manager的程序。有了 Node Manager 之後,YARN 就會認為 HDFS DataNode 的節點,在其管理範圍之內,當需要計算任務可以分發到這個節點上來完成。儲存任務和資料就在同一個機器裡了,計算的時候就可以直接讀到磁碟上的資料。

為什麼 Hadoop 在設計之初是一個儲存計算耦合的架構?

一個不能忽略的重要的原因是,網路通訊和硬體的侷限。2006年,當時雲端計算幾乎還沒有發展,亞馬遜才釋出第一個服務而已。

在機房裡面,當時我們面對的最大的問題就是網路卡,主流的還是百兆網路卡,剛開始用千兆網路卡。這個時候,巨量資料使用的磁碟,吞吐大概是 50MB/s,對網路頻寬來說要乘以 8,也就是 400M bps;如果一個節點裡放 8 塊盤,吞吐都跑起來,就需要幾千兆頻寬傳輸了,但是網路卡最高也就1Gb。這就意味著每一個節點網路頻寬根本不夠,無法讓這個節點裡面的所有的磁碟的能力都發揮出來。所以如果計算任務在網路的一端,資料在資料節點在網路的另一端,計算任務需要說通過網路傳輸來進行,網路頻寬是一個最明顯的瓶頸。

存算分離的需求出現

首先從,企業的需求看,從 2006 年發展到 2016 年左右,這十年我們看到了一些新的變化,第一企業資料增長很快,但是算力的需求其實長得沒那麼快。這些任務靠人開發,不會發生一天一倍的去漲的情況,但是產生的資料的速度是是非常快的,有可能是指數型的;而且有些資料產生出來,也不一定馬上知道怎麼用,但未來會用,所以企業都會先把資料儘可能全量的去存起來,再去挖掘它的價值。

在這個背景下,存算耦合的硬體的拓撲的架構就給擴容帶來了一個影響,當儲存不夠,就要去加機器。但是不能只加機器,不能只有硬碟,因為在存算耦合的架構上,資料的節點還需要負責計算,所以 CPU 和記憶體也不能太差。因此設定的機器都是計算與儲存設定非常平衡的機器,在提供足夠儲存容量的同時,也提供了等量的算力。但實際場景中算力的需求沒漲。這樣擴出來的算力對企業來說造成了更大的浪費,整個叢集在儲存和 I/O 上的資源利用率可能是非常不平衡的,當叢集越大,這種不平衡就越嚴重。而且另外買機器也挺難的,購買的機器必須是計算與儲存平衡的。

而且,資料排程親和性的策略在實際的業務中未必能發揮作用,因為資料有可能會有很明顯的傾斜,可能會有很區域性的熱點,需要非常多的算力。巨量資料平臺的任務可能排程到有限節點上,I/O 仍然有可能成為瓶頸。

在這個過程中硬體也有變化,給存算分離架構帶來了可行性。首先,10Gb萬兆網路卡普及了,今天機房裡或者包括雲上也開始有更多的 20Gb、40Gb,甚至 50Gb,有些 AI 的場景甚至有100Gb的網路卡,網路的頻寬其實加大了比以前提升了100倍之多。

儲存方面,在今天大的資料叢集裡面,許多企業還是使用磁碟來儲存,磁碟的吞吐提升了一倍,從 50MB/s 每秒提升到 100MB/s。一個設定了萬兆的網路卡的範例,可以支援差不多 12 塊磁碟的峰值吞吐,對於大部分企業來說已經夠用了,以前網路傳輸的瓶頸就基本不存在了。

不僅網路卡,磁碟也在變化,軟體也在變化。最早的時候,我們可能用 csv 或者打一個 zip 包,現在有了更高效的壓縮演演算法,比如說有 snappy、lz4、zstandard 這些。而且有了 Avro、Parquet、Orc 這些列存格式。

這些變化加在一起,都進一步減小了需要傳輸的資料量。同時, 網路卡在提升,再加上硬硬碟本身的吞吐沒增加多少,企業以前曾經要面對的 I/O 的瓶頸就逐漸的在弱化甚至消除,保證了存算分離的可行性。

如何實現存算分離?

最初的嘗試:在雲上獨立部署 HDFS

從2013、2014年,行業內開始看到一些存算分離架構的嘗試。最初的方案比較簡單,就是獨立部署 HDFS,不再和負責計算 worker 去混合部署。這個方案在 Hadoop 生態裡,沒有引入任何的新元件。

從下面的示意圖可以看到, DataNode 節點上不再部署 Node Manager,意味著不再把計算任務傳送到 DataNode 節點上。儲存成為一個獨立叢集,計算需要用到的資料都會通過網路來傳輸,端到端的萬兆網路卡去支援,網路傳輸線沒有在下圖示出。

在這個改變裡,儘管 HDFS 最巧妙的資料本地性這個設計被捨棄了,但由於網路通訊速度的提高, 給叢集的設定帶來更大的便利。Juicedata 創始人 Davies,2013 年在 Facebook 工作期間,團隊就做了這樣的實驗, 發現這樣的一個存算分離的改造,對整個平臺效能的影響是僅僅是幾個百分點,但是給叢集的設定管理帶來了一個還很大的便利,可以獨立的部署和管理計算節點了。

但是這個嘗試沒有得到進一步發展,是什麼原因呢?最大的一個原因,當在機房做這樣的改造是可行的,但當我們去使用雲上資源的時候,這個方案的弊端就顯露了。

首先,源自 HDFS 的多副本機制在雲上會增加企業的成本。過去,企業在機房使用裸硬碟去搭建一套 HDFS,為了解決裸硬損壞的風險, HDFS 設計了多副本的機制,來保證資料安全性;同時多副本還承載著保證資料可用性的作用。除了磁碟損壞,當某一個 DataNode 的節點臨時宕機了,這個節點上的資料存取不到了?多副本機制在可靠性和可用性上都發揮作用。當資料被遷移到雲上時,雲提供給使用者的是經過多副本機制儲存的雲盤,不再是裸硬碟了,企業用這塊雲盤去搭一個HDFS,又要做3副本,企業資料在雲上要存 9 副本,成本立馬飆升了好幾倍。

後來,雲也會提供一些有裸硬碟的機型,但是這類機型往往都非常少,比如說雲上有 100 款虛擬機器器,雲盤可以任意設定,但是有裸盤的機型只有 5~10 款,選擇餘地比較少,這些型號不一定能匹配企業的叢集需要。

第二個原因, 這個方案不能讓企業得到雲上的獨特價值,比如開箱即用,彈性伸縮,以及按量付費這些雲上最大的優勢。在雲上部署 HDFS, 需要自己建立機器,手動部署和維護,自己監控和運維,而且還不能方便地擴縮容。這種情況下,HDFS 上雲實現存算分離,仍然有其痛點。

第三個原因,HDFS 本身的侷限。首先是,NameNode,只能垂直擴充套件,並不能分散式擴充套件說擴出更多的 NameNode 節點,限制了 HDFS 單叢集去管理的檔案數量。

當 NameNode 的資源佔用比較多,負載又高的時候就有可能會觸發 FullGC(Garbage Collection) 。一旦觸發這個問題之後,它會影響到整個 HDFS 叢集可用性。系統儲存可能宕機,不能讀,又無法干預 GC的過程,系統卡多久無法確定。這個也是 HDFS 高負載叢集一直以來的痛點。

根據實際運維經驗,一般在 3 億檔案以內,運維 HDFS 還是比較輕鬆的,3 億檔案之後運維的複雜度就會明顯提升,峰值可能就在 5 億檔案左右,就達到單機群的天花板了。檔案量更多,需要引入 HDFS的 Federation 聯邦的機制,但是它就增加了很多的運維和管理的成本。

公有云+ 物件儲存

隨著雲端計算技術的成熟,企業儲存又多了一個選項,物件儲存。不同的雲廠商有不同的英文縮寫名,例如阿里雲的物件儲存服務叫做 OSS,華為雲 OBS,騰訊雲 COS,七牛 Kodo;物件儲存適用於大規模儲存非結構化資料的資料儲存架構,其設計的初衷是想滿足非常簡單的上傳下載資料,企業儲存系統擁有超級強大的彈性伸縮的能力,還能保證低成本的儲存。

最早從 AWS 開始,後來所有的雲廠商其實都在往這個方向發展,開始推動用物件儲存去替代 HDFS。這些方案首先帶來了兩個 HDFS 無法實現的最明顯的好處:

  • 第一,物件儲存是服務化的,開箱即用,不用做任何的部署監控運維這些工作,特別省事兒。

  • 第二,彈性伸縮,企業可以按量付費,不用考慮任何的容量規劃,開一個物件儲存的 bucket ,有多少資料寫多少資料,不用擔心寫滿。

這些方案相比在雲上獨立部署 HDFS , 運維方面是有了很大的簡化。但當物件儲存被用來去支援複雜的 Hadoop 這樣的資料系統,就會發現如下的一些問題

  1. 檔案 Listing 的效能比較弱。Listing 是檔案系統中最基礎的一個操作。我們在檔案系統中 List 目錄,包括 HDFS 裡面 List 目錄,都是非常輕量快的操作。它的效能是源於在檔案系統中,資料是一個樹形結構。

物件儲存沒有樹形結構的,它的整個儲存結構是扁平的。當用戶需要儲存成千上萬,甚至數億個物件,物件儲存需要做的是用 Key 去建立一份索引,Key 可以理解為檔名是該物件唯一識別符號。如果使用者要執行 Listing,只能在這個索引裡面去搜尋,搜尋的效能相比樹形結構的查詢弱很多。

  1. 物件儲存沒有原子 Rename, 影響任務的穩定性和效能。在 ETL 的計算模型中,每個子任務完成會將結果寫入臨時目錄,等到整個任務完成後,把臨時目錄改名為正式目錄名即可。

這樣的改名操作在 HDFS 和其他檔案系統中是原子的,速度快,而且有事務性保證。但由於物件儲存沒有原生目錄結構,處理 rename 操作是一個模擬過程,會包含大量系統內部的資料拷貝,會耗時很多,而且沒有事務保證。

使用者在使用物件儲存時,常用檔案系統中的路徑寫法作為物件的 Key,比如 「/order/2-22/8/10/detail」。改名操作時,需要搜尋出所有 Key 中包含目錄名的物件,用新的目錄名作為 Key 複製所有的物件,此時會發生資料拷貝,效能會比檔案系統差很多,可能慢一兩個數量級,而且這個過程因為沒有事務保證,所以過程中有失敗的風險,造成資料不正確。這樣看起來很細節的差異對整個任務 pipeline 的效能和穩定性都會有影響。

物件儲存資料最終一致性的機制,會降低計算過程的穩定性和正確性。舉個例子,比如多個使用者端在一個路徑下並行建立檔案,這是呼叫 List API 得到的檔案列表可能並不能包含所有建立好的檔案列表,而是要等一段時間讓物件儲存的內部系統完成資料一致性同步。這樣的存取模式在 ETL 資料處理中經常用到,最終一致性可能會影響到資料的正確性和任務的穩定性。

為了解決物件儲存存在無法保持強資料一致性的問題。AWS 釋出過一個名為 EMRFS 的產品。AWS EMRFS 的做法是,因為知道 Listing 結果可能不對,所以另外準備一個 DynamoDB 資料庫, 比如 Spark 在寫檔案的時候,同時也寫一份檔案列表到 DynameDB 裡,再建立一個機制,不斷呼叫物件儲存的 List API,和資料庫裡面存下來的結果做比較,直到相等了再返回。但這個機制的穩定性不好,它會受物件儲存所在的區域的負載高低影響忽快忽慢,不是一個理想的解決方式。

除了上述由於檔案系統和物件儲存本身差異帶來的問題外,在物件儲存上使用 Hadoop 的另一大問題,就是物件儲存對於 Hadoop 元件的相容性相對弱。在文章開頭 Hadoop 架構介紹中提到了 HDFS 是 Hadoop 生態早期幾乎唯一的儲存選擇,上層各種各樣的元件都是面向 HDFS API 開發的。而到了物件儲存上,資料儲存的結構變了, API 也變了。

雲廠商為了能夠與現有的這些 Hadoop 元件適配,一方面需要去改造元件和雲物件儲存之間的 connector,另一方面還需要給上層的元件去打 patch ,對於每一個元件都一一的去驗證相容性,這對公有云廠商來說意味著巨大的工作量。所以,目前公有云它提供的巨量資料元件裡面能包含的計算元件是有是有限的,一般只能包含 Spark、 Hive、 Presto 三個常用元件,而且還只能包含少數幾個版本。這樣就會給將巨量資料平臺遷移上雲,或者有需要使用自己的發行版和元件需求的使用者帶來了挑戰。

企業如何能夠享受到物件儲存的強大效能,同時又兼顧檔案系統的準確性?

物件儲存 + JuiceFS

當用戶想在物件儲存上去進行復雜的資料計算、分析訓練這些場景的時候,物件儲存確實無法滿足企業的需求;這也是我們去做 JuiceFS 的一個出發點,希望能夠站在物件儲存之上去補充他不擅長的部分,與物件儲存一起以比較低廉的價格服務好密集性的資料計算、分析、訓練這些場景。

JuiceFS + 物件儲存是如何工作的呢?通過下圖 JuiceFS 在 Hadoop 叢集中的部署方式,簡單介紹原理。

從下面這個簡單的示意圖看到, YARN 管理的這些執行節點上,都帶一個 JuiceFS Hadoop SDK, 這個 SDK 可以保證完整相容 HDFS。圖片下方可以看到, SDK 它需要存取兩個部分,左側是 JuiceFS Meta Engine,右側是 S3 bucket。Metadata engine 就相當於 HDFS裡的 NameNode,整個檔案系統的後設資料資訊會儲存在這裡,後設資料資訊包括目錄數、檔名,許可權時間戳這些資訊,並且相應的解決掉了 HDFS NameNode 擴充套件性 、GC 這些的痛點。

另外一邊,資料存在 S3 bucket 裡面,這裡的 S3 bucket 等同於HDFS 中的 DataNode,可以將它看成一大堆海量的磁碟來用,它會管理好的資料儲存和副本的相關任務。JuiceFS 就是三個元件組成,JuiceFS Hadoop SDK, Metadata Engine 和 S3 Bucket。

相較於直接使用物件儲存, JuiceFS 還有哪些優勢呢?

  1. HDFS 100% 完整相容。這得益於我們最初完整相容 POSIX 的這個設計。POSIX API 的覆蓋程度以及複雜程度是大於 HDFS的,HDFS 在設計的時候就是去簡化了 POSIX,因為最先去實現複雜的 API 集,再去簡化它就變得非常容易了,所以這也是 JuiceFS 能實現 100%實現 HDFS 完整相容性的一個原因。

同時, 使用者可以和 HDFS 一起使用,無需完全替換 HDFS。這也得益於 Hadoop 系統的設計,在一個 Hadoop 叢集裡,可以設定多個檔案系統,JuiceFS 和 HDFS 可以同時使用,並不是互相替代的關係,而是可以互相合作。這樣的架構給我們我們現有的叢集帶來的好處是使用者不用完整替代現有的 HDFS 叢集,完整替代的工作量和風險上都太大了。使用者可以結合著業務,結合著叢集的情況,分步分批的去做融合。

  1. 後設資料效能強大,JuiceFS 將後設資料引擎獨立出來不再依賴於 S3 裡面的原資料效能,保證了後設資料的效能。使用 JuiceFS 的時候,對底層物件儲存的呼叫簡化到只是 get、 put、delete 這三個最基礎的操作,像 listing, update 等命令都用不到,在這樣的架構下,使用者就避開了物件儲存後設資料效能弱的問題,最終一致性這些問題也都不再存在了。

  2. 原子 rename, 因為有獨立的原資料引擎,JuiceFS 也可以支援原子 rename。

  3. 快取,有效提升熱資料的存取效能,提供了 data locality 特性。快取可以讓熱資料快取到執行器 worker 節點原生的一些磁碟空間上。有了快取後,會反覆存取的熱資料,不需要每次都通過網路去物件儲存裡面讀資料。而且 JuiceFS 特意實現了HDFS 特有的資料本地性的 API,讓所有支援資料本地性的上層元件都能重新獲得資料親和性的感知,這會讓 YARN 把自己的任務優先排程到已經建立快取的節點上面,綜合的效能可以和儲存計算耦合的 HDFS 相當的。

  4. 相容 POSIX, 與機器學習、AI 相關的任務應用結合方便。JuiceFS 還相容 POSIX,可以和機器學習, AI相關的這些業務更便捷地融合。

小結

伴隨著企業需求的更迭、基礎技術的發展,儲存和計算的架構在變,從最初的耦合到分離;實現存算分離方式多樣,各有利弊,從直接將 HDFS 部署到雲上,到使用公有云提供相容 Hadoop的方案,再到公有云 + JuiceFS 這樣的適合在雲上進行復雜巨量資料計算和儲存的方案。對於企業來說,沒有銀彈,結合自身需求做架構選型才是關鍵。

但無論選什麼,保持簡單都不會錯。

如有幫助的話歡迎關注我們專案 Juicedata/JuiceFS 喲! (0ᴗ0✿)