UCloud API&SDK 工程化實踐(一):如何設計與實現 SDK 全生命週期自動化

摘要前端


對於公有云服務,API 是客戶接入使用的主路徑之一,API 的質量和持續穩定性相當重要,在使用者角度,表明着雲服務的質量和穩定性。如何妥善管理API 以及基於API的 SDK, 如何在產品快速發展的同時,持續的保障 API 的穩定性、可靠性,是一個很是大的挑戰。算法

UCloud 接入產品開發團隊在 2019 年下半年,經過工程化的手段,打通了 API 管理、測試管理、網關日誌、Gitlab CI 等內部系統,以 API 團隊的契約爲中心改造了整條工具鏈產品的發佈鏈路,對 API&SDK 進行閉環校驗,持續保證質量。並對基於 Github 的開源項目構建了通用的發佈管道和版本策略。編程

當前工程化共支持了 16 個產品 300+ API SDK 的平常發佈,以及 SDK側長達半年的每日迴歸測試。從須要 1 人天/產品的人工開發成本,到 5 分鐘完成一次新產品 SDK 代碼生成,工程效率產生了質的飛躍。安全

本文介紹了 UCloud 在 API/SDK 工程化改造上的一些實踐,以供你們交流與參考。網絡

概覽異步


一、爲何須要進行圍繞 API/SDK 的工程體系建設

UCloud 接入產品團隊負責研發工具鏈產品與網關,探索控制檯之外的基礎設施接入方式。陸續發佈了包括 Terraform、Packer、CLI、移動網絡探測等接入工具並所有開源。工具鏈產品因爲便於使用,更符合現代軟件開發的理念等特色,成爲了客戶 DevOps 團隊接入的最主要手段。編程語言

怎麼定義工具鏈產品?首先對於雲廠商來講,與 Restful 等面向資源的 API(Resource-Oriented)風格不一樣,UCloud 開放的 OpenAPI 大可能是面向行爲(Action-Oriented)的。OpenAPI 除了實現 CRUD 以外,還負責發送各類各樣控制平面(Control Plane)的指令,如資源的伸縮啓停。ide

那麼對於 UCloud 來講,工具鏈產品的價值在於,API 實現最小顆粒度的行爲,工具鏈產品組合這些行爲,進行更高層次的抽象。好比 Terraform 對於資源依賴圖的抽象,CLI 對行爲的級聯和批量操做,Packer 對鏡像的生命週期管理等等。工具鏈產品構建在 API/SDK 之上,是 API/SDK 的高層次封裝。模塊化

圖 1 :工具鏈產品示例異步編程

工具鏈產品區別於控制檯的特色在於,工具鏈產品實踐了基礎設施即代碼(IaC,Infrastructure as Code)這一理念,設施與行爲均可以使用代碼來嚴格地描述與版本化,易於集成。同時工具鏈產品在運行時的生命週期是全自動的,在實際的應用場景中,當咱們定義好一個資源拓撲,或者基於工具鏈產品編寫了一個腳本以後,咱們指望它的運行過程是全自動的,不須要人工干預。

因爲上述兩個特色,工具鏈產品對外的接口必須保證是嚴格的,不能隨意變更,這就要求 API 也必須是嚴格的。同時對於公有云 API/SDK 可用性的重度依賴,使得咱們不得不尋求一些工程上的方法,來確保工具鏈產品的整個生命週期都是可用的。

二、面臨的問題

• 開發容易變動難,工具鏈產品包括幾乎全部 IaaS 產品的流量接入,平常變動十分繁瑣,須要自動化變動。

• 工具鏈產品全線開源,做爲知名的雲廠商,必須保證開源社區的核心指標,即 90% 覆蓋率,A 以上的代碼風格評級,須要有成熟的機制來自動化驗證。

• 相比友商,咱們的團隊規模小不少,須要儘量複用現有設施,對系統變動最頻繁的部分作自動化生成,充分發揮技術帶來的效率紅利。

全景圖


爲了解決上述問題,團隊在 2919 年下半年開始進行 API/SDK 工程體系的建設,第一期的目標是使工具鏈產品所依賴的 SDK 實現全自動化的代碼生成。

圖 2 :API/SDK 工程體系建設全景圖

從圖中能夠看出,工具鏈產品團隊構建了一個通用的發佈代理服務,用來執行 CI Job、處理相關的發佈任務,並經過機器人與 Github 作了打通。

相關技術解析


一、API 建模

爲了使 API 可以獲得統一管理,同時防止產品間豎井式的信息隔離,UCloud 測試平臺部在 2017 年末打造了公共的 API 管理平臺「優效」,將全部現網 API 的定義收斂至統一的 API 註冊中心上,使用自定義的格式來形式化地描述 API。

在接下來的一年時間裏,優效添加了測試管理功能,支持了一種基於 DSL 表達式的行爲驅動測試開發模式,測試團隊在優效上編輯測試步驟,使用 DSL 完成數據的抽取轉換和對比,構建測試場景並執行。

2018 年下半年,咱們團隊開始進行工具鏈產品的研發,包括資源編排、CLI 及其依賴的全線 IaaS 產品控制平面 OpenAPI SDK,發佈了初版基於優效自動生成的 Go SDK。

2019 年下半年,咱們經過跨部門協做對測試表達式進行了中間表示生成,實現了將 DSL 表達式完整地轉譯成各語言 SDK 調用代碼,完成了與測試團隊的工做流整合。

圖 3 :API、SDK、測試相互依存

這一系列工做的意義在於,API 做爲企業與雲廠商之間交互的契約,須要具備實際的效力,而這個效力是須要一些工程上的手段來確立的。API、測試與 SDK 相互做用,構成一個有機的總體,才能使這個契約具備實際的效力。

二、SDK 代碼生成

代碼生成是編譯器領域的一個典型問題,目前各界對於相關技術已經有了很充分的研究,而 SDK 生成使用的技術並不複雜。這裏只是簡單分享一下,UCloud 在代碼生成過程當中使用了哪些技術,以及如何將一個不規則的遺留問題,經過形式化的方法轉化爲一個已知領域的問題的思路,但願對你們有所啓發。

2.1 調用方代碼生成

因爲雲服務 SDK 對於編程語言類型系統中,類型安全的須要,雲服務的 SDK 除了提供一種萬能的,泛化調用方式(就像各語言的 HTTP Client 同樣),還會將每個 API 的參數和返回經過類型系統顯式的定義出來。好比聲明成 Python/Java 中的類,Go 中的結構體。

得益於測試平臺部在優效上的早期工做,調用方代碼只需使用模版引擎將目標代碼渲染出來便可。對於一些版本割裂比較嚴重的語言,如 Python 2/3,咱們選擇利用語言原生的抽象語法樹來對代碼進行二次剪枝處理。

因爲衆所周知的緣由,Python 2 原計劃於 2020 年 1 月中止維護,雖然受一些緣由影響,中止維護的時間向後延期了一段時間,但 Python 2 的壽終正寢已成定局。但做爲雲廠商而言,依然存在很多客戶的存量系統依然在使用 Python 2,其中不乏大致量的客戶,存量系統改造對他們來講,是一個風險收益很難權衡的問題,因此雲廠商依然要作好長期支持 Python 2 的準備。

圖 4 :Python 3 to 2 語法樹剪枝

UCloud 對於 Python 2/3 SDK 的生成方式是,首先生成 Python 3 的代碼,以後將 Python 3 的代碼轉換成抽象語法樹(AST,Abstract Syntax Tree),以後遍歷這棵 AST,移除其中 Python 3 Only 的節點,如 type hints,添加一些兼容性的節點,例如新式類 object 基類,utf8 header 等等,最終從這棵樹中重建出 Python 2 SDK 的代碼。

2.2 測試代碼生成

優效測試模塊由測試團隊於2018 年開發上線,使用一種可視化配置來編寫測試,遵循行爲驅動開發的理念(BDD,Behavior-driven development),使測試定義與實現解藕。

因爲接入產品團隊對於 API 的重度依賴,若是想保證工具鏈產品的可靠性,必須對依賴的 SDK 進行充分測試。並且因爲 SDK 原則上要覆蓋全部公有云 IaaS 產品,出於成本和責權劃分等因素的考慮,複用現有存量的測試用例是惟一可行的驗證手段。

圖 5 :測試結構抽象

相似於 ThoughtWorks 的 3S (Specification、Scenario、Step)抽象,UCloud 將測試抽象爲測試解決方案(Solution)、測試集(Set)、測試步驟(Step) 三個層次。多個測試步驟(Step)構成一個測試集(Set),順序執行;多個測試集構成一個解決方案(Solution),並行執行。

每個測試步驟由請求、綁定和驗證三個部分構成,幷包含一些控制類屬性如重試、延時、快速終止等。以處理片狀測試(Flaky Testing )的場景。

測試步驟中請求、綁定和驗證的值均可以使用一種 DSL 表達式來編寫,能夠處理一些複雜邏輯,如獲取時間戳、四則運算、數據抽取等。

測試代碼生成的難點就在於,原有的測試執行引擎是使用 Python 解釋執行的,類型系統比較薄弱,同時因爲沒有經過形式化的文法定義表達式,隨着時間的推移,表達式變得愈來愈不規範,如何將 DSL 表達式,轉譯成 SDK 的測試代碼,成了一個巨大的挑戰。

一個測試表達式的示例以下,能夠看出這個表達式的語法仍是比較簡單的。

${u_eval(${u_get_timestamp(10)} - 3600)}

對於形如上式的簡單表達式解析,有兩種思路,一種是直接手寫一個遞歸降低的解析器,將詞法分析和語法分析一併完成,咱們早期也是這麼處理的。

可是隨着時間的推移,咱們發現不只僅只有上述那一種表達式形式,更多不規範的表達式寫法被挖掘出來,咱們意識到,一味改 Parser 並非一個長久的辦法,應該將文法清晰地定義出來,造成一個共識,只複用那些較爲規範的測試集,這樣才能應用在多種不一樣的編程語言上。因此咱們迴歸了傳統的寫法,手寫 Lexer,經過  Yacc 生成 Parser。

圖 6 :語法分析構造語法樹

Golang 寫 Lexer,參考了 Rob Pike 2011 年的 Slide,《 Lexical Scanning in Go》,這裏再也不贅述,其核心思想就是將狀態轉移的動做抽象成函數,描述出在 One Pass 過程當中,遇到每一個字符時的狀態轉移動做。下圖是 Lexer 過程當中的狀態轉移:

圖 7 :Lexer 狀態機

Parser 的部分咱們使用了 goyacc,經過定義好的文法,生成解析器代碼,解析器的輸入是詞法分析階段產生的 Token,輸出是一棵 JSON 格式的表達式語法樹,下圖是文法和語法樹的示例:

圖 8 :語法樹示例

有了 JSON 格式的中間表示,咱們在任何語言中,讀出這棵語法樹,以後用簡單的模版引擎,就能夠渲染出想要的目標代碼,生成 SDK 的測試代碼。

三、運行時抽象

在 SDK 的代碼中,有一部分代碼是公共的,而且極少變動,實現了請求序列化、重試、日誌、錯誤處理等等,對於這部分代碼須要一個統一的運行時抽象來減小開發成本。SDK 的本質是請求的生命週期管理,該場景有一個典型的抽象:

圖 9 :洋蔥圈模型

圖片來源:Egg.js 文檔《異步編程模型》小節

洋蔥圈模型是面向切面編程(AOP)的經典應用,業務邏輯做爲洋蔥的一層表皮,當一個請求發起的時候,通過一層一層的前側的洋蔥表皮,例如參數注入,簽名算法等,到達洋蔥中心,請求響應時,從洋蔥中心再通過後側的洋蔥表皮例如日誌、重試、錯誤處理等,返回結果。

這樣作的好處在於,業務邏輯與請求生命週期是徹底正交的,能夠更加靈活地添加或刪除業務邏輯。例如客戶能夠自定義 API 返回錯誤時它的公共處理行爲,注入自定義模塊等等,保持業務邏輯是易理解,可測試的。

四、持續發佈

OpenAPI SDK 項目包含幾乎全部 IaaS 產品的 API 變動,做爲一個典型的高頻變動業務,項目早期面臨如下問題:

• 手工發佈有出錯風險,雖然能夠補救,但會給客戶形成困擾

• 研發負擔大,開源項目發佈很是繁瑣,須要變動版本號、標籤、README、Release Note 等,浪費研發資源

• 僅有自動化測試,沒有自動化變動,形成兩者會產生不一致現象

由於相比友商,UCloud 的人員數量少不少,因此更須要充分發揮技術帶來的效率紅利。接入產品部最終選擇了使用 Gitlab CI + Github Bot 來對 SDK 等開源工具鏈產品進行全自動化發佈。

五、流水線設計

首先,咱們將 SDK 發佈拆分爲平常發佈與窗口發佈,將大量具備中斷性質的審查任務移動到每週的固定時間進行處理,保證團隊在高工做負載下依然可以保證足夠的研發能力。並對兩種發佈任務分別設計了全自動化的發佈流水線,以下圖所示:

圖 10 :平常發佈流程

圖 11 :窗口發佈流程

平常發佈任務會執行平常的代碼生成和測試工做,合入代碼倉庫。在窗口發佈任務中會使用 Github 機器人進行版本凍結和發佈工做,並推送 SDK 到各語言官方製品倉庫中。

六、版本策略

能夠看到,在每週的發佈窗口內,若是有發佈任務,SDK 將對版本進行自動化變動,版本號遵循如下策略自動計算:

• 合併PR 前,引導代碼貢獻者提供格式化的描述信息

• 每次發佈時,歸併上次發佈之間全部PR 的描述信息

• 經過Pull Request Comment 計算版本

• 新特性,新產品:主版本+1

• 特性加強,新API:次版本+1

• 問題修復,更新API:補丁版本+1

• 若是是預覽版,則主版本號鎖定爲零,從次版本號開始遞增

例如:

圖 12 :版本自動計算規則

七、服務化

在作 SDK 相關能力的時候,經常有團隊但願使用 SDK 與兄弟團隊聯調,或在產品內測時使用 SDK 自我驗證,這就須要 SDK 可以提供主動發佈能力,由外部團隊觸發構建,並生成預覽版。

對於這種場景,接入產品團隊對發佈流程進行了服務化改造,提供了一個簡單的代理服務,用來觸發 CI Job:

圖 13 :代理服務拓撲

CI/CD 服務化帶來的收益包括,發佈流程的標準化、模塊化,可複用性顯著提高,發佈流水線自己做爲業務領域統一建模,使得發佈自己是可維、可測的。而且給未來更進一步的改造打下堅實的基礎。

總結與展望


在過去的半年中,UCloud 從新梳理了 API 模型,添加了 SDK 通用抽象,使用編譯器前端和模版技術對 SDK 代碼生成問題進行了統一建模,將發佈流水線自動化,完成了 API/SDK 能力的第一期工程體系建設。

下一階段的目標是,將 API/SDK 工程化的能力集成到 Git 工做流中,使得任何人均可以自助地進行發佈,而無需特定的團隊參與。咱們在每個階段的工做都會經過文章記錄並分享出來,給業界有相似場景的小夥伴分享實際落地的經驗以供參考,但願能夠對你們有所幫助。

相關文章
相關標籤/搜索