對外開源是提高影響力、共建生態的有力手段。在項目對外開源的過程當中,首先是將能夠開源的部分抽離出來,發佈一個「開源版本」,與內部基礎設施相關的部分則留在內部版本中。在後續的開發中,開源版本隨着社區不斷演進,內部版本則隨着內部的需求不斷迭代。若是沒有明肯定義內外部版本的邊界、代碼同步不徹底、不及時,將會致使內外版本越走越遠,變成兩個不相干的項目。git
促使咱們開始作內外版本一致事情的,主要是如下三點考慮:github
本文將詳細說明 PouchContainer 是如何作到內外部版本一致的。api
PouchContainer 開源已經將近一年了,在內外部版本差別梳理以前,沒有人能講清楚兩個版本之間的差別。因而我用 Beyond Compare4 等軟件,進行文件級別的比對,一個個找出內外部不一樣的代碼,進而梳理出內外部功能級別的差別。所幸內外部的差別比我想象中的要小。主要有如下幾類差別:promise
前三類差別是集團內針對 PouchContainer 的定製化內容。固然,有一些是目前無可奈何加上的臨時邏輯。在後續的發展中,將會改造、下線。第四類的差別,主要緣由在於開發者沒有版本同步的意識:bugfix 在內部緊急修復以後,沒有同步到社區。社區開發者的 bugfix, feature 沒有及時同步進內部代碼。網絡
在差別梳理出來以後,須要明確內外部的功能邊界。能開源的就開源,獨有的功能繼續保留。抹平非功能性的不一致。ide
__版本同步的根本仍是提高軟件的可拓展性__,容許共用一套核心代碼,針對不一樣的業務場景進行定製。因此咱們面對的問題不只是內外部版本的一致問題,應該是三版本、四版本、多版本的一致問題。這也是 PouchContainer 賦能其餘業務場景的基礎。一樣也是開源項目內部使用的必修課 因此,咱們作的第二步是經過__插件機制__提高 PouchContainer 的可拓展性。目前咱們支持 API, container, daemon, volume,cri 這五種插件。具體見文檔。測試
插件的設計旨在提高軟件的擴展性,但不容許插件改變原有的工做流程。PouchContainer 提供了對 container, daemon, volume, cri 關鍵步驟的 hook。以 daemon 插件爲例,該插件提供了對啓停接口的 hook。插件實現者能夠在 daemon 啓動前運行一些其餘程序,好比 dfget。在 daemon 中止前,作一些清理操做。this
// DaemonPlugin defines places where a plugin will be triggered in pouchd lifecycle type DaemonPlugin interface { // PreStartHook is invoked by pouch daemon before real start, in this hook user could start dfget proxy or other // standalone process plugins PreStartHook() error // PreStopHook is invoked by pouch daemon before daemon process exit, not a promise if daemon is killed, in this // hook user could stop the process or plugin started by PreStartHook PreStopHook() error }
而 API 插件經過將路由表傳給插件,容許插件實現者擴展、刪除、修改 API。這讓接口有了很大的靈活性。spa
import "github.com/alibaba/pouch/apis/server/types" // APIPlugin provide the ability to extend PouchContainer HTTP API and change how handler behave. type APIPlugin interface { // The default handler of each API would be passed in while starting HTTP server. // UpdateHandler could register extra HTTP API to PouchContainer server, // change the behavior of the default handler. UpdateHandler([]*types.HandlerSpec) []*types.HandlerSpec }
經過插件化的改造,絕大部分的內部 PouchContainer 定製化邏輯都在插件中實現了。插件單獨一個文件目錄,在代碼合入的時候幾乎不會產生衝突。以後將內部插件邏輯和其餘差別一個個 commit 到開源分支上。__作到內外部版本的同源。__插件
PouchContainer 開源版本表明通用功能,若是外部開發者在通用版本上迭代的新增功能,集團內部用不到,該如何保障外部功能同步到內部以後,不影響內部的現有功能的?
首先內部版本是有一套完整的測試覆蓋的,__內部測試在開源測試的基礎上還包含針對內部場景的測試__。內部測試經過,咱們便認爲該版本是符合內部場景要求的,開源版本沒有影響內部穩定性的。若是測試不經過,有兩個選擇,一是從新評估開源通用功能,是否有代碼缺陷;二是在內部倉庫打補丁。以此來保障開源代碼同步至內部後的穩定性。
咱們先來看看,在這以前的工做流是怎樣的。開發者分別在內部倉庫和開源倉庫提代碼,緊急一些的需求會先在內部倉庫提 merge request,不那麼重要的需求會先在社區提 PR。有人會按期將開源分支 merge 進內部倉庫。這裏有幾個問題,一是內部也有一套測試流程,這套流程可能不像 travisCI 或 circleCI 對接 github 那麼方便,有些設計好的測試在內部倉庫甚至不會跑。二是沒有插件化前,某些功能在內外部有兩種不一樣的實現,這在每次手動 merge 代碼的時候幾乎都會衝突,解決衝突的過程很容易引入非預期的修改,下次又繼續衝突。
git flow 如圖
在完成一致性改造以後,咱們創建一套規則來保證後續不會再出現版本分離的事情。
開源,能幫助項目吸取外部營養,加速項目的演進。在一致性的改造過程當中,幫助開發者明確內外部版本的邊界,打造同一份核心代碼,提高核心代碼的可定製化能力,更好地服務於不一樣的場景。