一面數據: Hadoop 遷移雲上架構設計與實踐

  • 2022 年 7 月 28 日
  • 筆記

背景

一面數據創立於 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✿)