PouchContainer 開源版本及內部版本一致性實踐

PouchContainer 開源版本及內部版本一致性實踐

爲何要作內外版本一致

對外開源是提高影響力、共建生態的有力手段。在項目對外開源的過程當中,首先是將能夠開源的部分抽離出來,發佈一個「開源版本」,與內部基礎設施相關的部分則留在內部版本中。在後續的開發中,開源版本隨着社區不斷演進,內部版本則隨着內部的需求不斷迭代。若是沒有明肯定義內外部版本的邊界、代碼同步不徹底、不及時,將會致使內外版本越走越遠,變成兩個不相干的項目。git

促使咱們開始作內外版本一致事情的,主要是如下三點考慮:github

  • __下降維護成本: __這也是同步工做的出發點。減小多版本維護的工做負擔,也方便團隊的交流。
  • __發揮社區的優點: __吸取社區力量是開源的一個重要考慮,讓社區的力量真正在內部發揮價值。社區會有一些咱們沒有考慮到的需求,有集結社區力量的更嚴格的 review,與 github 集成的強約束 CI 等。
  • __下降功能同步的風險: __在內外部版本不一樣源的狀況下,將外部代碼合併進來很是容易衝突。代碼模塊較多,解決衝突的過程當中,一不當心就會引入非預期的修改,而這在合併的過程當中,很難發現。

本文將詳細說明 PouchContainer 是如何作到內外部版本一致的。api

差別梳理及邊界定義

PouchContainer 開源已經將近一年了,在內外部版本差別梳理以前,沒有人能講清楚兩個版本之間的差別。因而我用 Beyond Compare4 等軟件,進行文件級別的比對,一個個找出內外部不一樣的代碼,進而梳理出內外部功能級別的差別。所幸內外部的差別比我想象中的要小。主要有如下幾類差別:promise

  • 對內部老系統的接口兼容
  • 爲內部系統開的後門接口
  • 對接內部基礎設施的邏輯,如存儲,網絡
  • bugfix, feature 沒有互相同步

前三類差別是集團內針對 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 代碼的時候幾乎都會衝突,解決衝突的過程很容易引入非預期的修改,下次又繼續衝突。

image.png | left | 747x560

 

 

git flow 如圖

 

在完成一致性改造以後,咱們創建一套規則來保證後續不會再出現版本分離的事情。

  1. 原則上非私有加強,應該先在社區提交 PR,merge 以後經過同步機制進入內部版本
  2. 若是時間緊急,bugfix 先在內部版本上提交。後續 commiter 負責將其 cherry-pick 到社區。社區 review 發現須要繼續修改的,將修改另提一個 commit,這樣保證 commit 不與內部倉庫衝突
  3. 代碼同步,由機器人定時提交 merge request 將開源同步至內部
    在 merge 的時候,保證是 fast-forward 的,這樣內外部的 commit 是一一對應的,減小衝突。

總結

開源,能幫助項目吸取外部營養,加速項目的演進。在一致性的改造過程當中,幫助開發者明確內外部版本的邊界,打造同一份核心代碼,提高核心代碼的可定製化能力,更好地服務於不一樣的場景。

相關文章
相關標籤/搜索