深入探索云原生流水線的架構(gòu)設(shè)計(jì)
本文約 4400 字,預(yù)計(jì)閱讀時(shí)間:12 分鐘
支持各種任務(wù)運(yùn)行時(shí),包括 K8s Job、K8s Flink、K8s Spark、DC/OS Job、Docker、InMemory 等?
支持快速對(duì)接其他任務(wù)運(yùn)行時(shí)?
支持任務(wù)邏輯抽象,并且快速地開發(fā)自己的 Action?
支持嵌套流水線,在流水線層面進(jìn)行邏輯復(fù)用?
支持靈活的上下文參數(shù)傳遞,有好用的 UI 以及簡(jiǎn)單明確的工作流定義?
······
那么,不妨試試 Erda Pipeline 吧~
CI/CD 快數(shù)據(jù)平臺(tái) 自動(dòng)化測(cè)試平臺(tái) SRE 運(yùn)維鏈路 ……
自研能更快地響應(yīng)業(yè)務(wù)需求,進(jìn)行定制化開發(fā); 時(shí)至今日,開源社區(qū)還沒有一個(gè)實(shí)質(zhì)上的流水線標(biāo)準(zhǔn),各種產(chǎn)品百花齊放; K8s、DC/OS 等的 Job 實(shí)現(xiàn)都偏弱、上下文傳遞缺失,無法滿足我們的需求,更不用說靈活好用的 Flow 了,例如:嵌套流水線。
整體架構(gòu)
內(nèi)部架構(gòu)
水平擴(kuò)展
分布式架構(gòu)
功能特性
實(shí)現(xiàn)細(xì)節(jié)

Pipeline 支持靈活的使用方式,目前支持 UI 可視化操作、OPENAPI 開放接口、CLI 命令行工具幾種方式。
協(xié)議層面,在 Erda-Infra 微服務(wù)框架的加持下,以 HTTP 和 gRPC 形式對(duì)外提供服務(wù):在早期的時(shí)候,我們只提供了 HTTP 服務(wù),由于 Erda 平臺(tái)本身內(nèi)部是微服務(wù)架構(gòu),服務(wù)間調(diào)用就需要手動(dòng)編寫 HTTP 客戶端,不好自動(dòng)生成,麻煩且容易出錯(cuò)。后來我們改為使用 Protobuf 作為 IDL(Interface Define Language),在 Erda-Infra 中自動(dòng)生成 gRPC 的客戶端調(diào)用代碼和服務(wù)端框架代碼,內(nèi)部服務(wù)間的調(diào)用都改為使用 gRPC 調(diào)用。
在中間件依賴層面,我們使用 ETCD 做分布式協(xié)調(diào),用 MySQL 做數(shù)據(jù)持久化。ETCD 我們也有計(jì)劃把它替換掉,使用 MySQL 來做分布式協(xié)調(diào)。
最關(guān)鍵的任務(wù)運(yùn)行時(shí)(Task Runtime)層面,我們支持任務(wù)可以運(yùn)行在 K8s、DC/OS(分布式云 OS,在 2017-2019 年非常火)、用戶本地 Docker 環(huán)境等。
我們還有開放的任務(wù)擴(kuò)展市場(chǎng),在平臺(tái)級(jí)別內(nèi)置了非常多的流水線任務(wù)擴(kuò)展,開箱即用。同時(shí),用戶也可以開發(fā)企業(yè)/項(xiàng)目/應(yīng)用/個(gè)人級(jí)別的任務(wù)擴(kuò)展,這部分功能在代碼層面已經(jīng)完全支持,產(chǎn)品層面正在開發(fā)中,在后續(xù)迭代中很快就可以和大家見面。

使用 Erda-Infra 微服務(wù)框架開發(fā),功能模塊劃分清晰:
Server 層包括業(yè)務(wù)邏輯,對(duì) Pipeline 來說業(yè)務(wù)就是創(chuàng)建、執(zhí)行、重試、重試失敗節(jié)點(diǎn)等。
Modules 層提供不含業(yè)務(wù)邏輯的公共模塊,其余兩層均可調(diào)用,包括預(yù)校驗(yàn)機(jī)制、定時(shí)守護(hù)進(jìn)程、YAML 解析器、配置管理、事件管理等。
Engine 層負(fù)責(zé)流水線的推進(jìn),包括:
Queue Manager 隊(duì)列管理器,支持隊(duì)列內(nèi)工作流的優(yōu)先級(jí)動(dòng)態(tài)調(diào)整、資源檢查、依賴檢查等。
Dispatcher 任務(wù)分發(fā)器,用于將滿足出隊(duì)條件的流水線分發(fā)給合適的 Worker 進(jìn)行推進(jìn)。
Reconciler 協(xié)調(diào)器,負(fù)責(zé)將一條完整的流水線解析為 DAG 結(jié)構(gòu)后進(jìn)行推進(jìn),直至終態(tài)。
模塊內(nèi)部使用插件機(jī)制,對(duì)接各種任務(wù)運(yùn)行時(shí)。
AOP 擴(kuò)展點(diǎn)機(jī)制(借鑒 Spring),把代碼關(guān)鍵節(jié)點(diǎn)進(jìn)行暴露,方便開發(fā)同學(xué)在不修改核心代碼的前提下定制流水線行為。
目前已經(jīng)有的一些擴(kuò)展點(diǎn)插件,譬如自動(dòng)化測(cè)試報(bào)告嵌套生成、隊(duì)列彈出前檢查、接口測(cè)試自動(dòng)登錄保持等。
這個(gè)能力后續(xù)我們還會(huì)開放給調(diào)用方,包括用戶,去做一些有意思的事情。
統(tǒng)一使用 Event,封裝了 WebHook / WebSocket / Metrics。

Leader & Worker 模式,兩者在部署上不區(qū)分狀態(tài),僅為 Replicas 多實(shí)例:
使用 ETCD 選舉,每個(gè)實(shí)例都可以是 Leader。
只有 Leader 開啟 Queue Manager 和 Dispatcher,分發(fā)任務(wù)給 Worker。
Leader 本身也可以作為 Worker,支持單節(jié)點(diǎn)部署模式,Leader 必須開啟 Reconciler。
Dispatch 使用有界負(fù)載的一致性哈希算法:
使用一致性哈希來分配任務(wù)。但一致性哈希會(huì)超載,例如某些熱點(diǎn)內(nèi)容會(huì)持續(xù)打到一個(gè)節(jié)點(diǎn)上。
有界負(fù)載算法(The bounded-load algorithm):
只要服務(wù)器未過載,請(qǐng)求的分配策略與一致性哈希相同。
過載服務(wù)器的溢出負(fù)載將在可用服務(wù)器之間分配。
Pipeline 實(shí)例增減時(shí),已經(jīng)被分配的流水線不重新分配,盡可能減少切換成本,防止重復(fù)推進(jìn);新增的流水線使用一致性 Hash 算法進(jìn)行分配。

該分布式架構(gòu)是典型的 AP 模型,數(shù)據(jù)層面遵循最終一致性。
中心 Pipeline 直接負(fù)責(zé)流程推進(jìn),調(diào)用邊緣 K8s 創(chuàng)建 Job。
當(dāng)網(wǎng)絡(luò)分區(qū)時(shí),原有部署架構(gòu)下,定時(shí)任務(wù)無法正常執(zhí)行。
中心下發(fā)任務(wù)定義,由 Edge Pipeline 負(fù)責(zé)推進(jìn),直連 K8s,更加穩(wěn)定。
在網(wǎng)絡(luò)分區(qū)恢復(fù)時(shí),主動(dòng)上報(bào)執(zhí)行數(shù)據(jù),實(shí)現(xiàn)數(shù)據(jù)最終一致性。
這里簡(jiǎn)單列舉一些比較常見的功能特性:
配置即代碼 擴(kuò)展市場(chǎng)豐富 可視化編輯 支持嵌套流水線 靈活的執(zhí)行策略,支持 OnPush / OnMerge 等觸發(fā)策略 支持工作流優(yōu)先隊(duì)列 多維度的重試機(jī)制 定時(shí)流水線及定時(shí)補(bǔ)償功能 動(dòng)態(tài)配置,支持“值”和“文件”兩種類型,均支持加密存儲(chǔ),確保數(shù)據(jù)安全性 上下文傳遞,后置任務(wù)可以引用前置任務(wù)的“值”和“文件” 開放的 OpenAPI 接口,方便第三方系統(tǒng)快速接入
如何實(shí)現(xiàn)上下文傳遞(值引用)
每個(gè)節(jié)點(diǎn)的特殊輸出(按格式寫入指定文件或者打印到標(biāo)準(zhǔn)輸出)會(huì)被保存在 Pipeline 數(shù)據(jù)庫中; 后續(xù)節(jié)點(diǎn)通過 outputs 語法聲明的表達(dá)式會(huì)在節(jié)點(diǎn)開始執(zhí)行前被替換為真正的值。

如圖所示:第一個(gè)節(jié)點(diǎn) repo 拉取代碼;第二個(gè)節(jié)點(diǎn) build erda 則是構(gòu)建 Erda 項(xiàng)目。

如何實(shí)現(xiàn)上下文傳遞(文件引用)文件引用比值引用復(fù)雜,因?yàn)槲募臄?shù)據(jù)量比值大得多,不能存儲(chǔ)在數(shù)據(jù)庫中,而是存儲(chǔ)在卷中。 這里又根據(jù)是否使用共享存儲(chǔ)而分為兩種情況,兩者的區(qū)別在于申請(qǐng)的卷的類型和個(gè)數(shù)。 對(duì)于流水線使用者而言,沒有任何區(qū)別。

使用共享存儲(chǔ)

如何實(shí)現(xiàn)緩存加速
在許多流水線場(chǎng)景中,同一條流水線的多次執(zhí)行之間是有關(guān)聯(lián)的。如果能夠用到上一次的執(zhí)行結(jié)果,則可以大幅縮短執(zhí)行時(shí)間。
典型場(chǎng)景是 CI/CD 構(gòu)建,我們以 Java 應(yīng)用 Maven 構(gòu)建舉例:不但同一條流水線不同的多次執(zhí)行可以復(fù)用 ${HOME}/.m2 目錄(緩存目錄),甚至同一個(gè)應(yīng)用下的多個(gè)分支之間都可以使用同一個(gè)緩存目錄,就像本地構(gòu)建一樣~

仍然使用前面的例子,在第二步 build erda 里加上 cache 即可。

Action Agent
對(duì)敏感日志進(jìn)行脫敏處理,保證數(shù)據(jù)安全
無感知的錯(cuò)誤分析和數(shù)據(jù)上報(bào)
文件變動(dòng)監(jiān)聽及實(shí)時(shí)上報(bào)
……
上述所有機(jī)制都是由 Action Agent 程序完成的,它是一個(gè)靜態(tài)編譯的 Go 程序,可以運(yùn)行在任意 Action 鏡像中。Agent 完整的執(zhí)行鏈路如下:

Action Executor 擴(kuò)展機(jī)制恰當(dāng)?shù)娜蝿?wù)執(zhí)行器抽象,使得 Batch/Streaming/InMemory Job 的配置方式和使用方式完全一致,流批一體,對(duì)使用者屏蔽底層細(xì)節(jié),做到無感知切換。
在同一條流水線中,可以混用各種 ActionExecutor。

Go 接口定義

AOP 擴(kuò)展點(diǎn)機(jī)制

