近兩個月由于個人處于新環境、新項目的適應階段,沒怎么提筆寫些文章。中間有好幾個想法想記錄下來分享,但受限于沒有很好的時間段供自己總結思考(也可以總結為間歇性懶癌和劇癌發作),便啥也沒有更新。借這個周末閑適的下午和明媚的陽光,決定把近來項目上的CI/CD(持續集成/持續交付)策略以及git分支模型和以前的項目做一下分析比較,希望對各位有所幫助,也能有所思考,尤其是那些期望搭建項目部署流水線或者想了解git分支模型的開發、運維人員。
背景
??廢話不多說,由于近期做了N次release,所以對自己目前所處的新項目的部署方式有了一定的了解。為了方便,本文就叫該項目為A項目吧。發現A項目的部署方式和我之前接觸的TW“傳統”CI/CD策略差異比較大(在TW,幾乎每個項目都有持續集成/持續交付流水線,如果你對它們的概念還不是很清楚,建議閱讀持續交付這本書,將對你梳理整個交付流程幫助巨大)。
??關于A項目的背景,受客戶保密協議的限制,我只能透露幾點。A項目所屬公司為國外某大型電信運營商,主要內容為用戶賬戶自服務平臺。該平臺涉及諸多內外部服務,如認證、訂單跟蹤、短信認證等等,數量總數在三十多個左右,而每個服務都是一個獨立的子系統,有獨立的代碼庫、獨立的機器實例(AWS EC2 實例)用于運行,以及一套獨立的jenkins job用于自動化構建和部署(即我們接下來談的內容)。當然,這也是為什么A項目想往微服務架構遷移的主要目的。
??接下來,讓我剝去諸多項目的其他內容,僅僅討論一下它的CI/CD策略,也可以說是它的構建、部署方式。
A項目的CI/CD策略
??千言萬語還是不及一張圖(作者小學美術數學老師教的,望見諒):
??上圖,為一個獨立子項目(如背景中所說的某個服務)在其jenkins里面的任務(job)結構圖,主要有兩種自動化任務,build和deploy:
build - 即構建任務。developer在代碼倉庫(這里是github上某個私有倉庫)某個分支上提交了代碼后,自動或者手動地被觸發。它會根據對應的分支,如develop、一些feature分支或release分支上,而在其對應的任務上構建、運行各層測試以及生成對應的AMI鏡像。
deployment - 即部署任務。該任務需要人工手動點擊觸發,因為很多時候需要改動一些部署配置,比如說選擇剛剛build任務生成的哪個分支的那個AMI文件以及更改一些endpoint的值。它會根據你需要部署的環境,利用自動化部署工具chef,基于對應的AMI鏡像生成對應的EC2實例、ELB等等資源,讓我們的服務在對應的環境中正式地運行起來(當然也伴隨著銷毀舊的資源的過程)。這個過程如果目標環境是prod的話,其實就是真實的發布了。
這用在該項目組中幾乎所有的以服務為單位的子系統之上,也就是說,我們有將近三十套左右類似這樣的jenkins任務。
??需要說明的是,上圖中的黑色圓圈、黑色圓圈加橫線和黑色空心圓圈分別代表完全自動化、需要手動更改配置后點擊觸發 和 需要手動點擊出發 三種情況,即如下圖所示:
??這樣的方式有如下幾個特點:
以分支和環境為中心 這種策略在構建時以分支(branch) 來區分構建的產物,如果你的工作模式是在各個不同的分支上開發且測試的話,你可以基于分支的相互獨立的進行對應的部署和測試。舉個栗子,如果你基于feature1分支構建生成了一個AMI鏡像,然后你基于該鏡像部署它到qa-1環境中,然后同樣的將feature2分支部署到qa-2環境中,然后測試人員就可以同時在兩個環境測試不同的功能。
保持了CI/CD中的自動化 構建和部署實際上還是自動化的,不過需要在運行自動化腳本之前,手動更新一些配置,比如該使用哪個AMI鏡像等。
自動化測試時間不會特別長 這里所說的特別長其實不容易定義,具體多長時間為長,都是相對而論。個人感覺,只要你覺得不用給各層測試做獨立的jenkins任務(全都放在build中),仍然可以清晰的知道什么時候運行什么測試,什么測試出現了問題,即可。
環境之間的遞進關系不明顯 這種策略下,由于是手動選擇和觸發部署過程,所以一次代碼更改可能不會被部署到所有環境中,可能只會被部署到某一個測試環境中用于測試。所以環境之間的遞進(如下文中越來越接近產品環境的)只能體現它對應的部署任務里的一些配置參數上,比如說preprod環境的部署job用的是真實數據庫,而QA環境的部署job用的是mock的數據。
B項目的策略及比較
??而我曾經接觸過的一些項目,同樣為了便于說明,這里我們統稱它為B項目,不管它的CI/CD工具用的是jenkins還是go.cd,它們都會是一種流水線(pipeline)的形式,如下圖所示(沒錯,請叫我靈魂畫師,<手動羞恥臉>):
??如上圖所示,相對A項目的策略來說,這些jenkins任務分的更加細,中間的各層測試視具體項目而言可能包含單元測試、集成測試、回歸測試、集成測試等等,然后就是將其部署到Dev環境(開發人員手動測試、驗證的環境)。毋庸置疑,這里從開發人員提交代碼到部署至Dev環境,包括測試的運行在內,都是自動化的。這意味著如果你的代碼沒有問題,你不需要做任何事,除了提交代碼和看一下這個pipeline的狀態。之后的幾個環境,由于越來越接近產品環境,而且會提供給不同的人用于測試或者演示(showcase),所以很多時候需要對應的人手動的觸發對應的部署/發布。當然,這樣的部署/發布過程也是自動化,所以說在發布到產品環境之前,類似的部署/發布方式其實已經被驗證過很多次了,而且是一次更改必須強制性地必須經過各個環境的測試和驗證。
??結合《持續交付》一書中提到的部署流水線的三個目標而言,我們來比較一下A項目和B項目用的這兩種部署策略優缺點:
可視化 - 讓軟件的構建、部署、測試和發布過程對所有人可見,這一點對于合作至關重要。A項目這種分離的任務形式,其實不夠直觀,也不太能夠讓開發人員之外的業務人員、管理人員等直觀地明白我們在哪里出現了問題,任務的劃分也相對簡單。B項目的這種策略,任務劃分相對直觀明了,任何人只要關注這條流水線,就大概知道應該是什么流程出現了問題。
及時反饋 - 持續交付的最大好處其實就是及時反饋了。而這一點在A和B上都有體現,任務的成功與失敗都可以給出對應的反饋,告訴我們是否哪兒出了問題。不過A相對來說,反饋方式(可視化程度)更弱一點,反饋周期(集成周期較長)更長一點。
自動化 - 很明顯,從上面兩個圖可以看出,B的自動化程度肯定是高于A的,無論是構建還是部署,A都需要去手動更改配置和手動觸發。不過兩種策略中間的實現毋庸置疑都是自動化的。
如果只從上面看,其實B項目的策略理應優于A項目的策略的。但是,很顯然,“沒那麼簡單 就能去愛 別的全不看”。還記得我們說過A項目服務眾多嗎,A的采用這種策略很大一部分原因,個人猜測(還未經驗實),一是重視任務之間的隔離性,二是為了便于管理各個服務之間依賴。比如,在A項目中,我想把之前feature1的某個測試環境里面的某個服務改為另外一個合適的版本,我只需要在部署時,將部署任務執行前的某個參數改為對應的endpoint就行,這在B項目策略中雖然也是可行的,但A項目的方式相當于在每次部署前都會提醒你這些參數的值,你可以決定是否修改。
??當然,我個人覺得這與它們的git分支模型也不無幾分關系。接下來就讓我們來看看它們分別使用什么樣的git分支模型。
A項目的git分支模型
??A項目使用的git分支模型 - git flow(如果你還不了解這個概念,請閱讀A successful Git branching model):
??簡單介紹一下的各種分支:
master - 與產品環境代碼保持一致的分支,也就是每次發布完成之后發布的功能分支就要合并于此,以保持master更新。
develop - 開發的主分支,feature和release分支會基于此分支。
feature - 具體要開發的功能的分支,完成后合并到develop。
release - 用于發布新版本的分支,完成后合并到develop和master。
hotfix - 用于緊急修復已發布的產品問題的分支,完成后合并到develop和master。
這種模型的話,理論上來說相對安全。但是一般feature分支都是需要用于開發一個較大的功能才做的分支,在此之上,我們還要建對應的故事卡(敏捷中,一個不可/不宜劃分的需求單位)的分支,如下所示:
??這么做的好處有:
隔離性比較好,更加安全 所有的功能都會有對應的分支,開發和測試工作不會互相干擾,發布進程也不會受其他未開發完的功能干擾。
分支職責明確 對應的分支做對應的事情,職責明確。
但是缺點也比較明顯:
集成的周期太長 如果同時有幾個大的功能在各自的分支上開發,每個功能的開發周期都不短的話,那之后他們之間的合并、集成工作將會十分痛苦。如果以《持續集成》這一本書中觀點來看,這甚至算不上持續集成。
會有比較多的重復測試 完成分支的測試之后,在集成到主分支之后,還要重復一遍測試。自動化測試重復還可以接受,重復地手動的測試就比較煩人了。
結構相對復雜 分支較多,且存在層級關系(比如故事卡分支出自feature分支,feature分支出自develop分支)。
B項目的git分支模型
??對應地,B項目,存在分支的話(我這么說,是因為也有不使用分支的真實項目),以我之前的某個離岸海外項目為例,會像如下圖所示:
??明顯地,這種結構看起來簡單很多。所有分支都是基于develop或者叫master這樣的主分支?;诠适驴ńǚ种В喜⒎种?。
??這么做有如下好處:
結構相對簡單 所有分支的都是以故事卡為單位,結構簡單。全部圍繞一條主分支。
符合小步提交、持續集成思想 以一張故事卡為集成的最小單位,相對來說集成的周期短,反饋的速度也快,能夠及早的遇到問題,從而及早的解決問題。
但是,金無足赤,它有時候也可能會有一些缺點:
feature toggle的引入與測試 這種模型下,為了不讓某些沒有完成的功能影響已經完成的功能發布進程。在軟件的設計初級以及后期測試,都需要把對應的feature toggle加入進來。也就是說,需要確保在各個環境中那些沒有完成的功能應該處于disable狀態。這無疑增加一部分工作量,也會帶來一點風險。不過,這種工作量和風險大部分團隊都會承擔,畢竟如果計劃分析的合理,發生的幾率還是挺小的。
隔離性較差 引入feature toggle的很大一部分原因就是為了彌補隔離性上的缺陷。但是如果你主張:所有的分支終究是要合并到一個分支、發布成一個產品的,那這一缺點其實并不重要。
總結
??當然,還有很多其他的策略和分支模型(或者沒有分支的模型),我這里不再探討過多。其實就我目前提到的AB兩種,甚至可以交叉使用(比如A項目情況用B項目的策略),具體如何采用以及何時適合采用,這個問題可以留給有心的讀者自己思考。
??最后我想說,這幾種方式雖然各有優缺點,但相比更加傳統的缺乏自動化的方式而言,已然進步太多。