一面資料: Hadoop 遷移雲上架構設計與實踐

語言: CN / TW / HK

背景

一面資料創立於 2014 年,是一家領先的資料智慧解決方案提供商,通過解讀來自電商平臺和社交媒體渠道的海量資料,提供實時、全面的資料洞察。長期服務全球快消巨頭(寶潔、聯合利華、瑪氏等),獲得行業廣泛認可。公司與阿里、京東、位元組合作共建多個專案,旗下知乎資料專欄“資料冰山”擁有超30萬粉絲。一面所屬艾盛集團(Ascential)在倫敦證券交易所上市,在 120 多個國家為客戶提供本地化專業服務。

公司在 2016 年線下機房部署了 CDH 叢集,到 2021 年已儲存和處理 PB 級的資料。公司自創立以來一直保持每年翻一番的高增長,而比業務量增長更快的是 Hadoop 叢集的資料量。

在這幾年間,按 1 到 2 年規劃的硬體,往往因資料增長超出預期而在半年後不得不再次擴容。每次擴容週期可達到一個月,除了花費大量精力跟進行政和技術流程,業務端也不得不安排較多人日控制資料量。

為了降低運維難度,發展可持續擴張的大資料處理方案,我們從 2021 年 10 月份開始探索取代現有Hadoop 叢集的方案。當時提出了這些需求:

  • 上雲,彈性伸縮、靈活運維
  • 儲存計算分離
  • 儘量使用開源元件,避免雲廠商繫結
  • 儘量降低業務遷移工作量

最終選擇的方案是使用阿里雲 EMR + JuiceFS + 阿里雲 OSS 來搭建存算分離的大資料平臺,將雲下資料中心的業務逐步遷移上雲。截至目前(2022 年 7 月)整體遷移進度約 40%,計劃在 2022 年內完成全部業務的搬遷,屆時雲上 EMR 的資料量預計會超過單副本 1 PB.

技術選型

首先是決定使用哪家雲廠商。由於業務需求,AWS、Azure 和阿里雲都有在用,綜合考慮後認為阿里雲最適合,有這些因素:

  • 物理距離:阿里雲在我們線下機房同城有可用區,網路專線的延遲小,成本低
  • 開源元件齊全:阿里雲 EMR 上包含的開源元件很多很全,除了我們重度使用的 Hive、Impala、Spark、Hue,也能方便整合 Presto、Hudi、Iceberg 等。我們在調研時發現只有阿里雲 EMR 自帶了 Impala,AWS 和 Azure 要麼版本低,要麼要自己安裝部署。

阿里雲的 EMR 本身也有使用 JindoFS 的存算分離方案,但基於以下考慮,我們最終選擇了JuiceFS:

  1. JuiceFS 使用 Redis 和物件儲存為底層儲存,客戶端完全是無狀態的,可以在不同環境訪問同一個檔案系統,提高了方案的靈活性。而 JindoFS 元資料儲存在 EMR 叢集的本地硬碟,不便於維護、升級和遷移。
  2. JuiceFS 的儲存方案豐富,而且支援不同方案的線上遷移,提高了方案的可移植性。JindoFS 塊資料只支援 OSS.
  3. JuiceFS 以開源社群為基礎,支援所有公有云環境,方便後期擴充套件到多雲架構。

關於 JuiceFS

直接擷取官方文件的介紹:

JuiceFS 是一款面向雲原生設計的高效能共享檔案系統,在 Apache 2.0 開源協議下發布。提供完備的 POSIX 相容性,可將幾乎所有物件儲存接入本地作為海量本地磁碟使用,亦可同時在跨平臺、跨地區的不同主機上掛載讀寫。

JuiceFS 採用「資料」與「元資料」分離儲存的架構,從而實現檔案系統的分散式設計。使用 JuiceFS 儲存資料,資料本身會被持久化在物件儲存(例如,Amazon S3),相對應的元資料可以按需持久化在 Redis、MySQL、TiKV、SQLite 等多種資料庫中。

除了 POSIX 之外,JuiceFS 完整相容 HDFS SDK,與物件儲存結合使用可以完美替換 HDFS,實現儲存和計算分離。

實施過程

我們在 2021 年 10 月開始探索 Hadoop 的上雲方案;11 月做了大量調研和討論,基本確定方案內容;12 月和 2022 年 1 月春節前做了 PoC 測試,在春節後 3 月份開始搭建正式環境並安排遷移。為了避免導致業務中斷,整個遷移過程以相對較慢的節奏分階段執行,截至目前(2022 年 7 月)進度約 40%,計劃在 2022 年內完成整體的搬遷。 遷移完後,雲上的 EMR 叢集資料量預計會超過單副本 1 PB.

架構設計

做完技術選型之後,架構設計也能很快確定下來。考慮到除了 Hadoop 上雲之外,仍然有大部分業務會繼續保留在資料中心,所以整體實際上是個混合雲的架構。

部署和配置

  • 關於IDC-阿里雲專線:能提供專線服務的供應商很多,包括 IDC、阿里雲、運營商等,選擇的時候主要考慮線路質量、成本、施工週期等因素,最終我們選擇了IDC的方案。IDC 跟阿里雲有合作,很快就完成了專線的開通。這方面如果遇到問題,可以找 IDC 和阿里雲的支援。除專線租用成本,阿里雲也會收取下行(從阿里雲到 IDC)方向傳輸費用。專線兩端的內網 IP 完全互通,阿里雲和 IDC 兩側都需要一些路由配置。
  • 關於EMR Core/Task 節點型別的選擇:
  1. JuiceFS 可以使用本地硬碟做快取,能進一步減少 OSS 頻寬需求並提高 EMR 效能。更大的本地儲存空間,可以提供更高的快取命中率。
  2. 阿里雲本地 SSD 例項是較高性價比的 SSD 儲存方案(相對於雲盤),用作快取正合適。
  3. JuiceFS 社群版未支援分散式快取,意味著每一個節點都需要一個快取池,所以應該選用盡量大的節點。

基於以上考慮和配置對比,我們決定選用 ecs.i2.16xlarge,每個節點64 vCore、512GiB Memory、1.8T*8 SSD。

  • 關於 EMR 版本: 軟體方面,主要包括確定元件版本、開啟叢集、修改配置。我們機房使用的是 CDH 5.14,其中 Hadoop 版本是 2.6,阿里雲上最接近的版本是 EMR 3.38. 但調研時發現該版本的 Impala 和 Ranger 不相容(實際上我們機房使用的是 Sentry 做許可權管理,但 EMR 上沒有),最終經過評估對比,決定直接使用 EMR 5 的最新版,幾乎所有元件的大版本都做了升級(包含 Hadoop 3、Spark 3 和 Impala 3.4)。此外,使用外部 MySQL 作為 Hive Metastore、Hue、Ranger 的資料庫。

  • 關於 JuiceFS 配置: 基本參考JuiceFS官方文件《在 Hadoop 中通過 Java 客戶端訪問 JuiceFS》即可完成配置。另外我們也配置了這些引數:

  • 快取相關:其中最重要的是 juicefs.cache-dir 快取目錄。這個引數支援萬用字元,對多個硬碟的例項環境很友好,如設定為/mnt/disk*/juicefs-cache(需要手動建立目錄,或在EMR節點初始指令碼中建立),即用全部本地 SSD 作為快取。另外也要關注 juicefs.cache-sizejuicefs.free-space 兩個引數。

  • juicefs.push-gateway:設定一個 Prometheus Push Gateway,用於採集 JuiceFS Java 客戶端的指標。

  • juicefs.usersjuicefs.groups:分別設定為 JuiceFS 中的一個檔案(如jfs://emr/etc/usersjfs://emr/etc/groups),解決多個節點 uid 和 gid 可能不統一的問題。

  • 關於 Kafka Connect 使用 JuiceFS:

經過一些測試,確認 JuiceFS 可以完美應用於 Kafka Connect 的 HDFS Sink 外掛(我們把配置方式也補充到了官方文件)。相比使用 HDFS Sink 寫入HDFS,寫入 JuiceFS 需要增加或修改以下配置項:

  • 將 JuiceFS Java SDK 的 JAR 包釋出到 Kafka Connect 每一個節點的 HDFS Sink 外掛目錄。Confluent 平臺的外掛路徑是:/usr/share/java/confluentinc-kafka-connect-hdfs/lib
  • 編寫包含 JuiceFS 配置的 core-site.xml,釋出到 Kafka Connect 每一個節點的任意目錄。包括這些必須配置的專案:
fs.jfs.impl = io.juicefs.JuiceFileSystem

fs.AbstractFileSystem.jfs.impl = io.juicefs.JuiceFS

juicefs.meta = redis://:[email protected]:6379/1

請參見 JuiceFS Java SDK 的配置文件。

  • Kafka Connector 任務設定:
hadoop.conf.dir=<core-site.xml所在目錄>

store.url=jfs://<JuiceFS檔案系統名稱>/<路徑>

PoC

PoC 的目的是快速驗證方案的可行性,有幾個具體目標:

  • 驗證 EMR + JuiceFS + OSS 整體方案的可行性
  • 檢查 Hive、Impala、Spark、Ranger 等元件版本的相容性
  • 評估對比效能表現,用了 TPC-DS 的測試用例和部分內部真實業務場景,沒有非常精確的對比,但能滿足業務需求
  • 評估生產環境所需的節點例項型別和數量(算成本)
  • 探索資料同步方案
  • 探索驗證叢集與自研 ETL 平臺、Kafka Connect 等的整合方案

期間做了大量測試、文件調研、內外部(阿里雲 + JuiceFS 團隊)討論、原始碼理解、工具適配等工作,最終決定繼續推進。

資料同步

要遷移的資料包括兩部分:Hive Metastore 元資料以及 HDFS 上的檔案。由於不能中斷業務,採用存量同步 + 增量同步(雙寫)的方式進行遷移;資料同步完後需要進行一致性校驗。

存量同步

對於存量檔案同步,可以使用 JuiceFS 提供的功能完整的資料同步工具 sync 子命令 來實現高效遷移。JuiceFS sync 命令支援單節點和多機併發同步,實際使用時發現單節點開多執行緒即可打滿專線頻寬,CPU 和記憶體佔用低,效能表現非常不錯。

Hive Metastore 的資料同步則相對麻煩些:

  • 兩個 Hive 版本不一致,Metastore 的表結構有差異,因此無法直接使用 MySQL 的匯出匯入功能
  • 遷移後需要修改庫、表、分割槽儲存路徑(即 dbs 表的 DB_LOCATION_URIsds 表的 LOCATION

因此我們開發了一套指令碼工具,支援表和分割槽粒度的資料同步,使用起來很方便。

增量同步

增量資料主要來自兩個場景:Kafka Connect HDFS Sink 和 ETL 程式,我們採用了雙寫機制。

Kafka Connect 的 Sink 任務都複製一份即可,配置方式上文有介紹。ETL 任務統一在內部自研的低程式碼平臺上開發,底層使用 Airflow 進行排程。通常只需要把相關的 DAG 複製一份,修改叢集地址即可。實際遷移過程中,這一步遇到的問題最多,花了大量時間來解決。主要原因是 Spark、Impala、Hive 元件版本的差異導致任務出錯或資料不一致,需要修改業務程式碼。這些問題在 PoC 和早期的遷移中沒有覆蓋到,算是個教訓。

資料校驗

資料同步完後需要進行一致性校驗,分三層:

  • 檔案一致。在存量同步階段做校驗,通常的方式是用 checksum. 最初的 JuiceFS sync 命令不支援 checksum 機制,我們建議和討論後,JuiceFS 團隊很快就加上了該功能(issuepull request)。除了 checksum,也可考慮使用檔案屬性對比的方式:確保兩個檔案系統裡所有檔案的數量、修改時間、屬性一致。比 checksum 的可靠性稍弱,但更輕量快捷。
  • 元資料一致。有兩種思路:對比 Metastore 資料庫的資料,或對比 Hive 的 DDL 命令的結果。
  • 計算結果一致。即使用 Hive/Impala/Spark 跑一些查詢,對比兩邊的結果是否一致。一些可以參考的查詢:表/分割槽的行數、基於某個欄位的排序結果、數值欄位的最大/最小/平均值、業務中經常使用的統計聚合等。

資料校驗的功能也封裝到了腳本里,方便快速發現數據問題。

後續計劃

大致有幾個方向:

  • 繼續完成剩餘業務的上雲遷移
  • 探索 JuiceFS + OSS 的冷熱分級儲存策略。JuiceFS 的檔案在 OSS 上完全被打散,無法基於檔案級別做分級。目前的思路是將冷資料從 JuiceFS 遷移到 OSS 上,設定為歸檔儲存,修改 Hive 表或分割槽的 LOCATION,不影響使用。
  • 目前 JuiceFS 使用 Redis 作為元資料引擎,假如將來資料量增加,使用 Redis 有壓力的話可能考慮切換為 TiKV 或其他引擎。
  • 探索 EMR 的彈性計算例項,爭取能在滿足業務 SLA 的前提下降低使用成本

一手實戰經驗

在整個實施過程中陸陸續續踩了一些坑,積累了一些經驗,分享給大家做參考。

阿里雲 EMR 和元件相關

相容性

  • EMR 5 的 Hive 和 Spark 版本不相容,無法使用 Hive on Spark,可以把預設的引擎改成 Hive on Tez.
  • Impala 的 stats 資料從舊版同步到新版後,可能因為 IMPALA-10230 導致表無法查詢。解決方案是在同步元資料時,將 num_nulls=-1 的改成 num_nulls=0. 可能需要用到 CatalogObjects.thrift 檔案
  • 原叢集有少量 Textfile 格式的檔案用了 snappy 壓縮,新版 Impala 無法讀取,報錯 Snappy: RawUncompress failed,可能是 IMPALA-10005 導致的。規避方案是不要對 Textfile 檔案使用 snappy 壓縮。
  • Impala 3.4 相比 2.11 的 CONCAT_WS 函式行為有差異,老版本 CONCAT_WS('_', 'abc', NULL) 會返回 NULL,而新版本返回 'abc'.
  • Impala 3.4 對 SQL 中的保留關鍵字引用更嚴格,必須加上``. 其實一個好習慣是業務程式碼不要使用保留關鍵字。
  • PoC 或前期測試的覆蓋度儘可能完整,用真實的業務程式碼去跑。我們在 PoC 和早期遷移的業務中用到的元件特性比較少,基本都是最常用、保持相容的功能,因此比較順利。但在第二批遷移過程中就暴露出了很多問題,雖然最終都有解決,但花了很多額外的時間去做診斷和定位,打亂了節奏。

效能

  • EMR 5 的 Impala 3.4 打了 IMPALA-10695 這個補丁,支援對 oss://jfs://(本意是支援 JindoFS,但 JuiceFS 也預設使用 jfs 這個 scheme)設定獨立的 IO 執行緒數。在 EMR 控制檯上增加或修改 Impala 的配置項 num_oss_io_threads.
  • 阿里雲 OSS 有賬號級別的頻寬限制,預設 10Gbps,隨著業務規模上升容易成為瓶頸。可以與阿里雲溝通調整。

運維

  • EMR 可以關聯一個 Gateway 叢集,通常用來部署業務程式。如果要在 Gateway 上用 client 模式提交 Spark 任務,需要先將 Gateway 機器的 IP 加到 EMR 節點的 hosts 檔案。預設可以使用 cluster 模式。
  • EMR 5 會開啟一個 Spark ThriftServer,在 Hue 上可以直接寫 Spark SQL,用起來很方便。但預設配置有個坑,會寫大量日誌(路徑大概是 /mnt/disk1/log/spark/spark-hadoop-org.apache.spark.sql.hive.thriftserver.HiveThriftServer2-1-emr-header-1.cluster-xxxxxx.out),導致硬碟寫滿。解決方案有兩個:配置 log rotate 或把 spark.driver.extraJavaOptions 配置清空(阿里雲技術支援的建議)。

JuiceFS 相關

  • JuiceFS 需要每個節點上具有相同的 UID 和 GID,否則很容易出現許可權問題。有兩種實現方式:修改作業系統的使用者(比較適合新機器,沒有歷史包袱),或者在 JuiceFS 上維護一個使用者對映表。我們之前也分享過一篇 JuiceFS + HDFS 許可權問題定位,有詳細討論。通常需要維護對映的使用者有 impala, hive, hadoop 等。如果使用 Confluent Platform 搭建 Kafka Connect,也需要配置 cp-kafka-connect 使用者。
  • 使用預設的 JuiceFS IO 配置時,相同的寫查詢,Hive on Tez 和 Spark 都比 Impala 快很多(但在機房裡 Impala 更快)。最終發現將 juicefs.memory-size 從預設的 300 (MiB) 改成 1024 之後 Impala 的寫入效能有成倍的提升。
  • 在做 JuiceFS 的問題診斷和分析時,客戶端日誌很有用,需要注意 POSIX 和 Java SDK 的日誌是不一樣的,詳見 JuiceFS 故障診斷和分析 | JuiceFS Document Center
  • 注意監控 Redis 的空間用量,Redis 如果滿了,整個 JuiceFS 叢集無法寫入。
  • 使用 JuiceFS sync 把機房資料往雲上同步時,選擇在有 SSD 的機器上跑,獲得更好的效能。

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