前幾天障癌,在一個(gè)群中和群友關(guān)于軟件開發(fā)關(guān)于gitflow進(jìn)行了討論浪听,其實(shí)也蠻有意思膳凝,所以特地也寫一篇來記錄和說明一下討論的重點(diǎn)之所在。
討論的起因是有兩位小伙伴在朋友圈里轉(zhuǎn)了《DevOps: 項(xiàng)目多環(huán)境配置和健康檢查》一文龟再,匆匆看完之后迄靠,我立即就震驚了筒占。腦中立馬響起了靈魂三問:
1. Merge后沒測(cè)試的包可以上生產(chǎn)舌涨?
2. 靠merge能保證不同的代碼一致么?
3. 到底應(yīng)該在哪個(gè)分支上集成和測(cè)試抓半?
爭論點(diǎn)分析
在《DevOps: 項(xiàng)目多環(huán)境配置和健康檢查》一文中喂急,對(duì)于各個(gè)環(huán)境的叫法還是非常傳統(tǒng)的,分別叫做dev(開發(fā))笛求、sit(system integration test 集成測(cè)試)廊移、uat(user acceptance test, 用戶接受測(cè)試)、prod(生產(chǎn)環(huán)境)探入,而非現(xiàn)在DevOps的測(cè)試狡孔、預(yù)發(fā)布、灰度蜂嗽、生產(chǎn)等叫法苗膝。就我多年以來非常傳統(tǒng)的軟件開發(fā)的經(jīng)驗(yàn)來看,在一個(gè)相對(duì)面向產(chǎn)品的軟件開發(fā)過程中植旧,軟件配置管理的基線必須是產(chǎn)生在相對(duì)一致的過程中辱揭,即代碼庫的TAG ,代碼的二進(jìn)制包病附,測(cè)試和發(fā)布的二進(jìn)制包问窃,二進(jìn)制包對(duì)應(yīng)的文檔必須保證一致性。一條相對(duì)穩(wěn)定的基線的上的源代碼完沪,二進(jìn)制包域庇,文檔(產(chǎn)品文檔,測(cè)試報(bào)告覆积,發(fā)布說明)必須是完全一致性的听皿。
在特殊的情況下,針對(duì)于一些應(yīng)急的臨時(shí)的發(fā)布的二進(jìn)制包(hotfix)宽档,可以在基線代碼或上一個(gè)hotfix的代碼基礎(chǔ)上做少量的改動(dòng)写穴,做一些少量的回歸甚至不回歸而直接發(fā)布/ 上線。即使這樣雌贱,這些應(yīng)急的代碼最后仍然要走正常的流程啊送,回到相應(yīng)的代碼庫里,并生成相應(yīng)TAG和二進(jìn)制包欣孤,并進(jìn)行必要的回歸測(cè)試馋没,同時(shí)再發(fā)布到其他(客戶)系統(tǒng)中。
于是我把這些問題帶到了群里給小伙伴們降传,得到了轉(zhuǎn)發(fā)者的回復(fù)篷朵。他給出經(jīng)典的GitFlow《參考資料1》中的圖片給我解釋,開發(fā)一般是在Develop分支上工作婆排,Master分支是已發(fā)功能的主干声旺。
并說流程應(yīng)該是測(cè)試通過-上線成功-最后merge到Master,這樣才能保證master 不被污染段只。好吧腮猖,這又是打破我認(rèn)知的一種說法。因?yàn)樵贑VS/SVN時(shí)代Head代表的是最新的代碼赞枕,而我一直以來的工作方式是Head是不用受控的澈缺,可以放開讓團(tuán)隊(duì)成提交代碼,而發(fā)布分支炕婶,特別是已經(jīng)有發(fā)布的分支是受控的姐赡,團(tuán)隊(duì)提交的代碼必須經(jīng)過Review,打包柠掂、打上TAG后提交測(cè)試项滑。那難道是我對(duì) GitLab和DevOps的認(rèn)知出了問題,才導(dǎo)致有如此大的偏差涯贞?
關(guān)于GitFlow的再理解
在認(rèn)真的讀了《A successful Git branching model》一文的原文和相關(guān)的中文譯文后枪狂,并且仔細(xì)看了一下GitLab Admin 的管理功能后,才發(fā)現(xiàn)了大家分歧的點(diǎn)在哪里肩狂。
1. "production ready" 的意義:如果我們是一個(gè)DevOps的團(tuán)隊(duì)摘完,那么,我理解的是這個(gè)狀態(tài)應(yīng)該是準(zhǔn)備好可以上生產(chǎn)環(huán)境了傻谁。但是孝治,在Develop分支測(cè)試完成,merge完之后审磁,是不是就production ready了呢谈飒?其實(shí)技術(shù)上來說,也未必态蒂。如果是一個(gè)相對(duì)大的系統(tǒng)杭措,在這里測(cè)試完成,其實(shí)只是微服務(wù)里某個(gè)服務(wù)測(cè)試完成了钾恢,還需要上和生產(chǎn)環(huán)境更為接近的預(yù)發(fā)布系統(tǒng)進(jìn)行測(cè)試手素。那在提交預(yù)發(fā)布前鸳址,應(yīng)該是用master上出的包去提交。如果測(cè)試通過后續(xù)還有灰度流程泉懦,都應(yīng)該只是部署過程稿黍。如果測(cè)試不通過,則應(yīng)該還是修改代碼崩哩,然后重走merge 巡球,構(gòu)建流程。
2. Release 分支的意義:在GtiFlow的定義里邓嘹,真實(shí)的定義是準(zhǔn)備Release的分支酣栈。如果你的團(tuán)隊(duì)在提交測(cè)試期間除修復(fù) Bug外還有人力的話,那么在這個(gè)期間如果向Develop分支 push代碼就會(huì)導(dǎo)致新的Feature 代碼和Bug Fix的代碼都被提交汹押。這時(shí)我們就需要Release 分支了矿筝。在提測(cè)期間,新功能的代碼將被提交到Develop分支鲸阻,而修復(fù)Bug則在Release分支跋涣。
3. 缺失的維護(hù)分支:在Gitlab的建議里,如果是產(chǎn)品型鸟悴,長期維護(hù)的產(chǎn)品陈辱,可以定義維護(hù)分支,對(duì)應(yīng)于已經(jīng)發(fā)布的版本细诸。在這點(diǎn)上DevOps團(tuán)隊(duì)可能就比較少有這樣的概念沛贪,因?yàn)檎G闆r下就是開發(fā)一版上一版拋棄一版。每一個(gè)版本都不需要長期維護(hù)震贵。但如果是一個(gè)產(chǎn)品型的團(tuán)隊(duì)利赋,就需要真正的定義維護(hù)分支了。舉個(gè)例子說猩系,微軟同會(huì)維護(hù)著Win Xp, Win 7媚送,Win 10等若干個(gè)Windows的版本,需要不同的維護(hù)分支對(duì)應(yīng)不同的windows版本寇甸,然后在這些維護(hù)分支上出hotfix塘偎。
4. Feature分支的意義:僅為某個(gè)功能的開發(fā)而打的分支。宏觀的來看拿霉,我更關(guān)心在服務(wù)器上對(duì)哪些分支做CI/CD吟秩,而非每一個(gè)人工作在哪個(gè)分支上,如果團(tuán)隊(duì)或者某幾個(gè)開發(fā)者在開發(fā)過程中臨時(shí)打了分支绽淘,而這些分支在合并回Develop時(shí)已經(jīng)被刪除涵防,其實(shí)在CI/CD的過程甚至應(yīng)該是忽略這些分支,或者要避免這些分支在CI/CD過程中打出的包對(duì)相應(yīng)系統(tǒng)的影響沪铭。
個(gè)人的幾點(diǎn)看法
說完了分歧點(diǎn)壮池,其實(shí)歸根到底這還是一個(gè)軟件配置管理的問題偏瓤,而現(xiàn)在各互聯(lián)網(wǎng)公司的實(shí)踐中,軟件配置管理往往又和CI/CD等緊緊的綁定在一起椰憋。那究竟應(yīng)該怎么做硼补,才是對(duì)的呢?說到此熏矿,我想起了一位特別能侃的項(xiàng)目管理界的教授的話,在工程領(lǐng)域离钝,對(duì)票编、錯(cuò)并不是最重要的,而要把項(xiàng)目完成了才行卵渴。其實(shí)就我個(gè)人的經(jīng)驗(yàn)來看
1. 上線的包慧域,是必須經(jīng)過測(cè)試的。功能回歸浪读,安裝測(cè)試等等都必須在盡可能與生產(chǎn)一致的環(huán)境中經(jīng)過測(cè)試才能上線昔榴。master上的包,直接上預(yù)生產(chǎn)再經(jīng)過一輪測(cè)是可以的碘橘,但直接上生產(chǎn)是一般是難以接受的互订。
2. 一致性是必須保持的,即源代碼痘拆,二進(jìn)制包仰禽,TAG/Branch以及在后續(xù)的測(cè)試、發(fā)布的工作中大家說的必須是同一個(gè)事情纺蛆。如果說吐葵,測(cè)試上報(bào)問題,說出來的類似 我測(cè)的是你昨天給的包扒攀稀温峭;開發(fā)又問,我?guī)c(diǎn)給的啊字支。這樣的溝通其實(shí)是很低效而且很容易出錯(cuò)的凤藏。正確的方式就是測(cè)試上報(bào) 0.2.x版本,任意一個(gè)開發(fā)都可以通過 這個(gè)版本號(hào)找到對(duì)應(yīng)的源代碼祥款。
3. 團(tuán)隊(duì)手工操作的一致性其實(shí)是很難保證的清笨,甚至在TAG/Branch的機(jī)制的理解上產(chǎn)生偏差也很正常。Git flow ?其實(shí)是對(duì)此提供了一系列的IDE插件來做的刃跛。
那吐槽完了抠艾,究竟應(yīng)該如何做呢?其實(shí)對(duì)于不同性質(zhì)的項(xiàng)目和產(chǎn)品桨昙,不同成熟度的開發(fā)團(tuán)隊(duì)检号,我覺得做法也會(huì)略有不同腌歉。
1. 小型幾乎無專職測(cè)試的項(xiàng)目團(tuán)隊(duì):dev+master 分支,在dev上開發(fā)齐苛,merge 到master分支上tag后出包翘盖。因?yàn)檫@個(gè)狀態(tài)的團(tuán)隊(duì)人員少,而且可能沒有專門的測(cè)試時(shí)間窗口凹蜂,開發(fā)完成后馍驯,少量的自測(cè)即可上線。所以一切以最小代碼為目標(biāo)玛痊。在極端情況下汰瘫,例如團(tuán)隊(duì)只有2-3人,只有master分支也未嘗不可擂煞。走的流程就是TAG混弥,出包,測(cè)試对省,上線蝗拿。
2. 有專職測(cè)試的項(xiàng)目團(tuán)隊(duì):dev+ release +master分支,或者 feature + dev + master分支蒿涎。在有專業(yè)測(cè)試的團(tuán)隊(duì)里哀托,在項(xiàng)目的(或每個(gè)迭代的)后期大量的測(cè)試任務(wù)都是在測(cè)試團(tuán)隊(duì)完成,這時(shí)開發(fā)團(tuán)隊(duì)會(huì)進(jìn)行新的功能的開發(fā)同仆。如果我們只有dev+master模式萤捆,在測(cè)試的后期,如果把新功能提交到dev分支的話俗批,就會(huì)對(duì)bug fix的merge造成污染俗或。所以我們需要在這個(gè)階段有三個(gè)分支,除了dev 和master分支外岁忘,要增加 replease 分支來對(duì)應(yīng)當(dāng)前迭代/ 版本的bug fix辛慰。
3. 產(chǎn)品團(tuán)隊(duì):master +? 版本維護(hù)分支或者dev + master +版本維護(hù)分支。
客戶化部署的多版本產(chǎn)品維護(hù):
為什么要這么分呢干像?如下圖所示帅腌,Git flow里其實(shí)是假設(shè)2.0永遠(yuǎn)比1.X的版本要新。
但真實(shí)情況里并不是如此麻汰,舉一個(gè)簡單的例子速客, Python 2.7.16就是比絕大多數(shù)的3.X的版本要新。在這種情況下五鲫,我們就需要版本的維護(hù)分支了溺职。整個(gè)情況如下圖所示:
由上圖可以看到,如果是個(gè)產(chǎn)品型的團(tuán)隊(duì),其實(shí)情況反而要比DevOps的情況復(fù)雜的多的多浪耘。以上圖為例乱灵,在1.0對(duì)外發(fā)布之前,一切都很簡單七冲。只有master和Develop痛倚,和DevOps也差不多。到了1.0甚至2.0發(fā)布之后澜躺,情況變的越來越復(fù)雜了:
1. Dev-1 merge到Master蝉稳,經(jīng)過測(cè)試之后,可以發(fā)布1.0 版本掘鄙,在Master上打上Tag
2. 可以在Develop上繼續(xù)開發(fā)颠区,前進(jìn)到Dev-2
3. 在1.0發(fā)布之后,如果用戶報(bào)了問題通铲,那么需要從1.0的TAG打出分支,進(jìn)行修復(fù)器贩,發(fā)布1.1颅夺。
4. 與此同時(shí),這個(gè)bugfix的代碼還要回到Dev分支蛹稍,在Develop 分到上得到Dev-3
5. 開發(fā)人員繼續(xù)開發(fā)吧黄,在Develop上得到Dev-4,經(jīng)過測(cè)試唆姐,merge到主干拗慨,驗(yàn)證后可以對(duì)外發(fā)布2.0版本
6. 此時(shí)再報(bào)Bug,首先要判斷是在1.0上的代碼導(dǎo)致 的奉芦,還是僅影響2.0版本赵抢。假設(shè)僅影響2.0版本,那么需要從2.0 上分支声功,然后進(jìn)行bug fix得到2.1版本烦却。再merge回develop,得到Dev-5
7. 如果這個(gè)bug是由于1.0上的代碼影響先巴,那么需要從1.x進(jìn)行修復(fù)其爵。在圖上這樣的情況,從1.1修復(fù)伸蚯,得到1.2版本摩渺。但此時(shí)要向Develop和2.x分支merge,在2.x分支上發(fā)布2.2版本剂邮;merge回developt得到Dev-6
從上面這些描述可以看到摇幻,如果是一個(gè)維護(hù)多分支多版本的產(chǎn)品,會(huì)面臨更多更復(fù)雜的情況。例如囚企,原則上維護(hù)分支是不接受新功能的開發(fā)的丈咐,新功能的開發(fā)必須在develop上分支進(jìn)行。那如果在上圖的結(jié)束時(shí)間點(diǎn)上龙宏,有一個(gè)重點(diǎn)客戶還在使用1.2版本棵逊,而且他們也不愿意做大版本升級(jí)到2.2版本。但這個(gè)客戶提了一個(gè)非常小的需求银酗,如果不滿足他就會(huì)投向友商辆影。此時(shí)應(yīng)該怎么做呢?似乎又來了一個(gè)靈魂之問黍特,對(duì)于開發(fā)團(tuán)隊(duì)來說蛙讥,Bug和需求真正的區(qū)別在哪里呢?
?關(guān)于Protected分支:
在GitLab的項(xiàng)目設(shè)置里灭衷,有開放分支和保護(hù)分支的說法次慢,保護(hù)分支是不允許提交/推送(commit/push)代碼,只允許 merge代碼翔曲,并可以通過Web界面來審核 merge request迫像。在git flow 的建議里,一般像1.x和2.x 是設(shè)置成保護(hù)分支瞳遍,需要再額外分支和增加merge request闻妓。產(chǎn)品型的團(tuán)隊(duì)可以根據(jù)自己的情況來設(shè)置是否需要在維護(hù)分支上設(shè)置protected ?標(biāo)志。
在Master上是否要測(cè)試掠械?
答案是肯定的由缆。在《Kubernetes如何加速UCloud內(nèi)部代碼部署的CI/CD流程》一文也提到了相應(yīng)的過程
則開發(fā)流程為:
1. 首先,在 Gitlab 上創(chuàng)建了編號(hào)為80的Issue猾蒂,跟進(jìn)這個(gè)optimize-allocate的feature均唉;
2. 從dev分支創(chuàng)建一個(gè)新分支,名為feature/80-optimize-allocate肚菠,在該分支上進(jìn)行開發(fā)浸卦;
3. 在feature/80-optimize-allocate上開發(fā)完成,進(jìn)行commit案糙,此時(shí)會(huì)觸發(fā)靜態(tài)測(cè)試限嫌、單元測(cè)試、Review等Pipeline Job时捌;
4. 測(cè)試通過后怒医,創(chuàng)建一個(gè)從feature/80-optimize-allocate到dev的merge request,由負(fù)責(zé)人進(jìn)行審核奢讨。審核通過并且merge成功后稚叹,觸發(fā)靜態(tài)測(cè)試、單元測(cè)試、鏡像構(gòu)建扒袖、鏡像部署塞茅、集成測(cè)試等Pipeline Job;
5. 測(cè)試通過后季率,創(chuàng)建一個(gè)從dev到master的mergerequest野瘦,由負(fù)責(zé)人進(jìn)行審核。審核通過并且merge成功后飒泻,負(fù)責(zé)人創(chuàng)建tag v1.1.1鞭光,然后觸發(fā)靜態(tài)測(cè)試、單元測(cè)試泞遗、鏡像構(gòu)建惰许、鏡像部署、集成測(cè)試等Pipeline Job史辙;
注:版本號(hào)tag是有命令規(guī)范的汹买,v{x}.{y}.{z}代表著v{主版本}.{次版本}.{小修訂版本}
最后,我想說的是聊倔,流程必須為自己團(tuán)隊(duì)/公司的工作而服務(wù)的卦睹。在實(shí)際的工作中,除了面臨自己產(chǎn)品和公司的流程的問題外方库,可能還會(huì)面臨諸如團(tuán)隊(duì)人員素質(zhì)等更為復(fù)雜的情形。具體采取什么樣的方式還是要根據(jù)團(tuán)隊(duì)的實(shí)際情況來制定相應(yīng)的工作流程障斋。流程無對(duì)錯(cuò)纵潦,適合最重要。
參考資料
1. https://nvie.com/posts/a-successful-git-branching-model/ 英文原文 2010-1-5
2. https://blog.csdn.net/shuzheng520/article/details/84906515 基于gitflow的開發(fā)上線流程?
3. https://blog.csdn.net/liubenlong007/article/details/69372348 Git 在團(tuán)隊(duì)中的最佳實(shí)踐--如何正確使用Git Flow
4.DevOps: 項(xiàng)目多環(huán)境配置和健康檢查
5. https://blog.csdn.net/master_yao/article/details/78622648 使用gitlab做git flow及代碼審查
6.Kubernetes如何加速UCloud內(nèi)部代碼部署的CI/CD流程UCloud技術(shù)