業務增加400%,Uber如何快準穩擴容HDFS集羣?

做者 | Ang Zhang , Wei Yan
編譯 | 李瑞豐
編輯 | Emily Chen

三年前,Uber 採用 Hadoop 做爲大數據分析系統中海量存儲(HDFS)和並行計算(YARN)的底層架構方案。隨着業務的發展,Uber 不斷對這套系統的穩定性、可用性以及用戶體驗進行了持續的改善。數據庫

Uber 使用 Hadoop 的場景有不少,包括批處理和流式計算。其涵蓋的業務場景有反欺詐、機器學習,以及到達時間預估系統等等。隨着 Uber 業務在近幾年的發展,其 Hadoop 集羣數據存儲容量大小和訪問頻次都在以指數級別的方式增加。僅在 2017 年,HDFS 存儲數據量就增加了 400% 以上。微信

而完成一個兼顧擴展性和高性能的架構設計,絕對不是一件容易的事情。爲了達到這個目標, Uber 的數據架構團隊對整個架構進行了完全仔細的檢查和分析,將目標拆分爲幾個大的方向去進行優化。具體方向包括: View File System (ViewFs),快速的 HDFS 版本升級,NameNode 垃圾回收 調優,限制系統中小文件數量,提供了 HDFS 加載管理服務,以及增長了一個 NameNode 的只讀副本。接下來咱們針對上述內容進行一個詳細介紹,看 Uber 是如何構建一個快速增加的、穩定的,而且可靠的存儲系統的。架構

更多幹貨內容請關注微信公衆號「AI 前線」,(ID:ai-front)負載均衡

擴容的挑戰

HDFS 的設計初衷是一個支持快速擴展的分佈式文件系統,可以在一個集羣內支持上千個節點。只要硬件充足,將存儲系統的容量擴展到 100 PB 以上應該是很容易,而且能快速實現的。運維

可是對於 Uber 來講,每週有數千個業務方用戶查詢(經過 hive 或者 presto)Uber 的數據系統。隨着業務的高速發展,Uber 既想快速擴容,又不想影響正常的查詢性能,實際上是一件很不容易的事情。機器學習

在 Uber 的系統中 ,Presto 用戶佔到了一半以上,而且 90% 的 Presto 查詢要持續 100s 以上。若是 HDFS 系統過載,這樣就會致使 Presto 查詢在隊列中堆積,最終使得查詢延遲。這種狀況下,Uber 須要解決的問題是,如何用最快速度準備好一個查詢結果相關的數據。分佈式

在原來的數據架構中,Uber 設計了 ETL 系統,在每一個集羣內應用來支持用戶的查詢。ETL 包括數據的提取、轉換和加載。這樣在同一個集羣當中,能夠減小由複製致使的延遲。但這種方式存在一個反作用,就是會產生大量的小文件,而小文件也是阻塞查詢隊列的重要緣由。ide

除了上述問題以外,還須要注意到大部分的業務團隊在進行數據訪問和使用的時候,所涉及的數據是現有數據的絕大部分。這樣致使 Uber 很難經過按照用戶場景或者組織架構層面去作集羣的拆分的方式,來下降業務快速增加給集羣帶來的壓力。工具

而致使 Uber 不能實現快速擴容又不下降用戶體驗的主要瓶頸,實際上是在 NameNode 這個節點的吞吐量上。NameNode 是整個集羣的文件索引,索引中記錄了數據文件在集羣中的位置。客戶端要想訪問 HDFS 集羣來獲取數據的話,第一步必需要先訪問 NameNode。更糟糕的是,在 NameNode 中是經過一個 ReadWriteLock 來實現對元數據區域的讀寫控制(爲了保證數據一致性,譯者注)。這使得讀寫 NameNode 的性能更加糟糕,由於每次寫操做都須要搶佔這個鎖,而且強制其餘請求在隊列中等待。oop

在 2016 年年底,Uber 分析了高請求量下(RPC 請求)請求的響應時間,並整理以下圖。從圖中能夠看到,偶爾有請求的執行時間超過了 500ms,最長的甚至都到了接近 1s。這意味着每一個 HDFS 請求都要在隊列中等待至少半秒的時間。而正常請求的響應時間一般只有 10ms。

圖 1:NameNode RPC 請求平均時間能超過半秒。

擴容 & 性能調優

Uber 對 HDFS 系統的目標是在能支持高性能查詢的同時,也能作到隨着業務增加,快速對 HDFS 集羣進行擴容。爲了實現這個目標,Uber 團隊設計了一些方案,可以在短時間內避免上述問題。與此同時,這些解決方案中也包含了在中長期計劃當中如何支持系統的高可用和水平擴展能力,從而應對業務的快速發展。

下面就列舉了 Uber 團隊是如何作到在 HDFS 容量實現了 400% 增加的前提下,還能保證整個系統的高性能和高可用。

使用 ViewFs 實現水平擴展

Uber 受到 Twitter 的啓發,採用了 View File System (ViewFs) 的方案來實現對多個物理 HDFS 集羣的管理。而且多個 HDFS 集羣都經過掛載的方式接入 ViewFs,這樣在外部使用者看來,就像一個系統同樣。

爲了實現拆分,Uber 參考 YARN 以及 Presto 集羣相關操做分佈的狀況,將底層 HBase 進行了拆分。拆分以後效果明顯,除了大幅下降了主集羣的負載以外,還使得 HBase 更加穩定,而且集羣重啓時間也由原來的數個小時縮短到了幾分鐘。

Uber 還特地爲 YARN 的日誌收集 單獨搭建了一套 HDFS 集羣。 YARN-3269 的日誌收集處理以後,能夠被用來優化 ViewFs。Uber 的 Hive scratch directory 也放到了這個集羣當中。目前看來,這種方式的效果很棒:新的集羣支撐了大概 40% 左右的寫請求,而且將幾乎所有的小文件的寫都轉移到了該集羣,從而幫助主集羣緩解其自身的壓力。除此以外,這個遷移很是順暢,由於對現有的用戶應用幾乎沒有任何影響。

最終,Uber 選用了多個 HDFS 集羣的方案,而沒有采納 Hadoop 官方提供的 HDFS Federation。這種方案能夠幫助 Uber 儘量地規避了採用單一的 HDFS 集羣這種方案的潛在風險(如宕機)。此外,物理層面的隔離還提升了整個系統的可靠性。不過這種方案有一個缺點,就是會增長 HDFS 集羣運維的複雜度。

圖 2:Uber 的 HDFS 部署方案:基於多數據中心的 ViewFs

HDFS 升級

Uber 遇到的第二個問題,就是如何解決 HDFS 集羣在大規模環境下作版本升級的問題。Uber 在一年以內完成了 HDFS 兩個主要版本的升級,分別是從 CDH 5.7.2(存在大量補丁一個版本)到 Apache 2.7.3,以後又升級到了 2.8.2 版本。爲了完成快速的升級,Uber 沒有采用第三方集羣管理工具,而是基於 Puppet 以及 Jenkins 構建了本身的部署系統。

版本升級帶給 Uber 生產環境中大規模 HDFS 集羣的好處是顯而易見的,主要包括:HDFS-9710, HDFS-9198, 以及 HDFS-9412。舉個例子,升級到 Apache 2.7.3 版本以後,上報的增量塊數量明顯減小,從而使 NameNode 負載顯著下降。

升級 HDFS 的風險仍是比較大的,可能會致使集羣宕機、性能降低,甚至於數據丟失。爲了解決這些隱患, Uber 在將其生產環境升級到 2.8.2 以前,花費了數月個的時間去進行相關驗證。但就算如此,Uber 在升級的過程當中,在其最大的 HDFS 集羣上仍是碰到了一個意料以外的 BUG HDFS-12800,而且這個 BUG 是在上線一段時間以後才發現。此時 Uber 的集羣隔離、穩定的升級步驟,以及降級回滾策略幫了大忙,讓 Uber 能將這個問題的影響降到最低。

能同時支持不一樣版本的 YARN 和 HDFS 在同一個集羣中同時運行,對擴容來講也是一個很是關鍵的點。YARN 和 HDFS 都是 Hadoop 的一部分,一般來說他們都是一塊兒進行升級的。可是 YARN 的大版本升級一般須要好久的時間來驗證,多是由於 YARN 的 API 接口發生變化,或者 JAR 依賴變化後致使衝突的發生。Uber 不想讓 HDFS 的升級過程受到 YARN 升級的干擾。爲了解決這個問題,Uber 會在升級 YARN 的同時,運行一箇舊版本的、可以很好支持現有應用的 YARN(須要注意的是,當使用了 Erasure Coding 這樣的特性的時候,這種方式可能就不生效了。由於這個特性須要客戶端也作出改動)。

NameNode 垃圾回收 調優

垃圾回收(GC)調優在 Uber 的平常優化中起到了很重要的做用。Uber 經過 GC 調優,能爲存儲集羣的擴容節省出很大的空間。

Uber 採用 CMS 垃圾回收器來進行垃圾回收,而且有針對性的調整了一些參數的值,如 CMSInitiatingOccupancyFraction,UseCMSInitiatingOccupancyOnly 以及 CMSParallelRemarkEnabled 來優化老年代的垃圾回收效果。這些參數可以經過提升對 CPU 的利用率的方式,更快的完成垃圾回收操做。

當遠程調用 (RPC)負載很高的時候,大量的短暫存活的對象會在年輕代建立。這會致使年輕代的垃圾回收器頻繁觸發 stop-the-world collection 操做,來進行垃圾回收。Uber 經過將年輕代的堆大小從 1.5GB 調整到 16GB 以及將 ParGCCardsPerStrideChunk 大小指定爲 32768 這種方式,將 NameNode GC 的 STW 總時間佔比從 13% 優化到了 1.7%,總體的吞吐量提高了 10% 之多。性能測試結果代表,只讀場景下性能提高更多。

下面列出了 JVM 中針對 GC 調優涉及到的一些參數(NameNode 堆大小是 160GB 的狀況下):

  • XX:+UnlockDiagnosticVMOptions

  • XX:ParGCCardsPerStrideChunk=32768 -XX:+UseParNewGC

  • XX:+UseConcMarkSweepGC -XX:+CMSConcurrentMTEnabled

  • XX:CMSInitiatingOccupancyFraction=40

  • XX:+UseCMSInitiatingOccupancyOnly

  • XX:+CMSParallelRemarkEnabled -XX:+UseCondCardMark

  • XX:+DisableExplicitGC

Uber 也在嘗試驗證 G1 垃圾回收器的效果,來決定是否要在系統中採用 G1 完成垃圾回收。不過以前的測試中還沒有發現 G1 相比 CMS 而言有特別明顯的優點。Uber 表示也會持續關注新版本 JVM 中 G1 垃圾回收器性能的提高,極可能接下來就須要從新審視垃圾回收器的選擇了。

圖 3:增長年輕代大小(從 1.5GB 調整到 16GB)和優化 ParGCCardsPerStrideChunk 參數與 GC 暫停時間之間的關係展現(從 13% 到 1.7%)。

控制小文件的數量

NameNode 會加載全部的元數據到內存當中,因此若是系統中存在不少的小文件,對 NameNode 的壓力就會特別大。此外,訪問相同數據量的狀況下,小文件過多還會致使 client 發起的讀文件 RPC 請求過多(寫入小文件的時候也是這樣)。針對這種狀況,Uber 主要採用了兩種方式來進行優化:

首先,Uber 數據團隊構建了一個新版本的數據管道(基於 Hoodie 庫),這樣會比原生的數據管道建立更少的小文件。做爲切換到新數據管道以前的一個過渡方案,Uber 數據團隊也提供了一個工具(內部稱之爲 stitcher)來對現有的小文件進行合併,保證大部分文件都在 1GB 大小之上。

其次,Uber 嚴格限定了 Hive 數據庫和應用的目錄所在的空間的配額。爲了實現這一點,Uber 維護了一個管理工具,能夠幫助用戶管理本身應用所在的空間目錄的配額。默認每一個文件都是 256MB 大小,用戶能夠經過 Hadoop 團隊提供的優化建議和工具來合併相應的文件,從而更合理的組織管理本身應用的文件。例如,經過調整 Hive auto-merge 配置和 reducers 的數量,能夠顯著改善由 Hive 插入 - 重寫 操做產生的小文件。

HDFS 負載管理服務

運維一個大規模、多租戶的基礎架構系統,好比 HDFS,有一個很大的挑戰就是發現系統突增負載的來源,而且能快速解決其帶來的問題。Uber 團隊構建了一個叫作 Spotlight 的 HDFS 內部負載的管理服務。

在 Spotlight 現有的實現當中,NameNode 會輸出一份審計日誌,而後經過 Flink 和 Kafka 進行實時處理。審計日誌分析結果能夠經過系統面板進行查看,而且自動禁用那些致使 HDFS 性能降低的帳戶,或者直接幹掉有問題的工做流。

圖 4:Spotlight 系統使得 Uber 能發現並處理致使 HDFS 性能出現問題的帳戶。

觀察者(Observer) NameNode

Uber 正在致力於開發一個做爲觀察者(Observer)的 NameNode(HDFS-12975)。這是 HDFS 的一個新特性,設計的初衷是經過提供一個只讀的 NameNode 副本的方式,來減小當前 Active NameNode 上的負載壓力。由於 Uber 目前主要的 HDFS RPC 壓力來自於 Presto 查詢發起的只讀請求,因此 Uber 指望能經過 Observer 來提高 NameNode 的吞吐量。Observer 目前正處於驗證階段,Uber 也開始着手準備將其遷移到生產環境當中。

圖 5:Uber 設計的採用了 Observer NameNode 以後, HDFS 的高可用架構方案。

關鍵措施

Uber 總結了本身在 HDFS 架構方面的一些最佳實踐(其餘一樣採用 HDFS 的公司或者組織一樣可能面臨的一些問題),具體以下:

  • 分期規劃解決方案:像 Observer NameNode 和 拆分 HDFS 集羣這種解決方案,屬於長期規劃;而短時間的、臨時的解決方案,好比 GC 調優以及合併小文件,能夠爲長期規劃提供緩衝的時間。兩類方案結合規劃施行,更加合理。

  • 小文件對 HDFS 是一個很大的隱患,關於小文件的治理,越早開始越好。團隊能夠經過提供工具、文檔,以及對用戶培訓的方式,在大規模的、多租戶的 HDFS 集羣中來儘量地避免小文件的產生。

  • 積極參與到社區當中:Hadoop 已經發行了有 10 年多的時間了,它的社區也比以前活躍了不少。每一個新的版本中,都會引入社區中的一些可擴展性和功能性方面的新特性。參與到 Hadoop 社區當中,提供本身的工具或者介紹本身的發現,也會對你本身的基礎架構有很大幫助。

快速發展

儘管 Uber 在過去的幾年當中取得了很大的進步,可是接下來爲了提高 HDFS 基礎架構的穩定性,還有不少的工做要作。

例如,Uber 在接下來計劃集成幾個新的服務到他們的存儲系統當中,就像圖 6 中展現的同樣。這些新服務會幫助 Uber 後續的基礎架構擴展,而且使 Uber 的存儲系統更加易用。

圖 6:Uber 將來的 HDFS 基礎架構。這套架構中新增的部分,會幫助 Uber 存儲系統的發展更加順利和穩定。

下面就着重介紹下兩個主要的項目:基於路由的 HDFS 集羣 和分層存儲:

基於路由的 HDFS 聯邦集羣

Uber 目前遇到系統負載壓力的時候,採用 ViewFs 的方式來進擴容。這種方式遇到的主要問題是,每次 ViewFs 存在掛載點的變更時,都要更新客戶端的配置。而且當出現配置問題的時候,更新操做很難在生產環境作到應用無感知的回滾。這也是咱們目前只對那些客戶端較少的應用採用這種方案來進行擴容的緣由,例如 YARN log 的收集。

Microsoft 在 HDFS 2.9 版本中提供的新特性:基於路由方案的聯邦集羣(HDFS-10467, HDFS-12615),就是對現有 ViewFs 架構的一個很好的補充。這種方案在集羣層面增長了一個由軟件管理的中心化的 HDFS 命名空間。這種方案提供的接口跟以前的 WebHDFS 接口是互相兼容的,可讓用戶的應用無需任何修改就能完成子集羣之間的互相訪問,同時還能獨立維護本身的數據。

經過在各集羣之上提供一個負載均衡工具,聯邦集羣(federation)能支持在子集羣以前的數據無縫遷移,從而下降某個子集羣上的負載,還可以實現分層存儲。聯邦集羣層會記錄當前全局命名空間的狀態,而且能夠同時啓用多個路由器來將用戶請求合理映射到不一樣的子集羣當中。

Uber 目前正在積極致力於將基於路由的 HDFS 聯邦集羣解決方案落地到本身的生產環境當中,而且幫助 Hadoop 社區改進相關開源代碼,包括 WebHDFS support。

分層存儲

隨着存儲量的不斷增長,Uber 認爲減小存儲的花費也是頗有必要的。最近 Uber 技術團隊研究發現,用戶主要的數據訪問集中在最近產生的數據(熱數據),而一些歷史數據(冷數據)的訪問頻次是很低的。因此頗有必要經過將歷史數據遷移到一個獨立的且資源不敏感的存儲當中以大幅減小存儲方面的開銷。對於這種方案,HDFS Erasure Coding、Router-based Federation、高密度存儲硬件(大於 250TB),以及數據遷移服務是其中的關鍵因素。Uber 計劃在接下來的文章中分析相關的分層存儲經驗。

查看英文原文:https://eng.uber.com/scaling-hdfs/

相關文章
相關標籤/搜索