從 Hadoop 到雲原生, 大數據平台如何做存算分離

  • 2022 年 9 月 14 日
  • 筆記

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✿)