1 前言
當代信息技術飛速發展,軟件和系統的代碼規模都變得越來越大,而且組件眾多,依賴繁復,每次新版本的發布都仿佛是乘坐一次無座的綠皮車長途夜行,疲憊不堪。軟件交付是一個復雜的工程,涉及到軟件開發的各個細節,其中任何一環出現問題,都會導致軟件不能及時交付,或者交付的質量堪憂。
從企業的角度來講,如何利用更科學的工具、更科學的流程來提高產品質量,提升客戶滿意度,是剛需。從員工角度來講,生命里值得追求的事情很多,不能把寶貴的時間浪費在一些機械的、重復的事情上面。
聯想企業網盤從2007年開始面向企業客戶提供專業的云存儲服務,10年來服務了250000+企業。軟件的更新迭代司空見慣,聯想企業網盤就是由成百上千臺服務器組成的,是一個非常復雜的互聯網應用,僅僅在服務端就有幾十個模塊協同工作,加上各種客戶端,需要使用不同的編譯發布環境,有時候需要單獨模塊發布,有時候需要多個模塊聯合發布,使得每次的升級情況都非常復雜。曾經經歷過一次大版本的升級迭代,運維和研發團隊不眠不休的工作了40多個小時,既影響了用戶的服務,也使得團隊疲憊不堪。類似的經歷,使得我們思考如何通過技術革新來解決這一難題,能夠把我們的工程師們從簡單勞動中解放出來,這樣在未來面對更大規模的集群的時候,才能夠游刃有余。
縮短上線時間,提高上線準確度,是我們建設這個系統的初衷。
2 問題
先讓我們借用一張圖(來源于 thoughtworks 官方文檔)來回顧一下軟件發布的一個完整的流程:
整個過程中,代碼管理,集成和測試,發布上線是3個主要的環節。我們所有的問題都集中在這3個環節當中。
1、代碼管理
代碼管理混亂是一個研發團隊的常見問題,研發的過程中,代碼的分支設計不合理,分支過多或者過少,分支依賴混亂,權限控制缺失,完全靠人治,沒有代碼審核。
2、集成和測試
從研發環境到測試環境,都沒有統一規范的部署環境,研發團隊直接給測試出版本(野版本),因為編譯環境,人員水平的差異會導致各種莫名其妙(有時候很低級)的問題,極大的影響了測試的效率和準確度。
3、上線交付
代碼最終部署到生產環境的時候,需要運維人員和研發人員頻繁手工操作,費時費力,還容易出錯,整個過程不可重復且沒有記錄,回滾操作復雜,有時候甚至是無法回滾的,一旦是上線出現錯誤,對我們用戶的影響就是非常惡劣的。
3 實踐
多年來,我們在研發過程中不斷總結,想了很多的辦法,在服務客戶的同時積累了大量的生產環境運維經驗,開發了許多工具和流程,來解決升級和產品上線的問題。,下面基于聯想企業網盤的生產實踐,分享一些我們在建設持續交付系統方面的方法。
如下圖所示,我們主要討論這幾個方面:
3.1 代碼管理
代碼是軟件交付過程的源頭,所以合理的規劃與管理尤為重要。
3.1.1 代碼倉庫
早期,我們所有研發人員的代碼都存放在一個 SVN 庫里,分支和 Tag 散布在各個模塊的子目錄里。SVN 是很好的一個工具,但是太靈活了,要大家嚴格遵守紀律,但是更多時候要靠大家自覺,但是人總是會有松懈的時候。一旦有人不守紀律,對于后來者就是一個苦不堪言過程。
所以我們的第一步,就是把 SVN 遷移至 Git。按照模塊拆分為單獨的庫,每個模塊單獨授權,統一分支模型。倉庫軟件用的 Gerrit,它原本是代碼審核工具,擁有強大的權限管理系統,Git 倉庫只是附帶的功能。
其實在從SVN遷移到Git的時候,有很多工程師會有疑問,為什么遷移到 Git?不是 SVN 不好,也不是為了追逐技術潮流,而是后面的自動化工作(包括代碼審核工具)用 Git 更方便,當然 Git 強大的分支功能以及分布式也是一個重要原因。
3.1.2 分支設計
分支我們參考比較常見的一個 Git 分支模型(參考鏈接),針對我們自己的需求做了一些調整,如下圖:
1、 設計兩條主分支,dev 和 master,dev 是開發分支,master 是對外的穩定分支,持續交付系統會從master分支拉取代碼進行構建;
2、 輔助分支只使用 feature 分支和 hotfix 分支,feature 分支原則上是盡量不建,只用于開發周期比較長的新功能開發,短平快的 feature 都直接提交至 dev。
3.1.3 審核
代碼是產品質量的源頭,代碼質量不行,其他再多輔助手段都沒用。代碼審核是保證代碼質量至關重要的一環。只要團隊人員數大于一個就應該推行代碼審核。
代碼審核有兩種模式:
l 集成前審核(pre review)
顧名思義,在代碼合并至目標分支前進行代碼審核,有問題改,改完再繼續審核,審核通過則集成進目標分支,這一類審核的代表工具軟件有:Github,Gerrit,其中 Github 是以分支為單位進行審核,Gerrit 以提交為單位進行審核。
l 集成后審核(post review)
先合并代碼,然后進行審核,有問題只能用新的提交來修復了,這一類審核的代表工具軟件(其實這兩款軟件也支持 pre review):reviewboard,phabricator。此種方式容易導致目標分支不穩定,所以一般不建議。
我們采用的是第一種集成前審核的方式,工具軟件用的 Gerrit,以提交為單位,強制審核過后再合并至目標分支(當然這個過程是自動的)。
好了,話不多說,有圖有真相,下圖是我們的代碼提交工作流:
圖中黃色的部分即是代碼審核的部分,每個提交需要經過其他人審核(Code Review +2)和持續集成系統驗證過(Verify +1)才能合并至目標分支。
代碼審核頁面:
3.2 構建部署
在這里我簡單的將構建部署分為持續集成和部署流水線,實際上,這兩塊很多地方有重合,這里的持續集成僅僅只討論構建驗證和自動集成,部署流水線包括從構建到部署至不同環境的整個過程。
3.2.1 持續集成
持續集成是一個大的議題,是敏捷開發的一項核心實踐。在持續交付過程當中,持續集成將從開發到部署的各個環節組成一條流水線,是整個交付過程的核心。重點是要快速反饋,在集成代碼之前迅速發現問題并改正。
我們把單元測試、編譯驗證、靜態掃描和覆蓋率檢測分離出來(這一步驟的時間控制在 5分鐘內,這也是前面為什么要把庫拆分的原因之一),在研發人員提交代碼后立即觸發構建,在5分鐘內把結果反饋給研發人員,繼而快速修復錯誤,直至驗證通過。
我們采用的工具軟件是 Jenkins,最流行的持續集成軟件,通過插件支持 Gerrit,功能非常強大。
在實際的實施過程當中,要求每個模塊都要提供在一個干凈環境執行編譯、單元測試等等步驟的腳本或方法,構建環境可以通過 Vagrant 或者 Docker 來自動配置,我們內部采用了Docker 技術來隔離各個構建環境。
流水線
3.2.2 部署流水線
顧名思義,這一步驟就是把打包好的軟件部署到不同的運行環境,并且要自動處理各個環境的配置(例如域名、數據庫信息、登錄信息等等),此步驟嚴重依賴于前面步驟的實現,倉庫的規劃、分支的規劃、持續集成的流水線構建等等。
一個典型的部署流水線
在構建部署流水線的時候,我們要遵循幾個原則:
1、 過程可重復;
2、 一次構建多地部署;
3、 模塊化部署;
4、 變更管理;
5、 審計功能;
6、 快速回滾。
在選擇部署工具方面,我們考察過兩個:thoughtworks go 和 Jenkins(插件 Delivery Pipeline)。
Go 系統自帶管道,但是靈活性不如 Jenkins;Jenkins 的一個好處是我們的持續集成都在 Jenkins 里實現,很多腳本都可以復用,甚至很多任務都能直接復用,缺點是管道各任務之間數據共享比較繁瑣,需要額外的插件(例如 Copy Artifact),所以實現的不是很自然。
在實際的實施過程當中,能夠完全實現自動化(無人值守發布)是一種理想狀態,但實踐當中總是會受各種因素制約,所以必要時也必須向現實低頭。我們最終實現了一鍵部署加關鍵環境(例如生產環境)手工觸發(下面圖中的播放小箭頭就是這樣的步驟)相結合的流程,參見下圖:
在實施過程當中,配置文件的管理也是很重要的一個議題。配置文件主要分為兩類:
1、 配置文件與運行程序不能分離,像J2EE這樣的應用,配置文件與編譯成果物打包成一個 war 文件,我們的處理方法是把敏感信息(例如數據庫信息)存放在其他的Git 庫,構建的時候針對不同環境分別構建,構建時由Jenkins 自動記錄代碼的版本和配置文件的版本;
2、 配置文件與運行程序可以分離,類似于 nginx 這樣,我們把程序打包成 rpm 或者 deb ,配置文件存放在 puppet 主服務器上,每次部署都觸發 puppet 的自動分發。
在持續交付流程中,我們可以清楚的知道當前每個環節,每個節點都處在一個什么版本狀態,這對于清晰的了解,快速回滾非常有用。參見下圖,某項目部分模塊不同環境版本信息(請忽略頁面丑陋這個細節,紅色即表示某個模塊正在發布,還沒最終上線):
4 尾聲
目前聯想企業網盤的服務已經全面采用流程化的上線交付體系,從研發環境到測試環境到生產環境,全部是流水線作業,保證了各個模塊間代碼和版本的一致性,代表的集成、發布只需要我們輕點一下鼠標,然后就可以喝著茶耐心等待收到發布成功的郵件了。
持續交付是一個長期的需要不斷完善的過程,公司的策略在變,產品需求在變,人在變,流程也在變,我們所做的僅僅是開始,還需要繼續去摸索,磨合,打造出更為完善的交付系統。這是一個任何軟件開發團隊都需要重點考慮的事情,建立規范,制定流程,利用科學的工具來實踐規范和流程,脫離小作坊式的交付模式,按時按質按量交付產品。
關于聯想企業網盤:
若想了解關于聯想企業網盤的更多信息,請訪問公司網址:http://box.lenovo.com