您好,登錄后才能下訂單哦!
在社區的支持下,PouchContainer 已有超過 2700 個 commit,100 多位社區開發者的踴躍貢獻,其中不乏國內一線互聯網公司、容器明星創業公司貢獻者的參與。
想了解更多 PouchContainer 的同學可以點擊這篇了解上下文: PouchContainer 富容器技術
為什么要做內外版本一致
一般開源產品在對外開源的過程中,首先是將可以開源的部分抽離出來,發布一個“開源版本”,與內部基礎設施相關的部分則留在內部版本中。在后續的開發中,開源版本隨著社區不斷演進,內部版本則隨著內部的需求不斷迭代。如果沒有明確定義內外部版本的邊界、代碼同步不完全、不及時,將會導致內外版本越走越遠,變成兩個不相干的項目。
促使我們開始做內外版本一致事情的,主要是以下三點考慮:
降低維護成本: 這也是同步工作的出發點。減少多版本維護的工作負擔,也方便團隊的交流。
發揮社區的優勢: 吸收社區力量是開源的一個重要考慮,讓社區的力量真正在內部發揮價值。社區會有一些我們沒有考慮到的需求,有集結社區力量的更嚴格的 review,與 github 集成的強約束 CI 等。
降低功能同步的風險: 在內外部版本不同源的情況下,將外部代碼合并進來非常容易沖突。代碼模塊較多,解決沖突的過程中,一不小心就會引入非預期的修改,而這在合并的過程中,很難發現。
本文將詳細說明 PouchContainer 是如何做到內外部版本一致的。
差異梳理及邊界定義
PouchContainer 開源已經將近一年了,在內外部版本差異梳理之前,沒有人能講清楚兩個版本之間的差異。于是我用 Beyond Compare4 等軟件,進行文件級別的比對,一個個找出內外部不同的代碼,進而梳理出內外部功能級別的差異。所幸內外部的差異比我想象中的要小。主要有以下幾類差異:
對內部老系統的接口兼容
為內部系統開的后門接口
對接內部基礎設施的邏輯,如存儲,網絡
bugfix, feature 沒有互相同步
前三類差異是集團內針對 PouchContainer 的定制化內容。當然,有一些是目前迫不得已加上的臨時邏輯。在后續的發展中,將會改造、下線。第四類的差異,主要原因在于開發者沒有版本同步的意識:bugfix 在內部緊急修復之后,沒有同步到社區。社區開發者的 bugfix, feature 沒有及時同步進內部代碼。
在差異梳理出來之后,需要明確內外部的功能邊界。能開源的就開源,獨有的功能繼續保留。抹平非功能性的不一致。
一致性改造
版本同步的根本還是提升軟件的可拓展性,允許共用一套核心代碼,針對不同的業務場景進行定制。所以我們面對的問題不僅是內外部版本的一致問題,應該是三版本、四版本、多版本的一致問題。這也是 PouchContainer 賦能其他業務場景的基礎。同樣也是開源項目內部使用的必修課 所以,我們做的第二步是通過插件機制提升 PouchContainer 的可拓展性。目前我們支持 API, container, daemon, volume,cri 這五種插件。具體見文檔。
插件的設計旨在提升軟件的擴展性,但不允許插件改變原有的工作流程。PouchContainer 提供了對 container, daemon, volume, cri 關鍵步驟的 hook。以 daemon 插件為例,該插件提供了對啟停接口的 hook。插件實現者可以在 daemon 啟動前運行一些其他程序,比如 dfget。在 daemon 停止前,做一些清理操作。
// 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。這讓接口有了很大的靈活性。
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 如圖
在完成一致性改造之后,我們建立一套規則來保證后續不會再出現版本分離的事情。
原則上非私有增強,應該先在社區提交 PR,merge 之后通過同步機制進入內部版本
如果時間緊急,bugfix 先在內部版本上提交。后續 commiter 負責將其 cherry-pick 到社區。社區 review 發現需要繼續修改的,將修改另提一個 commit,這樣保證 commit 不與內部倉庫沖突
代碼同步,由機器人定時提交 merge request 將開源同步至內部
在 merge 的時候,保證是 fast-forward 的,這樣內外部的 commit 是一一對應的,減少沖突。
總結
開源,能幫助項目吸收外部養分,加速項目的演進。在一致性的改造過程中,幫助開發者明確內外部版本的邊界,打造同一份核心代碼,提升核心代碼的可定制化能力,更好地服務于不同的場景。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。