多點DMALL × Apache Kyuubi:構建統一SQL Proxy探索實踐

網易數帆發表於2022-11-25
伴隨著國家產業升級的推進和雲原生技術成熟,多點 DMALL 大資料技術也經歷了從存算一體到存算分離的架構調整變遷。本文將從引入 Kyuubi 實現統一 SQL Proxy 的角度講述這一探索實踐的歷程。

多點 DMALL 成立於2015年,提供一站式全渠道數字零售解決方案 DMALL OS,目前已與130+連鎖零售企業、近1000家品牌達成合作,覆蓋5個國家和地區。作為一站式全渠道數字零售解決方案服務商,多點 DMALL 透過數字化解構重構零售產業,提供端到端的商業 SaaS。方案整體涵蓋了從商品選擇、供應商引入、倉儲供應鏈管理、門店經營、使用者精準營銷的整個產業鏈,實現了人人、事事、物物線上。

Apache Kyuubi 是網易數帆發起開源的一個分散式和多租戶閘道器,用於在 Lakehouse 上提供 Serverless SQL,社群目前已聚集海內外百餘名貢獻者。

作為 DMALL OS 數字化能力的技術底座,大資料平臺歷經多次迭代平穩支撐了公司 To B 業務的開展。伴隨著國家產業升級的推進和雲原生技術成熟,多點 DMALL 大資料技術也經歷了從存算一體到存算分離的架構調整變遷。本文將從引入 Kyuubi 實現統一 SQL Proxy 的角度講述這一探索實踐的歷程。

技術背景

行業技術發展趨勢

第一代的大資料技術主要採用傳統 Hadoop 開源技術棧,幾乎所有的企業搭建大資料叢集都是從 HDFS、Hive 和 YARN 開始的。除了 Apache 開源元件組合外,許多企業也會選用 CDH、HDP 等商業發行套件,方便運維和部署。隨著技術的成熟,傳統的 Hadoop 技術開始暴露一些缺點:

1. 部署運維成本高

Hadoop 生態元件所需基礎硬體資源較多,組建叢集時需要慎重設計和調整,部署成本高,後期運維複雜度高。

2. 叢集隔離,資源浪費

大資料叢集部署時常常需要單獨申請機器,與業務服務叢集資源隔離。總體來看,兩個叢集的潮汐現象都存在,大資料叢集白天較為空閒,業務服務叢集夜間使用者使用量低,隔離部署的方式會產生較大資源浪費。

3. 儲存成本持續升高

大資料叢集的資料量是一直增長的。縱使企業做了冷熱分離的設計、定時歸檔的處理,也只是減緩了增長趨勢,依舊無法解決儲存成本一直升高的現狀。當然,除了上述缺點,還有 NameNode 後設資料壓力過大、元件版本相互制約等問題。這些缺點對於企業內部使用尚且可以最佳化處理,但是作為一家需要為客戶提供定製化服務的企業,尤其面對日益突出的“效能”、“成本”、“安全”、“穩定性”等核心問題,多點 DMALL 意識到必須做出改變。

To B 的大資料平臺建設趨勢

To B 的企業商業邏輯和 To C 的差別很大,在大資料領域的差別更大。

1. 精細的成本管理

To C 的大資料平臺使用者多是企業內部員工。使用大資料平臺產生的成本單獨計算,個人使用者不會為這些資源消耗直接買單。但在 To B 模式下,使用者基本都來自B端企業客戶,客戶要為其資源使用付費。這就意味著作為服務提供商需要精細化成本,盡最大可能降低成本才能獲得客戶的認可。

2. 更低的使用門檻

企業內部使用的大資料平臺,員工背景可控。To B 的大資料平臺面對的環境要複雜很多。客戶當前的資訊化程度、員工技能等都不相同,大資料平臺需要提供更多方式、更低門檻來幫助客戶的使用。

3. 更快的交付效率

需要進一步簡化技術架構和實現,採用標準化的手段小時級交付新環境,節省時間同時進一步降低後續運維難度。這樣以後逐漸形成快速規模化複製的能力,輸出零售資料中臺解決方案。

多點 DMALL的大資料技術發展

上述技術背景和商業背景的發展,對多點 DMALL 的大資料技術基座持續提出更高的要求。在持續最佳化升級的過程中,多點 DMALL 的大資料叢集架構經歷了從傳統Hadoop生態邁向雲原生,從存算一體到存算分離的架構變遷。

接下來,我們將詳細講述這一變遷的背景及其設計,以及 Kyuubi 在其中承擔的角色和最佳化實踐。

存算一體架構下的即席查詢最佳化

架構背景

與其他大部分企業一樣,多點 DMALL 最開始採用的是傳統 Hadoop 技術棧構建資料中臺 UniData,強依賴 CDH 發行版,採用 IDC 物理機/雲主機叢集部署。在即席查詢中,我們採用自研的統一安全閘道器UniSQL連線使用者和底層引擎。UniSQL 可為使用者提供安全校驗、血緣採集、SQL 審計、限流、白名單、查詢降級、查詢結果限制等平臺管控級功能,最初只支援對接 Impala 和 Hive 查詢引擎。很明顯基於查詢速度的優勢,平臺使用者會優先選擇 Impala 作為查詢引擎。隨著平臺的推廣和使用者量的增加,我們發現這樣的架構存在不少問題。

1. Impala 資源緊張

Impala 的資源緊張現象越來越頻繁,因其本身資源隔離較為複雜,也沒有 HiveServer2 提供的原生查詢條件控制等,使得整體可用性降低。雖然我們在最佳化中透過平臺層面補救進行部分 SQL 的攔截,但是依然無法完全阻擋部分佔用大量資源的 SQL 執行。

2. Hive 查詢速度不佳

相比於 Impala,Hive 查詢速度不佳,使用者體驗較差。一方面,MapReduce 任務會產生大量臨時小檔案,影響運維人員的判斷;另一方面,MapReduce 任務報錯常常很隱晦,使用者從 Hive 執行日誌中無法直接獲取,需要運維人員去 YARN 管理頁面搜尋查詢詳細資訊,帶來較大的運維成本。

3. YARN 叢集潮汐現象嚴重

如圖所示,YARN 所在的叢集是為大資料獨立搭建的基於 IDC/雲主機的叢集。實踐中發現,YARN 叢集的潮汐現象非常明顯,凌晨時分伴隨著大量離線任務的啟動,叢集資源非常緊張;白天任務量很少,Impala 的大量使用意味著 Hive 查詢量少,叢集總體使用量較為空閒。基於上述的原因,我們考慮引入 SparkSQL 作為即席查詢的另一個可選項,在Impala資源緊張時,將Impala的查詢資源引匯入 YARN 叢集中,同時減少 Hive 的查詢以提升使用者整體的查詢速率。選型過程中 Kyuubi 進入了我們的視線。

### 架構設計

事實上,這不是我們第一次調研 Kyuubi。早在 Kyuubi 正式開源時,我們便嘗試引入Kyuubi 作為多租戶代理工具,正巧趕上當時公司技術發展方向的優先順序調整,這一嘗試暫時擱置。後來在需求的驅動下,我們開始重新引入 Kyuubi,這時 Kyuubi 已經到了1.5.2版本。現行的架構已經穩定在多個叢集執行,為了儘可能減少架構調整,保證引入過程中叢集其他任務及服務的穩定性,最終引入 Kyuubi 後即席查詢涉及的總體架構圖如下:

### 最佳化探索

線上環境中已經有 UniSQL 作為統一安全閘道器,Kyuubi 所承擔的角色僅聚焦於多租戶的 Spark SQL 代理。為了適配其他底層元件等原因,我們對 Kyuubi 進行了部分調整:

1. 在現有的 CDH 環境下,為了匹配 Sentry 的許可權管控和 YARN 的資源排程,我們修改了 Kyuubi 提交 Spark 的執行命令,不使用 proxy-user-queue 的引數,直接切換使用者提交;

2. 由於安全元件使用 Sentry 進行許可權管控的,在網上也沒有開源產品可以提供SparkSQL 基於 Sentry 的校驗,我們只好單獨適配實現了針對 Sentry 的 Spark Extension;

3. 基於 Kyuubi 提供的 kyuubi.engine.initialize.sql 引數,將我們原來為 Hive 和Impala 提供的 UDF 遷移到 SparkSQL 服務中,Kyuubi 在初始化 Spark 時便自動載入對應的 jar 包和 Function,使用者切換引擎時使用無感知;

4. 為了方便使用者從 Impala 切換到 Spark SQL 查詢,我們定製化支援了部分 SQL 語法,例如 invalidate metadata 進行後設資料主動重新整理等;在使用者的使用中,我們也進行了一系列引數最佳化。例如設定 spark.files.overwrite=true方便部分使用者重複提交 jar 包以及測試自定義 UDF 函式;設定 spark.sql.autoBroadcastJoinThreshold=-1 關閉自動 Broadcast 以提高穩定性等。

### 遺留問題

在原有架構中,我們以最簡單的方式引入 Kyuubi,為客戶提供了另一種查詢引擎的可能,但是這樣的引入很明視訊記憶體在其短板:

1. JDBC 鏈路太長

使用者從發起的 JDBC 連線到最終開始執行 SQL,中間需要分別經過 UniSQL 和 Kyuubi服務。多個連線池的管理提升了總體的連線管理複雜度,在報錯時更是不易分析查詢。有賴於 Kyuubi 和 UniSQL 的穩定,在實際使用中暫未發現因多個連線池連線導致的嚴重影響,但風險和隱患依舊存在。

2. SQL Proxy 角色重疊

同為 SQL Proxy,UniSQL 和 Kyuubi 的角色存在重疊,很明顯這樣冗餘的設計並不利於未來架構的發展。

3. Spark 版本不統一

基於歷史原因,現有架構中我們為使用者提供的是 Spark 2.4.6和 Spark 2.3.1版本,但是Kyuubi 是需要搭載 Spark 3.x版本。引入 Spark SQL 查詢,又不能貿然將之前的版本“一刀切”,我們只好暫時單獨為 Kyuubi 部署 Spark3.x 版本。這就造成現在叢集中存在不統一的 Spark 版本,增加運維的難度。

4. Zookeeper 版本不統一

我們使用的 CDH 發行版版本為5.16.2,搭載版本為3.4.5的 Zookeeper。Kyuubi 的 HA 同樣需要依賴 ZooKeeper,但是經過我們測試,使用3.4.5的 Zookeeper 會出現刪除節點失敗的情況,從而導致 Kyuubi Server 自動關閉。經過分析,這一 BUG 在更高版本中修復完畢。最終,我們只好單獨部署3.6.1版本的 Zookeeper 供 Kyuubi 使用。

5. Sentry 許可權與 Spark 的對接不易

CDH 限定了叢集的許可權管理服務為 Sentry。眾所周知,Sentry 的設計架構簡單,缺失更細粒度的許可權控制,包括檢視、行、列等。而且 Spark 沒有原生支援 Sentry 的許可權管控方式。雖然我們開發了針對 Sentry 的 Spark Extension 進行許可權管控,更細粒度的管控依舊是我們很頭疼的地方。

存算分離架構下的統一 SQL Proxy 實踐

架構背景

隨著多點 DMALL 全面 To B 轉型,為越來越多的B端客戶提供零售全渠道解決方案,需要具備在多雲部署環境下提供更具價效比、可複用的大資料底層基座和平臺工具鏈。我們也終於等到了一個契機,徹底甩開歷史包袱,設計搭建存算分離、輕量級、可擴充套件、雲中立大資料叢集架構。

幸運的是,Spark on K8s 的技術趨於成熟,我們得以全面擁抱雲原生。在 K8s 的基座上,我們合併了大資料計算叢集和業務服務叢集,甩開 CDH 的版本限制,剝離 Hadoop 生態的強依賴,充分利用物件儲存,簡化運維和部署,降低使用者使用成本。

考慮到上一版架構中的部分遺留問題,在升級架構設計中,經過詳細的調研、分析和選型,Kyuubi 也正式替換 UniSQL 作為 SQL Proxy,負責多套查詢引擎的統一代理服務。

升級架構設計

在升級架構中,我們充分考慮到之前版本不一致的情況,合併並簡化了技術選型,使整體架構更輕量級和可擴充套件:

實踐方案

使用 Kyuubi 作為統一 SQL Proxy,那麼 Kyuubi 就需要滿足之前 UniSQL 為代表的代理服務的全部輔助功能,包括審計日誌、實時監控、限流、動態引數修改、許可權管控等。因此我們深入學習了社群的實踐經驗和最優推薦,根據場景和需求,完善了 Kyuubi 的服務。以下為我們部分實踐結果。

  • SQL審計

作為統一代理層,需要對所有 SQL 和連線進行詳細的記錄,包括使用者、SQL、執行時間、執行結果等。這樣的資料並非實時使用,而是用於後續回溯查詢問題,或者進行使用者操作習慣的統計分析。幸運的是,Kyuubi 提供了 EventHandler 的方式,收集了非常完整的資訊。在 Kyuubi1.6.1中,支援使用下列引數將相關記錄落在本地日誌中:

kyuubi.backend.server.event.json.log.path
kyuubi.backend.server.event.loggers

本地日誌並不方便統計分析,我們需要更靈活的方式。最終我們選擇仿照原生JsonLoggingEventHandler類,繼承EventHandler自定義將所需日誌寫入 Kafka 中。後續再透過大資料平臺的功能,落地到 Hive 或其他引擎中用於回溯分析。

  • 實時監控

當前即席查詢的使用情況如何、連線數量、失敗原因等,都需要實時監控檢視。Kyuubi 提供了非常完整的指標監控能力,我們也複用內部運維平臺的監控體系,選用了 Prometheus + Grafana 作為實時監控的載體。

相關引數:

kyuubi.metrics.reporters
  • 限流

不同平臺和使用者的使用方式不同,容易導致連線數爆發,從而影響 Kyuubi 服務的穩定性。因此,限流是必須要納入考慮的。Kyuubi 提供了部分引數進行限流:

kyuubi.server.limit.connections.per.user
kyuubi.server.limit.connections.per.ipaddress

雖然這些引數限制到了單使用者的連線數,甚至單使用者單 IP 的連線數,但是這些引數是對所有使用者統一的,而且 Kyuubi 服務啟動後不能動態修改。就我們的使用經驗來看,支援針對不同使用者動態設定不同的連線限制將是一種更靈活的方式。但當前的功能也能基本滿足限流管控要求,最終決定針對這個場景我們不做程式碼修改,期待社群未來能夠提供更精細的管控功能。

  • 引數動態傳遞

線上環境缺乏合適的方式進行引數傳遞,這個遺憾在新的架構中得以彌補。透過 Kyuubi提供的自定義動態引數傳遞方式,實現SessionConfAdvisor類後對接配置中心。引數將在每次啟動Session的時候動態拉取,也能較好的滿足不同使用者不同資源分配的場景:

kyuubi.session.conf.advisor

當然,為了減少對不同環境引數的自定義jarb包管理,我們調整了傳入該類的引數,新增了KyuubiConf,這樣不同環境部署的 Kyuubi 自動可以傳入對應的配置中心路徑等資訊,自定義的 jar 包也可以環境無關,減少程式碼運維的複雜度。

  • 基於 Ranger 的許可權校驗

甩開了 CDH 對於 Sentry 的強制要求,新的架構選用 Ranger 進行許可權管控。因此可以直接使用 Kyuubi 提供的 auth-extension 進行許可權校驗。不過為了提升整體的鑑權效率,我們重新設計了鑑權體系,將鑑權能力集中在新的鑑權服務 RAC 中,避免每一個 Spark 任務都在 Driver 本地落地一份 Ranger 許可權策略。在這樣的思路下,對於 Kyuubi 提供的 auth-extension 也做了適配性的修改。不過這不影響這個工具的便捷性:

spark.sql.extensions=org.apache.kyuubi.plugin.spark.authz.rangerRangerSparkExtension

只不過,就社群的更新速度來看,auth-extension 更新頻率很高,我們在測試中發現了一些問題,包括檢視許可權、臨時 Function 許可權等問題,修復後都還沒來及脫敏貢獻給社群,社群就在 master 分支修復釋出了。實話說,這也導致我們的這個外掛更新非常痛苦,我們需要不斷修改,不斷適配。這也是我們越來越擁抱社群的原因,只要社群提供了功能,經過測試後我們便刪除自定義程式碼,方便未來更新,也是儘可能跟著社群一起成長吧。

  • 自動合併小檔案

Kyuubi 提供的自動合併小檔案工具是純開箱即用的,Spark3.3 版本主要依靠 rebalance 邏輯。經過我們的測試,同樣的來源表,同樣寫出10個分割槽,寫出檔案從近2000個縮小到10個,落地時間也從7min 縮小到40s,不僅降低了叢集後設資料的壓力,更有效提升了整體任務效率。

spark.sql.extensions=org.apache.kyuubi.sql.KyuubiSparkSQLExtension
  • 查詢條件限制和掃描分割槽數限制

即席查詢中,絕大部分探索性的查詢都不需要大量的資料,結果資料量過大會引起 Driver 的壓力過大,從而影響查詢的穩定性以及使用者的體驗。社群最新提供了 HTTP 的非同步獲取的方式,但是這樣的方式涉及到上下游改動,還在效能測試中。

按照線上環境的經驗,我們將結果條數限制為1000條,針對不同的場景,這個引數也可以動態修改,基本滿足返回結果要求。

spark.sql.watchdog.forcedMaxOutputRows

Hive 支援限制查詢 SQL 中分割槽表必須帶分割槽條件的嚴格模式,Kyuubi 也提供了類似的功能,可支援控制掃描分割槽數的限制。只是控制掃描分割槽數對於不同場景的需要不同,我們還沒有正式利用這個功能,需要在實踐中動態新增引數使用。

spark.sql.watchdog.maxPartitions
  • 服務日誌切分

之前運維經驗發現,Kyuubi 服務在 logs 下寫入的服務日誌一直增量寫入,發現的時候單檔案日誌已經達到了 90G。新架構下我們吸取教訓,修改了 log4j2.properties,將日誌按大小和日期拆分,僅保留最近7天的日誌,徹底解決這個隱患。

  • 自由選擇資源組

在公司的實際使用中,一個 user 是可以在多個 group 中的,按照 Kyuubi 原生的提供的以 group 為引擎隔離 level 的情況下,會預設取該使用者的第一個 group。我們將其修改成了透過 JDBC 引數可以選擇 group 的能力,當然也包括了安全性校驗,不允許惡意使用不屬於自己的 group。

  • Volcano 排程器的模板檔案

Spark 官方文件中針對 Volcano 的排程器使用引數如下:

spark.kubernetes.scheduler.name=volcano
spark.kubernetes.driver.pod.featureSteps=org.apache.spark.deploy.k8s.features.VolcanoFeatureStep
spark.kubernetes.executor.pod.featureSteps=org.apache.spark.deploy.k8s.features.VolcanoFeatureStep
spark.kubernetes.scheduler.volcano.podGroupTemplateFile=podgroup-template.yaml

其中,最後這個引數 spark.kubernetes.scheduler.volcano.podGroupTemplateFile 需要是本地的 yaml 檔案。我們針對這個修改了提交命令,支援動態生成模板檔案進行提交。

下一步探索方向

我們也在根據原始架構中使用者的使用反饋,持續最佳化升級版架構的設計,不斷調整打造存算分離、輕量級、可擴充套件、雲中立的大資料叢集。

根據使用反饋,我們下一步將重點關注以下最佳化點:

1. 單點引擎穩定性不佳,當一個 SQL 將該引擎查掛後,下次再查詢會先報錯後啟動新的引擎。這對於使用者的使用不太友好。經過研究社群裡其他企業的分享,下一步我們將關注資源池模式和引擎高可用的設計,以提升使用者的使用友好度。

2. 精細化成本管控是升級架構的重中之重,但是原生的 Spark SQL 無法將資源拆分到SQL 粒度。這裡也是我們下一步需要重點突破的內容,為使用者提供清晰的成本資料。

3. 大資料基座上除了我們提供的大資料平臺產品外,常有其他業務系統申請直接使用大資料相關資源,最典型的就是 JDBC 連結。白名單將是我們需要重點實現的功能。除了使用者登入校驗外,還能驗證來源平臺,對使用來源做出限制。

總結來看,我們從 Kyuubi 正式開源時開始關注,到如今的1.6.1版本,越來越欣喜地發現,每次升級都會有新的功能補充和思考。我們也在升級中逐漸減少自定義程式碼,使用 Kyuubi 社群提供的新特性。我們會持續跟蹤社群動態,並將實踐經驗反哺社群,參與推動 Kyuubi 的發展。

相關文章