eBay 基於 Apache Kyuubi 構建統一 Serverless Spark 網關的實踐

作者 | 王斐

本文來自 eBay 軟件工程師、Apache Kyuubi PPMC Member 王斐在 Apache SeaTunnel & Kyuubi 聯合 Meetup 的分享,介紹了 Apache Kyuubi(Incubating) 的基本架構和使用場景,eBay 基於自身的需求對 Kyuubi 所做的增強,以及如何基於 Kyuubi 構建 Unified & Serverless Spark Gateway。

Kyuubi 是什麼

首先介紹一下 Kyuubi。Kyuubi 是一個分佈式的 Thrift JDBC/ODBC server,支持多租戶和分佈式等特性,可以滿足企業內諸如 ETL、BI 報表等大數據場景的應用。項目由本站數帆發起,已經進入 Apache 基金會孵化,目前的主要方向是依託本身的架構設計,圍繞各類主流計算引擎,打造一個 Serverless SQL on Lakehouse 服務,目前支持的引擎有 Spark、Flink、Trino(也就是 Presto)。我今天的主題是圍繞 Kyuubi 和 Spark, 關於其它計算引擎這裡不再展開。

對於 Spark,Kyuubi 有 HiveServer2 的 API,支持 Spark 多租戶,然後以 Serverless 的方式運行。HiveServer2 是一個經典的 JDBC 服務,Spark 社區也有一個類似的服務叫做 Spark Thrift Server。這裡介紹一下 Spark Thrift Server 和 Kyuubi 的對比。

Spark Thrift Server 可以理解爲一個獨立運行的 Spark app,負責接收用戶的 SQL 請求, SQL 的編譯以及執行都會在這個 app 裡面去運行,當用戶的規模達到一定的級別,可能會有一個單點瓶頸。

對於 Kyuubi,我們可以看右邊這張圖,有一個紅色的用戶和一個藍色的用戶,他們分別有一個紅色的 Spark app 和一個藍色的 Spark app,他們的 SQL 請求進來之後,SQL 的編譯和執行都是在對應的 app 之上進行的,就是說 Kyuubi Server 只進行一次 SQL 請求的中轉,把 SQL 直接發送給背後的 Spark app。

對於 Spark Thrift Server 來講,它需要保存結果以及狀態信息,是有狀態的,所以不能支持 HA 和 LB。而 Kyuubi 不保存結果,幾乎是一個無狀態的服務,所以 Kyuubi 支持 HA 和 LB,我們可以增加 Kyuubi Server 的個數來滿足企業的需求。所以說 Kyuubi 是一個更好的 Spark SQL Gateway。

Kyuubi 的架構分爲兩層,一層是 Server 層,一層是 Engine 層。Server 層和 Engine 層都有一個服務發現層,Kyuubi Server 層的服務發現層用於隨機選擇一個 Kyuubi Server,Kyuubi Server 對於所有用戶來共享的。Kyuubi Engine 層的服務發現層對用戶來說是不可見的,它是用於 Kyuubi Server 去選擇對應的用戶的 Spark Engine,當一條用戶的請求進來之後,它會隨機選擇一個 Kyuubi Server,Kyuubi Server 會去 Engine 的服務發現層選擇一個 Engine,如果 Engine 不存在,它就會創建一個 Spark Engine,這個 Engine 啓動之後會向 Engine 的服務發現層去註冊,然後 Kyuubi Server 和 Engine 之間的再進行一個 Internal 的連接,所以說 Kyuubi Server 是所有用戶共享,Kyuubi Engine 是用戶之間資源隔離。

Kyuubi 支持一些 Engine 的共享級別,它是基於隔離和資源之間的平衡。在 eBay 我們主要使用到了 USER 和 CONNECTION 級別。首先對於 CONNECTION 級別,對於用戶的每次連接都會創造一個新的 app,也就是一個 Kyuubi Engine,適用於 ETL 場景,ETL 的 workload 比較高,需要一個獨立的 app 去執行;對於 USER 級別,我們可以看到這裡有兩個 user,一個叫 Tom,一個叫 Jerry,Tom 的兩個 client 連接 Kyuubi Server,會連接到同一個屬於 Tom 的 Kyuubi Engine,USER 級別適用於 ad-hoc 場景,就是對於同一個用戶所有的連接都會到同一個 Kyuubi Engine 去執行,而對 Jerry 的所有請求都會到 Jerry 的 Kyuubi Engine 去執行。

對 USER 共享級別 Kyuubi 做了一些加強,引入了一個 Engine POOL 的概念,就像編程裡面的線程池一樣,我們可以創建一個 Engine 的 pool,pool 裡面有編號,比如說這裡 Tom 創建了兩個 pool,叫做 pool-a 和 pool-b,編號爲 pool-a-0,pool-a-1,如果說在客戶端請求的時候直接指定這個 pool 的名字,Kyuubi server 會從這個 pool 裡面去隨機選擇一臺 Engine 執行;如果 Tom 在請求的時候不僅指定 pool 的名字,還指定了這個 Engine 在 pool 裡面的索引,比如說這裡指定 pool-b-0,Kyuubi Server 會從這個 pool-b 裡面選擇編號爲 0 的 Engine 去做計算。對應的參數爲kyuubi.engine.share.level.subdomain.

這在 eBay 裡面爲 BI 工具集成提供了很大的便利,因爲 eBay,每個分析師團隊可能用同一個賬號去執行數據分析,BI 工具會根據用戶的 IP 去創建一個 Kyuubi Engine,因爲每個分析師需要的參數配置可能是不一樣的,比如說他們的內存的配置是不一樣的,BI 工具就可以創建一個這樣的 engine pool,然後保存用戶的 IP 和所創建 Engine 索引的一個 mapping,然後在這個用戶的請求過來的時候,根據 BI 工具保存的 IP 映射關係,去找到該用戶所創建的 Engine,或者是說一個團隊裡面很多人使用一個 pool,可以預創建許多 Spark app,讓這一個組裡面的人可以隨機選擇一個 Engine 去做執行,這樣可以加大併發度。同時也可以作爲 USER 共享級別下面的一個標籤用於標註該引擎的用途,比如說我們可以給 beeline 場景和 java JDBC 應用使用場景創建 USER 共享級別下的不同 engine pool, 在不同使用場景下使用不同的 engine pool, 互相隔離。

前面提到了不同的 Engine 共享級別,有的是爲每個連接創建一個 Spark App,有的是爲一個用戶創建一個或者多個 Kyuubi Engine,你可能會擔心資源浪費,這裡講一下 Kyuubi Engine 對資源的動態管理。首先,一個 Kyuubi Engine,也就是一個 Spark app,分爲 Spark driver 和 Spark executor,對於 executor,Spark 本身就有一個 executor dynamic allocation 機制,它會根據當前 Spark job 的負載來決定是否向集羣申請更多的資源,或者是說將目前已申請的資源返還給集羣。所說我們在 Kyuubi Server 層加一些限制,比如強制打開這個 executor dynamic allocation,並且把在空閒時候最小的 executor 數量設爲 0,也就是說當一個 app 非常空閒的時候只有一個 driver 帶運行,避免浪費資源。除了 executor 層的動態回收機制,Kyuubi 爲 driver 層也加了資源回收機制。對於 CONNECTION 分享級別,Kyuubi Engine 只有在當前連接才使用,當連接關閉的時候 Spark driver 會直接被回收掉。對 USER 級別的共享,Kyuubi 有一個參數kyuubi.session.engine.idle.timeout

來控制 engine 的最長空閒時間,比如說我們將空閒時間設置爲 12 小時,如果 12 個小時之內都沒有請求連接到這個 Spark app 上,這個 Spark app 就會自動結束,避免資源浪費。

Kyuubi 的使用場景

下面講一下 Use Case。目前 Kyuubi 支持了 SQL 語言和 Scala 語言的執行,也可以把 SQL 和 Scala 寫在一起去跑。因爲 SQL 是一種非常用戶友好的語言,它可以讓你不用瞭解 Spark 內部的原理,就可以使用簡單的 SQL 語句去查詢數據,但是它也有一定的侷限性;而 Scala 語言需要一定的門檻,但它非常的靈活,我們可以去寫代碼或者去操縱一些 Spark DataFrame API。

舉一個例子,就是可以在一個 SQL 文件或者一個 notebook 裡面去混合編程。首先用 SQL 語句創建了一張訓練的數據表,在創建表之後通過 SET 語句把語言模式設爲 Scala,然後開始用 Scala 去寫代碼,這裡是用一個 kMeans 把訓練數據進行處理,處理完之後把輸出保存在一張表裡面,再把語言模式切換到 SQL,繼續用 SQL 去處理。這樣非常方便,我們可以結合 SQL、Scala 的優點,基本上可以解決數據分析裡面的大部分的 case。我們也在 Kyuubi JDBC 裡面提供了一個非常友好的接口,可以直接調用KyuubiStatement::ExecuteScala去執行 Scala 語句。

Kyuubi 在 eBay 的實踐

eBay 需求背景

我們 Hadoop team 管理了很多個 Hadoop 集羣,這些集羣分佈在不同的數據中心,有不同的用途,有一套統一的基於 KDC 和 LDAP 的權限校驗。

剛開始引入 Kyuubi 的時候,我們就在想要爲每個集羣都部署一個 Kyuubi 服務嗎?如果這樣我們可能要部署三四個 Kyuubi 服務,在升級的時候需要進行重複操作,管理起來很不方便,所以我們就想是否能夠用一套 Kyuubi 來服務多個 Hadoop 集羣。

eBay 對 Kyuubi 的增強

下圖就是我們爲這個需求所做的一些增強。首先,因爲我們是支持 KDC 和 LDAP 認證的,我們就讓 Kyuubi 同時支持 Kerberos 和 Plain 類型的權限認證,並對 Kyuubi Engine 的啓動、Kyuubi 的 Beeline 做了些優化,然後我們擴展了一些 Kyuubi 的 thrift API 支持上傳下載數據。針對前面說的要用一個 Kyuubi 去訪問多個 Hadoop 集羣,我們加了一個 cluster selector 的概念,可以在連接的時候指定參數,讓請求路由到對應的集羣。還有就是我們也在完善 RESTfull API,已經爲 Kyuubi 支持了 SPNEGO 和 BASIC 的 RESTfull API 權限認證。此外我們也在做 RESTfull API 去跑 SQL Query 和 Batch job 的一些工作。圖中打編號的是已經回饋社區的一些 PR。

這裡講一下 2、3、4,對 Kyuubi 的 Thrift RPC 的一些優化。首先因爲 Thrift RPC 本身是針對 HiveServer2 來設計的,HiveServer2/Spark Thriftserver2 裡面建立一個連接是非常快的。而在 Kyuubi 裡面建立一個連接的話,首先要連接到 Kyuubi Server,Kyuubi Server 要等到和遠端的 Kyuubi Engine 建立連接完成之後,才能把結果返回給客戶端。

如果 Kyuubi Engine 一開始不存在,而且在啓動 Kyuubi Engine 的時候由於資源問題,或者是有一些參數設置不對,比如說他設置了無效的spark.yarn.queu,導致出現錯誤的話,中間可能會有一分鐘或者說幾十秒的延遲,客戶端要一直等,在等的過程中也沒有任何的 log 返回給客戶端。我們就針對這個做了一些異步的 OpenSession,將 OpenSession 分爲兩部分,第一步是連接到 Kyuubi Server,Kyuubi Server 再異步啓動一個 LaunchEngine Operation,之後立即把 Kyuubi Server 連接給客戶端,這樣客戶端可以做到一秒鐘就可以連接到 Kyuubi Server。但是他的下條語句以及進來之後,會一直等到這個 Engine 初始化好之後纔開始運行。其實這也是我們的 PM 的一個需求,即使第一條語句運行時間長一點也沒關係,但是連接是一定要很快,所以我們就做了這樣一個工作。

因爲 Hive Thrift RPC 是一個廣泛應用而且非常用戶友好的 RPC,所以我們在不破壞它的兼容性的情況下基於 Thrift RPC 做了一些擴展。首先對於 ExecuteStatement 這種請求及返回結果,它會在 API 裡面返回一個 OperationHandle,再根據 OperationHandle 獲取當前 Operation 的狀態和日誌。因爲我們前面已經把 OpenSession 拆成了 OpenSession 加上一個 LaunchEngine Operation,所以我們想把 LaunchEngine Operation 的一些信息,通過 OpenSession request 這個 configuration map 把它返回去,我們是把一個 OperationHandler 分爲兩部分,一部分是 guid,另一部分是 secret,放到 OpenSessionResp 的 configuration Map 裡面。

然後在拿到 OpenSessionResp 之後就可以根據這個 configuration 拼出 Launch Engine Operation 對應的 OperationHandler,然後再根據它去拿這個 LaunchEngine 的日誌和狀態。

下面是一個效果,我們在建立 Kyuubi 連接的時候可以實時知道spark-submit的過程中到底發生了什麼。比如說用戶將 spark.yarn.queue 設置錯了,或者說由於資源問題一直在等待,都可以清楚的知道這中間發生了什麼,不需要找平臺維護人員去看日誌,這樣既讓用戶感到極爲友好,也減少了平臺維護人員的 efforts。

構建 Unified & Serverless Spark Gateway

前面說到要用一個 Kyuubi 服務來服務多個集羣,我們就基於 Kyuubi 構建了一個 Unified & Serverless Spark Gateway。Unified 是說我們只有一個 endpoint,我們是部署在 Kubernetes 之上的,使用 Kubernetes 的 LB 作爲 Kyuubi Server 的服務發現,endpoint 的形式就是一個 Kubernetes 的 LB 加上一個端口,比如說kyuubi.k8s-lb.ebay.com:10009,要服務多個集羣,我們只需要在 JDBC URL 裡面加上一個參數kyuubi.session.cluster,指定 cluster name,就可以讓他的請求到指定的集羣去執行。關於權限校驗我們也是用 Unified 的,同時支持 Kerberos 和 LDAP 權限校驗。關於 functions(功能)也是 Unified 的,同時支持 Spark-SQL、Spark-Scala 以及 ETL Spark Job 的提交。

關於 Serverless, Kyuubi Server 部署在 Kubernetes 之上,是 Cloud-native 的,而且 Kyuubi Server 支持 HA 和 LB,Kyuubi Engine 支持多租戶,所以對於平臺維護人員來說成本非常低。

這是我們大概的一個部署,對於多集羣我們引入了 Cluster Selector 的概念,爲每個集羣都配了一個 Kubernetes ConfigMap 文件,在這個文件裡面有這個集羣所獨有的一些配置,比如這個集羣的 ZooKeeper 的配置,集羣的環境變量,會在啓動 Kyuubi Engine 的時候注入到啓動的進程裡面。

每個集羣的 super user 的配置也是不一樣的,所以我們也支持了對各個集羣進行 super user 的校驗。目前 Kyuubi 支持 HadoopFSDelegation token 和 HiveDelegation token 的刷新,可以讓 Spark Engine 在沒有 keytab 的情況下去長運行,而不用擔心 token 過期的問題。我們也讓這個功能支持了多集羣。

用戶一個請求進來的過程是這樣的:首先他要指定一個 Cluster Selector,Kyuubi Server(on Kubernetes)根據這個 Selector 去找到對應的集羣,連接集羣的 ZooKeeper,然後查找在 ZooKeeper 裡面有沒有對應的 Spark app, 如果沒有就提交一個 app 到 YARN 上面(Spark on YARN),Engine 在啓動之後會向 ZooKeeper 註冊,Kyuubi Server 和 Kyuubi Engine 通過 ZooKeeper 找到 Host 和 Port 並創建連接。

Kyuubi 從一開始就支持 Thrift/JDBC/ODBC API, 目前社區也在完善 RESTFul API. eBay 也在做一些完善 RESTFul API 的工作,我們給 RESTful API 加了權限校驗的支持,同時支持 SPNEGO(Kerberos) 和 BASIC(基於密碼) 的權限校驗。我們計劃給 Kyuubi 增加更多的 RESTful API。目前已有的是關於 sessions 的 API,比如可以關掉 session,主要用於來管理 Kyuubi 的 sessions。我們準備加一些針對 SQL 以及 Batch Job 的 API。關於 SQL 就是可以直接通 RESTful API 提交一條 SQL Query,然後可以拿到它的結果以及 log。關於 Batch Job 就是可以通過 RESTful API 提交一個普通的使用 JAR 來運行的 Spark app,然後可以拿到 Spark app 的 ApplicationId,還有 spark-submit 的 log,這樣可以讓用戶更加方便地使用 Kyuubi 完成各種常用的 Spark 操作。

eBay 的收益

對用戶來說,他們能夠非常方便地使用 Spark 服務,可以使用 Thrift、JDBC、ODBC、RESTful 的接口,它也是非常輕量的,不需要去安裝 Hadoop/Spark binary,也不需要管理 Hadoop 和 Spark 的 Conf,只需要用 RESTful 或者 Beeline/JDBC 的形式去連接就好。

對我們平臺開發團隊來說,我們有了一箇中心化的 Spark 服務,可以提供 SQL、Scala 的服務,也可以提供 spark-submit 的服務,我們可以方便地管理 Spark 版本,不需要將 Spark 安裝包分發給用戶去使用,可以更好地完成灰度升級,讓 Spark 版本對於用戶透明化,讓整個集羣使用最新的 Spark,也節省集羣的資源,節省公司的成本。此外,平臺的維護也是比較方便的,成本很低,因爲我們只需要維護一個 Kyuubi 服務來服務多個 Hadoop 集羣。

目前,除了 eBay,不僅國內有很多公司在使用 Kyuubi,國外也有公司在使用這個項目。

作者:

王斐,eBay 軟件工程師,Apache Kyuubi PPMC Member

附視頻回放及 PPT 下載:

https://www.slidestalk.com/SeaTunnel/Apache_Kyuubi_____eBay__________________?video

Kyuubi 主頁:

https://kyuubi.apache.org/

Kyuubi 倉庫:

https://github.com/apache/incubator-kyuubi

活動推薦

長期徵集|尋找中國卓越技術團隊

2022 年第一季即將上線,本期精選了包括騰訊雲鼎實驗室、優麒麟、PingCAP、西門子 Mendix、火山引擎 ByteHouse、搜狗輸入法無障礙產品在內的優秀團隊,敬請關注。同時,訪談錄現開放長期報名通道,如果你身處傳統企業經歷了數字化轉型變革,或者正在互聯網公司進行創新技術的研發,並希望 InfoQ 可以關注和採訪你所在的技術團隊,就請抓住機會吧!