對敏捷研發(fā)和DevOps過程實踐的若干問題思考總結(jié)
在前面提到云原生完整技術(shù)解決方案的時候,已經(jīng)提到了公司DevOps技術(shù)支撐平臺和容器云解決方案。但是DevOps絕對不是一個簡單的開源技術(shù)集成或者技術(shù)解決方案,而是結(jié)合本身的微服務(wù)架構(gòu)優(yōu)化,敏捷開發(fā)的企業(yè)研發(fā)過程改進和持續(xù)優(yōu)化。
敏捷研發(fā)和CI/CD持續(xù)集成過程脫節(jié),往往才是當(dāng)前最大的一個問題點。
今天談的重點不是DevOps具體的底層開源工具鏈和技術(shù),而是對于敏捷研發(fā),微服務(wù)如何更好的和DevOps過程形成一個高度協(xié)同的整體。在這個過程實踐中,可能會形成多篇文章,都是對我們實踐過程的一些問題總結(jié),思考和復(fù)盤。
先從單體微服務(wù)拆分談起
圖片
最早的時候,我們自己的DevOps管控治理平臺拆分為20多個微服務(wù),20多個微服務(wù)每個都是獨立的項目,獨立設(shè)計流水線,部署在獨立的容器里面??上攵麄€后續(xù)集成,部署和運維管控的復(fù)雜度有多大。
在去年,團隊進行了重構(gòu),將微服務(wù)進行了合并,在合并完成后包括基礎(chǔ)組件和能力中心微服務(wù),一共在10個左右的微服務(wù)模塊。合并到這個粒度后基本才處于一種可控的狀態(tài)。
微服務(wù)拆分的顆粒度實際上還和你團隊規(guī)模有關(guān)系,當(dāng)你團隊規(guī)模本身就不大的時候一定不要拆分的太細,一個人如果就管理多個微服務(wù),實際當(dāng)初進行微服務(wù)劃分,希望進行的邊界和解耦往往根本就無法做到。
在合并完成后仍然存在兩個問題。
其一是數(shù)據(jù)庫仍然沒有拆分是一個大數(shù)據(jù)庫。如果從理想的微服務(wù)架構(gòu)來說,并沒有做到完全的微服務(wù)化,數(shù)據(jù)庫層面沒有解耦。
但是當(dāng)你數(shù)據(jù)庫本身就沒有海量并發(fā)和數(shù)據(jù)量大幅擴展的壓力時候,你的數(shù)據(jù)庫為啥要拆分?數(shù)據(jù)庫本身不要為了拆分而拆分,數(shù)據(jù)庫的拆分更多是為了擴展性的需求。當(dāng)構(gòu)建的應(yīng)用在DB層本身沒有太大的性能壓力的時候,實際沒必要馬上就去做數(shù)據(jù)庫的拆分。
其次,我在很早就提出了微服務(wù)域的概念,即使上層是10個微服務(wù),你的數(shù)據(jù)庫本身也不需要就一定拆分為10個,而是應(yīng)該根據(jù)微服務(wù)域的劃分來進行數(shù)據(jù)庫的拆分。比如上層的編譯,構(gòu)建,流水線,交付管理等微服務(wù),完全可以合并在一個數(shù)據(jù)庫里面。
其二是在前后端分離情況下,整個平臺的BS前端合并在一個項目里面。這個我個人認(rèn)為并不太合理,也就是說一個功能如果涉及到前端應(yīng)用有改動,那么整個前端應(yīng)用都需要重新部署。比如資源管理部分的前端界面變化了,實際整個應(yīng)用前端都需要重新部署,那么對于流水線,資產(chǎn)庫這些微服務(wù)模塊對應(yīng)的前端是否造成影響并不清楚。
也就是說前端并沒有做到完全的解耦。
如果你是開發(fā)一個APP應(yīng)用功能,那么前端整合為一個項目無可厚非,但是如果是傳統(tǒng)的企業(yè)級的PC端的BS應(yīng)用,最好的方式仍然是前端需要進行分離。
從需求用戶故事到任務(wù)拆分
圖片
在敏捷研發(fā)里面我們強調(diào)基于用戶故事進行全流程的跟蹤。我們將收集的需求進行分析,將需求定義為用戶故事或需求點,同時將需求規(guī)劃到具體的項目版本中。這個是最基本的產(chǎn)品-項目-項目版本的分解過程。
當(dāng)需求規(guī)劃到項目版本后,一個重點就是將需求轉(zhuǎn)變?yōu)榫唧w的任務(wù)。你采取的不同研發(fā)過程,不同的管控顆粒度下,實際上任務(wù)的分解本身是有標(biāo)準(zhǔn)可以遵循的。如果從傳統(tǒng)的方式下,任何一個需求點往往包括了如下任務(wù)分解:
1.資源配置功能需求
1.1 資源配置需求文檔編寫
1.2 資源配置功能開發(fā)
1.3 資源配置測試用例編寫
1.4 資源配置功能測試
這是最常見的一個任務(wù)分解安排。
在敏捷開發(fā)和前后端分離下,你可以看到一個功能的開發(fā)同時涉及到前端和后端,后端開發(fā)完成后輸出的是接口,前端基于接口進行集成和聯(lián)調(diào)。在這個過程中測試人員又需要接入進行測試,其一是針對后端開發(fā)完成的接口測試,其二是針對前端完成的功能做黑盒測試。那么基于這個思路,你會看到任何一個功能的實現(xiàn)都可以分解為如下:
1.資源配置管理功能實現(xiàn)
1.1. 需求開發(fā)-》需求人員
1.2 后端功能和API接口開發(fā)-》后端開發(fā)
1.3 API接口測試-》測試人員
1.4 前端功能開發(fā)和集成-》前端開發(fā)
1.5 功能整體測試-》測試人員
當(dāng)思考到這里的時候,實際我們希望的是,對于敏捷研發(fā)項目管理工具,在你的開發(fā)模式確定后,基于某個需求點的子任務(wù)拆分應(yīng)該是自動化進行的?;蛘哒f可以基于標(biāo)準(zhǔn)的開發(fā)任務(wù)分配模板進行自動的子任務(wù)生成。
如果做到這點,實際上還是沒有辦法解決問題。因為我們?nèi)蝿?wù)的跟蹤實際上還是按照單個任務(wù)的方式,按未開發(fā)-進行中-已完成等任務(wù)狀態(tài)進行看板跟蹤。
但是我們實際需要的是按照開發(fā)模式關(guān)鍵技術(shù)進行任務(wù)跟蹤。比如前面談到的我們跟蹤的是用戶故事或需求點,我們關(guān)心的狀態(tài)是當(dāng)前用戶故事處于需求開發(fā)階段,還是后端開發(fā),還是前端開發(fā)集成階段。這個才是關(guān)鍵的看板跟蹤點。
也就是說傳統(tǒng)看板你看到的是類似下圖:
圖片
但是實際上我們希望看到的是基于需求或用戶故事點為核心的看板。這個看板并不是現(xiàn)實具體的子任務(wù),而是只到任務(wù)基本,子任務(wù)影響到的是看板面板卡片的狀態(tài)。
圖片
比如上圖,我們可以很清楚的看到當(dāng)前迭代版本一共規(guī)劃了11個功能點,同時每個功能點當(dāng)前在哪個階段或狀態(tài)。其次,對于某個崗位角色的人上來,也可以很清楚的看到他當(dāng)前自己的關(guān)鍵todo事項,他要做的是盡快完成自己泳道的事情,將任務(wù)狀態(tài)轉(zhuǎn)移到下個階段。也就是說當(dāng)做了如上改進了時候,才能夠更好的做到敏捷研發(fā)模式和敏捷任務(wù)看板管理的一個融合協(xié)同。
從需求變更到項目版本規(guī)劃
在這里我們將已有功能的需求變更和新增的小需求都納入到需求變更的范疇。當(dāng)前談CI/CD持續(xù)集成和持續(xù)部署,更多的都是應(yīng)用系統(tǒng)上線后的缺陷修改,需求變更引發(fā)的迭代版本開發(fā)和部署操作。
因此需求變更才是后續(xù)軟件應(yīng)用持續(xù)集成的一個基礎(chǔ)輸入。
圖片
在前面談產(chǎn)品和項目兩級流水線設(shè)計的時候,我就談到了一個產(chǎn)品拆分為了多個微服務(wù)模塊,每個微服務(wù)模塊都相對獨立和解耦。
但是用戶最終看到的仍然是整個應(yīng)用系統(tǒng)。
一次需求變更過來后,我們通過分析最終需要確定的就是涉及到哪幾個微服務(wù)模塊需要變更。在分析清楚后,最好的方式就是僅僅變更的微服務(wù)模塊需要重新進行持續(xù)編譯,構(gòu)建和部署集成,而對于沒有變更的模塊不應(yīng)該進行重新的編譯構(gòu)建操作。
微服務(wù)下對傳統(tǒng)單體應(yīng)用解耦后,最基本要做到的就是某個微服務(wù)如果沒有變更,就不應(yīng)該去重復(fù)地進行編譯和部署,任何重復(fù)多余的編譯部署操作往往都容易引入新的缺陷或問題。
比如上圖的例子,一次需求變更過來我們規(guī)劃V2版本,但是實際上只有綠色的三個微服務(wù)模塊需要進行版本升級和變更,而灰色的三個并沒有變更,不用進行重新的編譯構(gòu)建等操作。在這個時候容器管理部分的功能在應(yīng)用發(fā)版后不需要進行回歸測試,即使容器管理部分功能出現(xiàn)問題,我們也應(yīng)該追溯容器暴露接口相關(guān)的外圍消費和調(diào)用。
當(dāng)這個思考清楚后,你會看到需求變更納入到項目版本,那么我們實際最關(guān)心的是當(dāng)前的項目版本整體進展,這個進展不是只是需求,任務(wù)和缺陷的研發(fā)管理過程和任務(wù),同時也應(yīng)該包括了整個CI/CD過程進展。
簡單來說就是:研發(fā)管理過程和CI/CD過程應(yīng)該基于項目版本主線形成一個完整的類似看板一樣的管理視圖。這個視圖就是整個敏捷團隊的工作界面。
對于這個可視化看板,簡單構(gòu)思應(yīng)該如下:
圖片
也就是說我們希望看到一個完整的基于項目當(dāng)前版本的看板視圖,在這個視圖一個是可以看到當(dāng)前需求,任務(wù)的直接進展情況;其次是可以清楚地看到涉及到當(dāng)前項目版本的編譯構(gòu)建和部署情況。
這個也是我們進一步將研發(fā)任務(wù)管理的狀態(tài)和CI/CD過程進行集成的基礎(chǔ)。
比如我們在前面很多文章里面談到的。
開發(fā)人員對一個開發(fā)任務(wù)反饋完成,這個時候任務(wù)本身的狀態(tài)應(yīng)該是在待部署狀態(tài),這個是一個看板上的隱藏狀態(tài)并不需要人工去關(guān)心。而只有流水線執(zhí)行成功后相關(guān)的任務(wù)才會從待部署狀態(tài)轉(zhuǎn)移到待測試狀態(tài)。
也就是說開發(fā)完成任務(wù),這個功能本身仍然在開發(fā)人員的看板中,只有后臺的流水線執(zhí)行成功,完成了功能的自動部署后,該需求功能才會自動轉(zhuǎn)移到測試中這個看板。
這些關(guān)鍵環(huán)節(jié)必須系統(tǒng)自動銜接。
否則一個功能開發(fā)反饋已經(jīng)完成,但是測試人員上去發(fā)現(xiàn)并不能測試,最終追溯才看到流水線任務(wù)實際執(zhí)行失敗,導(dǎo)致開發(fā)完成的功能并沒有成功部署。這些都勢必會導(dǎo)致大量無效的人工溝通和協(xié)同工作。
流水線觸發(fā)和構(gòu)建頻率
圖片
在DevOps實踐方法論里面,始終在強調(diào)隨時隨地的觸發(fā)構(gòu)建,一天不需要去約束構(gòu)建次數(shù),只要代碼一check in就應(yīng)該觸發(fā)流水線編譯構(gòu)建流程進行構(gòu)建。
在講這個問題前,我想先談兩個例子。
一個是在游樂園里面做一個旋轉(zhuǎn)的游樂設(shè)施,剛開始的時候速度不快,完全能夠接受。但是后面速度越來越快,最后感覺就是腦袋一陣眩暈。實際上這個設(shè)施可以轉(zhuǎn)得更加快或者說頻度跟高,但是就個人來說,你始終會有一個你能夠接受的極限值,超過了就暈。個人由于各自的身體素質(zhì)差異,實際上這個極限值本身存在不同。
還有一個我們寫文章的例子。
比如我寫文章,我專門找了一個編輯幫我進行審核和修訂,我既可以是每寫一小段就發(fā)給對方讓他修改,也可以是我每天或每周發(fā)送一次給對方讓對方統(tǒng)一修改。如果我每寫一個小時就發(fā)送一次,那么對方馬上反饋問題后,我實際的寫作過程都在隨時被打斷,這個顯然是對我正常寫作造成影響。
圖片
回到我們構(gòu)建頻率這件事情上。
實際上構(gòu)建方式或頻率包括了代碼check in就自動構(gòu)建,也可以是人手工發(fā)起構(gòu)建,還可以是每天或每半天定時構(gòu)建一次。如果按DevOps最佳實踐方法是代碼提交即構(gòu)建,但是我要說的是這種方法并不適合大部分的團隊,什么原因呢?
其一是團隊和個人本身的成熟度和敏捷度就無法適應(yīng)這種高頻率構(gòu)建,工具高頻率容易,但是人要高頻率需要的是高度的自律。其二是當(dāng)隨時都在構(gòu)建的時候,你發(fā)現(xiàn)開發(fā)人員隨時都在解決構(gòu)建過程中出現(xiàn)的問題或依賴沖突,導(dǎo)致開發(fā)真正專注在編碼上的時間越來越少,也就是是高頻率構(gòu)建極其容器導(dǎo)致我們開發(fā)時間碎片化,這個顯然是開發(fā)的大忌。
當(dāng)做了上述思考后,最佳的方式仍然是按天或半天定時構(gòu)建,同時對于關(guān)鍵bug的解決根據(jù)業(yè)務(wù)驅(qū)動由測試手工發(fā)起流水線運行。
很多互聯(lián)網(wǎng)應(yīng)用每2到3天就再發(fā)布和迭代版本,但是對于企業(yè)內(nèi)部信息化應(yīng)用,實際上遠遠不需要如此敏捷和高頻,因此我們持續(xù)集成和構(gòu)建的頻率也無須如此。
當(dāng)團隊和個人自身的能力和成熟度達到后,我們可以進一步縮短構(gòu)建頻率,比如從半天到每2個小時一次構(gòu)建。同時對于構(gòu)建頻率的縮短往往還伴隨著PMS任務(wù)顆粒度的細化,你原來的任務(wù)顆粒度是1周或2到3天,那么新的顆粒度則可能是2小時或4小時。
父子流水線和環(huán)境遷移
圖片
首先再次強調(diào)一個關(guān)鍵點,即:
對于CI/CD的價值一定體現(xiàn)在跨環(huán)境的自動遷移部署能力,而不是單個環(huán)境的自動化編譯構(gòu)建和應(yīng)用部署。編譯構(gòu)建的過程只有一次,形成的是二進制文件包;而環(huán)境遷移可以多次并靈活編排,環(huán)境遷移不需要重新編譯構(gòu)建,最終確保基礎(chǔ)依賴的一致性。
也正是如此,環(huán)境遷移一定是我們流水線設(shè)計編排的一個重點。
比如最常見的業(yè)務(wù)場景,我們準(zhǔn)備了SIT集成測試環(huán)境和UAT用戶驗收測試環(huán)境,當(dāng)某個版本的軟件開發(fā)在SIT環(huán)境完成集成測試,所有的Bug都修復(fù)后。我們需要將軟件部署到UAT環(huán)境,并通知用戶進行驗收測試。
如何確保用戶驗收測試的版本就是我們SIT測試通過的版本?
即前面談到的基于二進制和鏡像文件的遷移,對于UAT環(huán)境部署重新編譯部署,而僅僅是鏡像文件在UAT環(huán)境的部署,這是CI/CD強調(diào)的一個重點。
圖片
那么問題的復(fù)雜度在哪里?
即前面談到的傳統(tǒng)的一個單體應(yīng)用系統(tǒng)以及拆分為了10個微服務(wù)模塊,每個微服務(wù)都可以獨立編譯構(gòu)建,打包和部署。每個微服務(wù)都有自己各自的流水線設(shè)計。
但是實際上就一個項目版本來說,我們只關(guān)注這個項目版本的完整性。比如這個項目版本僅僅涉及到3個微服務(wù)模塊要變更,那么在SIT測試通過后就應(yīng)該將這三個微服務(wù)模塊的最新測試通過版本遷移部署到UAT環(huán)境。
也就是說應(yīng)該在微服務(wù)流水線基礎(chǔ)上增加一個對應(yīng)產(chǎn)品或項目版本的流水線。編譯或構(gòu)建是以微服務(wù)為最小顆粒度單位,但是環(huán)境遷移構(gòu)建,是以產(chǎn)品或項目版本為單位進行。
我們還是回到前面的場景,比如DevOps平臺研發(fā)規(guī)劃了V2版本,這個版本涉及到門戶管理,研發(fā)管理,持續(xù)集成三個微服務(wù)模塊的變更和發(fā)布。
對于上面三個微服務(wù)模塊本身就已經(jīng)有自己的獨立編譯構(gòu)建部署流水線任務(wù)。
那么現(xiàn)在重點就是基于本次規(guī)劃的項目版本V2,構(gòu)建一個父流水線,同時將已經(jīng)有的三個微服務(wù)模塊流水線掛接進來,形成一個完整的父子流水線模式。
也就是說在父流水線上我們只會編排環(huán)境遷移的關(guān)鍵節(jié)點,比如:開發(fā)測試環(huán)境-》SIT環(huán)境-》UAT環(huán)境
我們會在流水線上設(shè)置由測試人員參與的手工審核和處理節(jié)點,當(dāng)測試人員確認(rèn)SIT測試通過的時候,我才進行自動化的環(huán)境遷移動作。同時父節(jié)點必須是在各個子流水線運行成功后再跳轉(zhuǎn)到下一個活動節(jié)點狀態(tài)。
比如前面談到發(fā)起一次父流水線運行,那么先去執(zhí)行各個子流水線,子流水線如何全部執(zhí)行成功則將父流水線狀態(tài)轉(zhuǎn)移到待測試狀態(tài)。測試人員在完成一輪測試后,如果不通過則進行不通過處理,將流水線退回到初始狀態(tài)。
同時在進入流水線視圖的時候,可以清楚地看到當(dāng)前整體流水線的執(zhí)行情況,即當(dāng)前項目版本涉及到幾個子流水線,各自對應(yīng)哪個微服務(wù)模塊,每個子流水線是否允許成功,如果失敗可以進入到詳細的流水線任務(wù)查看界面查看原因等。