魏文定,鄂海紅,王 曦,宋美娜,宿興輝
(1.北京郵電大學(xué) 計算機學(xué)院(國家示范性軟件學(xué)院),北京 100876;2.中國科學(xué)院信息工程研究所,北京 100085;3.聯(lián)洋國融(北京)科技有限公司,北京 100088)
隨著全球數(shù)據(jù)產(chǎn)業(yè)[1]的蓬勃發(fā)展,數(shù)據(jù)系統(tǒng)正扮演著關(guān)鍵的支撐和促進(jìn)角色。隨著大數(shù)據(jù)行業(yè)的發(fā)展,開發(fā)人員所需要參考和分析的海量數(shù)據(jù)日益龐大,對于大數(shù)據(jù)組件的依賴要求也越來越高,對數(shù)據(jù)計算和存儲提出了新的需求。
數(shù)據(jù)倉庫和數(shù)據(jù)湖[2-5]是兩種比較有代表性的大數(shù)據(jù)產(chǎn)品,數(shù)據(jù)倉庫其核心特征是面向主題、集成性、穩(wěn)定性,幫助企業(yè)完成數(shù)據(jù)價值提取,缺點就是計算和存儲高度耦合,而且無法存儲和查詢非結(jié)構(gòu)化文件[6]。隨之?dāng)?shù)據(jù)湖技術(shù)誕生,數(shù)據(jù)湖的核心是提供一個容納所有形式的存儲集[7-16]。
最初,要使用數(shù)據(jù)湖平臺,企業(yè)需要購買至少十幾臺服務(wù)器,然后尋找專業(yè)人員安裝每個大數(shù)據(jù)組件。安裝完成后,還需要建立開發(fā)平臺、運維平臺,并購買各種工具。這種建設(shè)成本、使用門檻以及決策風(fēng)險都比較高。
隨后,傳統(tǒng)的數(shù)據(jù)湖平臺開始逐漸暴露出了一些短板和不利因素:
(1)多個部門共享同一個集群,缺乏資源隔離和限制,導(dǎo)致相互之間產(chǎn)生影響。
(2)系統(tǒng)部署依賴復(fù)雜的手動操作,且運維成本較高。
(3)服務(wù)器在不定因素下出現(xiàn)故障,使得某個數(shù)據(jù)湖組件下線以致整個數(shù)據(jù)湖資源不可正常使用。
(4)缺乏標(biāo)準(zhǔn)的大數(shù)據(jù)組件發(fā)布流程,無法培養(yǎng)客戶的自主數(shù)據(jù)能力。
(5)計算資源和存儲資源之間存在著高度耦合關(guān)系,導(dǎo)致整個數(shù)據(jù)湖平臺難以延展。
目前,有些廠商開發(fā)的數(shù)據(jù)湖平臺是基于其自有的調(diào)度系統(tǒng)和分布式計算體系構(gòu)建的。盡管它們正在進(jìn)行Kubernetes改造,將部分調(diào)度工作遷移到Kubernetes上,但是絕大部分組件仍然基于原有的大數(shù)據(jù)平臺架構(gòu)運行,因此它們還沒有真正實現(xiàn)云原生架構(gòu)下的數(shù)據(jù)湖平臺。
另外,對于數(shù)據(jù)入湖,缺乏完備的管理體系,數(shù)據(jù)湖會逐步形成沼澤湖,導(dǎo)致用戶無法從中提取數(shù)據(jù)價值。同時,在數(shù)據(jù)處理層面上,入湖任務(wù)和云原生計算引擎之間缺乏完整的橋梁,容易導(dǎo)致數(shù)據(jù)無法寫入數(shù)據(jù)湖或?qū)懭胝_的位置。
云原生是一種新興的應(yīng)用程序開發(fā)和部署方法,它基于云計算、容器化、微服務(wù)和自動化等技術(shù),旨在幫助開發(fā)人員和企業(yè)更加高效地構(gòu)建、交付和運維應(yīng)用程序。針對以上問題,結(jié)合云原生技術(shù),對于數(shù)據(jù)湖組件進(jìn)行改造并且完全實現(xiàn)云原生化,建立入湖任務(wù)和計算引擎之間的關(guān)系,就變得更加有必要了。
整體架構(gòu)如圖1所示,從上往下依次為業(yè)務(wù)層、業(yè)務(wù)支撐層、容器層、運行環(huán)境層和存儲層。

圖1 整體架構(gòu)
業(yè)務(wù)層由數(shù)據(jù)湖云原生化和入湖管理組成。數(shù)據(jù)湖云原生化服務(wù)提供可視化界面供用戶完成數(shù)據(jù)湖組件容器資源信息定義以及數(shù)據(jù)湖構(gòu)建任務(wù),在云原生環(huán)境中搭建數(shù)據(jù)湖平臺。入湖管理負(fù)責(zé)外部數(shù)據(jù)源與數(shù)據(jù)湖信息對接,形成入湖預(yù)處理信息,并向后端提交任務(wù)。
業(yè)務(wù)支撐層對上層任務(wù)調(diào)度進(jìn)行統(tǒng)一管理,支撐數(shù)據(jù)湖云原生服務(wù)發(fā)布,實現(xiàn)入湖作業(yè)對計算引擎的調(diào)度。提供統(tǒng)一Catalog接口,讓不同計算引擎共享元數(shù)據(jù),保證數(shù)據(jù)訪問方式統(tǒng)一。
在容器層,數(shù)據(jù)湖云原生化之后以容器形式存在,主要分為數(shù)據(jù)處理服務(wù)、元數(shù)據(jù)服務(wù)、監(jiān)控服務(wù)和鑒權(quán)服務(wù)。數(shù)據(jù)處理服務(wù)包括計算引擎(例如Spark)和分析引擎(例如Trino),支撐數(shù)據(jù)湖計算工作。元數(shù)據(jù)服務(wù)維護(hù)數(shù)據(jù)湖元數(shù)據(jù)信息。監(jiān)控服務(wù)由Prometheus和Grafana組件組成,用于監(jiān)控Pod健康狀態(tài)。鑒權(quán)服務(wù)基于RBAC(Role-Based Access Control)完成對容器授權(quán)。
在運行環(huán)境層,Kubernetes與Docker共同構(gòu)成云原生環(huán)境,為容器提供可運行環(huán)境。采取Docker對數(shù)據(jù)湖組件進(jìn)行鏡像化,目前Docker與Kubernetes具有很好的兼容性,促使數(shù)據(jù)湖組件能夠在Kubernetes環(huán)境下容器化,基于云原生的特性在云原生環(huán)境上運行更多的容器,有助于發(fā)揮數(shù)據(jù)湖的可擴充性。
在存儲層,維護(hù)數(shù)據(jù)湖數(shù)據(jù)內(nèi)容,以Iceberg表格式將數(shù)據(jù)寫入文件系統(tǒng),構(gòu)建JuiceFS分布式文件系統(tǒng)為數(shù)據(jù)存取提速,并提供統(tǒng)一存儲訪問接口。
為實現(xiàn)數(shù)據(jù)湖部署流程化,需要用戶完成數(shù)據(jù)湖組件容器信息定義以及組件之間的關(guān)聯(lián),提交Json請求之后,由后端完成業(yè)務(wù)邏輯組織,最終實現(xiàn)數(shù)據(jù)湖云原生化,實現(xiàn)細(xì)節(jié)如圖2所示。數(shù)據(jù)湖變成服務(wù)資源之前,需要經(jīng)過一系列化模塊組織。在容器信息定義時,系統(tǒng)接收資源構(gòu)建Json請求,并解析Json提取容器定義信息,再將信息持久化到數(shù)據(jù)庫,同時對不存在鏡像由鏡像定義器完成構(gòu)建。啟動過程由數(shù)據(jù)湖啟動器進(jìn)行接管,實現(xiàn)數(shù)據(jù)湖的自動化部署。而模板引擎生成Yaml文件流,最后調(diào)用Kubernetes將Yaml配置跟相關(guān)鏡像組合完成數(shù)據(jù)湖部署工作。

圖2 數(shù)據(jù)湖云原生化流程設(shè)計
計算引擎在數(shù)據(jù)湖功能系統(tǒng)中起著承上啟下的作用,而對象存儲承擔(dān)著數(shù)據(jù)存儲重要角色。但計算引擎與對象存儲之間存在著兼容性差問題,并且對象存儲元數(shù)據(jù)操作性能低。S3fs,Goofys與JuiceFS相比,不支持本地緩存,性能遠(yuǎn)低于JuiceFS。因而在計算引擎與對象存儲之間引入中間件JuiceFS,JuiceFS是一款面向云原生設(shè)計的高性能分布式文件系統(tǒng)。同時JuiceFS支持多協(xié)議兼容,如POSIX,HDFS和S3協(xié)議。在一定程度上能夠提升系統(tǒng)的讀寫性能和可擴充能力。
由于該文采用云原生技術(shù)構(gòu)建數(shù)據(jù)湖,能夠輕量化數(shù)據(jù)湖服務(wù)調(diào)度,更好地發(fā)揮數(shù)據(jù)湖的效力。為使容器編排技術(shù)、計算引擎、對象存儲更好地結(jié)合,需要通過存算分離降低技術(shù)連接復(fù)雜性。借助Kubernetes抽象存儲接口(CSI)以及存儲卷掛載技術(shù)(PV和PVC),將JuiceFS以PV的方式掛載到容器持久卷,如圖3所示,以達(dá)到存算分離效果,進(jìn)一步實現(xiàn)容器編排技術(shù)、計算引擎、對象存儲之間的高效協(xié)作。

圖3 數(shù)據(jù)湖存算分離方案
如圖4所示,在容器編排系統(tǒng)安裝juicefs csi driver,完成對存儲服務(wù)的綁定,在容器環(huán)境內(nèi)形成高性能、簡單易用的共享文件系統(tǒng)。在環(huán)境內(nèi)初始化StorageClass和PVC后,只需在Pod聲明PVC,即可完成Pod對存儲卷的掛載。

圖4 數(shù)據(jù)湖組件Pod與文件系統(tǒng)連接流程
借助中間件JuiceFS以及容器編排系統(tǒng)動態(tài)掛載持久卷技術(shù),實現(xiàn)了計算和存儲的分離。對于不同組件Pod只需聲明已初始化PVC,即可完成在文件系統(tǒng)的存儲空間分配,同時容器編排系統(tǒng)自身的容錯機制,即使Pod宕機之后再恢復(fù)健康狀態(tài),Pod仍能恢復(fù)對原有持久卷的掛載,進(jìn)一步保證數(shù)據(jù)湖系統(tǒng)運作的可靠性。
數(shù)據(jù)湖整體由關(guān)系數(shù)據(jù)庫、對象存儲、元數(shù)據(jù)庫和計算引擎組成。而組件之間、組件與依賴包之間存在著版本依賴關(guān)系,例如Hive MetaStore及Spark與Iceberg之間存在著版本上關(guān)系。為減少組件版本適配的試錯成本,在數(shù)據(jù)湖資源上云之前,對于部分鏡像可以提前進(jìn)行創(chuàng)建。構(gòu)建流程如下:①準(zhǔn)備數(shù)據(jù)湖組件依賴文件和腳本文件。②編寫Dockerfile文件。③由docker build完成鏡像生成。④對鏡像進(jìn)行標(biāo)簽化并上傳至鏡像倉庫以備調(diào)用。
借助提前定制化鏡像模型雖能很好地滿足系統(tǒng)功能需要,但在數(shù)據(jù)湖組件升級過程中,需要人為進(jìn)行干預(yù),導(dǎo)致鏡像生成依然過度依賴于人工。為解決這項功能的不足,采取數(shù)據(jù)湖鏡像定義新思路,其過程如圖5所示。

圖5 數(shù)據(jù)湖鏡像生成過程
在數(shù)據(jù)湖云原生化時,由鏡像定義器對鏡像資源進(jìn)行檢查,若檢查鏡像倉庫中不存在適配鏡像資源時,發(fā)起創(chuàng)建鏡像過程。根據(jù)當(dāng)前組件類型,檢索關(guān)系數(shù)據(jù)庫中當(dāng)前組件所依賴的jar包以及版本信息,從文件管理中心獲取所需的jar包依賴,并基于jar版本信息構(gòu)建臨時Dockerfile文件。后由鏡像定義器主動發(fā)起構(gòu)建鏡像請求,從私有倉庫中獲取各類基礎(chǔ)鏡像,并將所依賴的jar包COPY至鏡像容器內(nèi),鏡像構(gòu)建完成后將鏡像進(jìn)行標(biāo)簽化,由鏡像定義器遠(yuǎn)程控制Docker將鏡像上傳至鏡像倉庫以備后續(xù)調(diào)用。
在使用Kubernetes對資源進(jìn)行部署過程中,對每類資源都需要重復(fù)編寫大量配置文件,在系統(tǒng)移植和資源重新構(gòu)建過程中,對于開發(fā)用戶不太友好。因而,將每類部署文件進(jìn)行定制模板,借助模板引擎屏蔽底層技術(shù)的實現(xiàn)細(xì)節(jié),簡化用戶對數(shù)據(jù)湖部署的操作,實現(xiàn)簡單易用原則。
模板引擎需要解決兩個問題,分別是模板文件的定義和模板屬性的賦值。
(1)模板文件的定義:在Kubernetes環(huán)境中包含著Deployment,Service,ConfigMap等多種資源對象,然而這些資源對象的屬性存在一些不同的定義,同時,數(shù)據(jù)湖每類組件的部署方式也不同,所以無法通過一種通用模板來完成數(shù)據(jù)湖的部署工作。
(2)模板屬性值轉(zhuǎn)換:由于交互層采用界面化操作模式,除了需要完成對用戶輸入數(shù)據(jù)校驗性工作,還需完成輸入?yún)?shù)與可變屬性值的映射關(guān)系。
為對模板文件進(jìn)行合理化組織,采用模板文件分層設(shè)計,如圖6所示。將不同組件按照類型進(jìn)行區(qū)分,在每類組件下定義通用的Kubernetes資源對象模板。

圖6 模板分層設(shè)計
在用戶提交數(shù)據(jù)湖組件創(chuàng)建請求之后,后端獲取已被持久化的組件資源定義信息,進(jìn)一步獲取組件類型,同時將持久化信息轉(zhuǎn)化為Set(組件資源定義參數(shù)集合),再由模板引擎根據(jù)組件類型調(diào)用相應(yīng)類型模板,然后將Set反轉(zhuǎn)形成模板可變屬性值,最終構(gòu)建Yaml文件流,如圖7所示。

圖7 Yaml文件流形成過程
對于用戶而言,用戶無需關(guān)注配置文件的解析過程,只需要在交互界面輸入組件定義相關(guān)參數(shù),即可形成在Kubernetes可被運行的Yaml文件,系統(tǒng)屏蔽了模板引擎的實現(xiàn)過程。
數(shù)據(jù)湖啟動器以守護(hù)進(jìn)程的形式在Kubernetes環(huán)境中運行,以NodePort形式對外暴露服務(wù)。此外提供一種Pod監(jiān)控守護(hù)進(jìn)程,用于輔助數(shù)據(jù)湖組件容器部署和維護(hù),如圖8所示。

圖8 數(shù)據(jù)湖發(fā)布生產(chǎn)線
數(shù)據(jù)湖的發(fā)布需要完成三個步驟:
(1)自定義組件容器信息,對于缺失鏡像完成自動化創(chuàng)建。
(2)根據(jù)服務(wù)參數(shù),創(chuàng)建Kubernetes名稱空間實現(xiàn)資源隔離,并完成RBAC授權(quán)服務(wù)。在容器啟動之前,完成相應(yīng)類型模板的創(chuàng)建工作,并向Kubernetes API傳送Yaml文件流,之后由Kubernetes創(chuàng)建數(shù)據(jù)湖組件Pod。
(3)在Pod啟動過程中,不停監(jiān)控Pod狀態(tài)。當(dāng)檢測Pod處于不健康狀態(tài),且重啟次數(shù)超過閾值仍然失敗,停止重啟操作,并告知用戶創(chuàng)建數(shù)據(jù)湖失敗,同時清理相關(guān)容器,避免資源占用。
為解決湖中數(shù)據(jù)存放問題,需要提供元數(shù)據(jù)統(tǒng)一語義,核心問題在于:把目錄結(jié)構(gòu)和表結(jié)構(gòu)進(jìn)行統(tǒng)一,然后暴露給用戶統(tǒng)一的語義層。因而需要完成Catalog的統(tǒng)一,Catalog是數(shù)據(jù)資產(chǎn)的有序清單,使用元數(shù)據(jù)來幫助組織管理其數(shù)據(jù),并幫助數(shù)據(jù)專業(yè)人員收集、組織、訪問和豐富元數(shù)據(jù)以支持?jǐn)?shù)據(jù)發(fā)現(xiàn)和治理。
Spark,Iceberg和Trino支持多種Catalog后端存儲實現(xiàn),不同Catalog存儲方式和寫入方式有所差異,給元數(shù)據(jù)管理和治理帶來諸多影響,因而在Catalog層需要暴露一個統(tǒng)一的接口。
Hive Metastore(HMS)作為比較成熟的元數(shù)據(jù)管理系統(tǒng),且Spark,Iceberg和Trino都兼容HMS,統(tǒng)一使用HMS作為元數(shù)據(jù)基礎(chǔ)存儲組件。在創(chuàng)建Catalog時,統(tǒng)一調(diào)用HMS接口對Catalog持久化,最終形成統(tǒng)一的Catalog,如圖9所示。

圖9 Catalog統(tǒng)一設(shè)計
統(tǒng)一Catalog可以將元數(shù)據(jù)存儲在一個中心化的位置,使數(shù)據(jù)湖中的所有數(shù)據(jù)都可以被發(fā)現(xiàn)、描述和理解。可以為數(shù)據(jù)湖中的所有數(shù)據(jù)提供一個單一的視圖,從而更容易地找到需要的數(shù)據(jù)。
而數(shù)據(jù)湖要變成有機資源,需要數(shù)據(jù)的融入。由入湖作業(yè)、服務(wù)應(yīng)用中心、云原生計算資源、云原生存儲資源四個模塊構(gòu)成,如圖10所示。

圖10 數(shù)據(jù)處理模塊
入湖作業(yè)由可視化作業(yè)和工程作業(yè)兩種作業(yè)構(gòu)成,業(yè)務(wù)層識別入湖預(yù)處理信息,調(diào)動計算引擎將外源數(shù)據(jù)輸入到存儲空間。
(1)可視化作業(yè)。
獲取數(shù)據(jù)源信息和來源表之后,在可視化界面生成事實表,事實表為數(shù)據(jù)湖表(即中間表)建立映射參考對象,并提供創(chuàng)建中間表UI操作,然后選擇數(shù)據(jù)文件存放位置,保存之后,入湖信息生成。
在提交入湖任務(wù)后,服務(wù)應(yīng)用中心接受請求并解析入湖需求,獲取數(shù)據(jù)源信息以及湖表Schema,同時提取數(shù)據(jù)文件輸出位置信息,借助模板引擎更新計算引擎數(shù)據(jù)文件輸出目錄。成功獲取計算資源之后,將入湖任務(wù)提交至計算引擎,由計算引擎完成數(shù)據(jù)源到數(shù)據(jù)湖的數(shù)據(jù)寫入。任務(wù)完成之后,服務(wù)應(yīng)用中心將處理結(jié)果返回至前端。
(2)工程作業(yè)。
將數(shù)據(jù)工程作業(yè)上傳至文件系統(tǒng),并完成主類、計算引擎配置參數(shù)和程序入口參數(shù)編寫,保存之后,入湖信息生成。
在提交入湖任務(wù)后,服務(wù)應(yīng)用中心接受請求并解析入湖需求,根據(jù)入湖信息生成Dockerfile,如圖 11所示,由鏡像定義器將數(shù)據(jù)工程作業(yè)生成鏡像并上傳至鏡像倉庫。由模板引擎根據(jù)計算引擎配置參數(shù)以及作業(yè)參數(shù)生成Yaml,并向Kubernetes API傳輸Yaml文件流,完成臨時計算作業(yè)集群的生成,由計算作業(yè)集群完成數(shù)據(jù)入湖工作。任務(wù)完成之后,服務(wù)應(yīng)用中心將處理結(jié)果返回至前端,并觸發(fā)臨時計算作業(yè)集群清理工作,避免資源占用。提供多類型作業(yè)調(diào)動云原生計算資源來滿足數(shù)據(jù)從數(shù)據(jù)源到數(shù)據(jù)湖的轉(zhuǎn)變,以適應(yīng)多元化業(yè)務(wù)場景。
在實際運行過程中,數(shù)據(jù)湖各項組件能夠在Kubernetes以自動化形式進(jìn)行部署,并完成數(shù)據(jù)源數(shù)據(jù)以Iceberg表格形式寫入對象存儲中。
在實踐過程中,存在著數(shù)據(jù)湖組件部署過程復(fù)雜且難以維護(hù)、組件部署過度依賴人工化處理等問題。其次,存算不分離制約著數(shù)據(jù)湖平臺的靈活性。將數(shù)據(jù)湖組件容器化,并借助容器編排系統(tǒng)對數(shù)據(jù)湖組件以生產(chǎn)線的方式進(jìn)行組織解決部署和維護(hù)問題,設(shè)計數(shù)據(jù)湖存算分離方案來提高數(shù)據(jù)湖平臺的可擴展性和可移植性。通過統(tǒng)一Catalog的方式可以為數(shù)據(jù)湖中的所有數(shù)據(jù)提供一個單一的視圖,更容易進(jìn)行數(shù)據(jù)發(fā)現(xiàn)。對入湖信息進(jìn)行預(yù)處理,提供多類型作業(yè)以滿足多元化入湖場景,最終將數(shù)據(jù)寫入數(shù)據(jù)湖正確的位置。