這才是DevOps演進及CI/CD實踐的正確打開方式!
一、前言
從2016年底發(fā)布的第一個版本到如今能夠完全支撐豬八戒網(wǎng)500+研發(fā)人員的日常研發(fā)工作,DevOps團隊經(jīng)歷了不斷的試錯和改進總結(jié)。本文側(cè)重于解決方案,更多細節(jié)可以關(guān)注八戒技術(shù)團隊公眾號獲取,希望對即將實踐DevOps和正在實踐DevOps的團隊有所幫助。
二、從0到1構(gòu)建DevOps
?1、背景介紹
2015年,歷經(jīng)10年步步為營,穩(wěn)定發(fā)展的豬八戒網(wǎng)厚積薄發(fā),迎來了業(yè)務(wù)的快速增長,隨之而來的就是公司人員的壯大,研發(fā)團隊從幾十人擴張到了幾百人,而正是人才的引入和業(yè)務(wù)增長的迫切需求,使得豬八戒網(wǎng)開始了一場轟轟烈烈的改革運動,而這樣的運動,在過去的十年里已經(jīng)搞了6次,因為公司的取經(jīng)文化,我們將這樣的運動稱之為“騰云行動”。

而這次的騰云行動,我們主要做了兩件事情:
第一件事是服務(wù)拆解,把龐大的單體應(yīng)用根據(jù)業(yè)務(wù)劃分,模塊功能劃分,拆解成一個個獨立的小應(yīng)用進行部署;
- 第二件事是服務(wù)重構(gòu),將之前由PHP編寫的程序用Java重構(gòu)了80%以上,同時引入以SOA為核心框架的架構(gòu)體系。
 

而這兩件事情,將交付周期從之前的月,縮短至周,甚至為天。這無疑對我們的交付能力提出了嚴峻的考驗。
于是,經(jīng)過充分的調(diào)研和準備,以及慎重的決定,在2016年第三季度,研發(fā)團隊抽調(diào)了部分運維人員和開發(fā)人員組建了devops團隊,這個團隊的目標只有一個,那就是滿足頻繁的交付,隨時隨地地交付。
而要實現(xiàn)這個目標,就必須要做到以下幾點:
- 構(gòu)建標準化的研發(fā)流程,使整個交付過程可靠和規(guī)范。
 
- 能一鍵生成可部署工程,一來是為了避免開發(fā)重復(fù)造輪子,縮短他們的開發(fā)周期,二來是統(tǒng)一技術(shù)棧,規(guī)范研發(fā),降低維護成本。
 
- 打造自動化的CI/CD流水線,替代人工部署,大幅度提升交付效率。
 
- 建立線網(wǎng)故障快速回滾機制,給高速生產(chǎn)可能出現(xiàn)的差錯提供應(yīng)對措施,提升全站可用性。
 
?2、標準化的研發(fā)流程
我們將豬八戒網(wǎng)的業(yè)務(wù)拆成了一條條業(yè)務(wù)線,現(xiàn)在將這些業(yè)務(wù)線抽象成一條條產(chǎn)品線,然后產(chǎn)品線下面有子產(chǎn)品線,子產(chǎn)品線下面就是具體的產(chǎn)品,從而形成了一棵層次分明、業(yè)務(wù)清晰的產(chǎn)品樹。

接著我們引入了產(chǎn)品責(zé)任制的概念,我們可以看到,每一個產(chǎn)品都包含了一些基本信息,這里需要注意的是,每一個產(chǎn)品都必須有一個產(chǎn)品經(jīng)理,而產(chǎn)品經(jīng)理也是有歸屬部門的,于是我們就得到了一條責(zé)任鏈,產(chǎn)品-產(chǎn)品經(jīng)理-歸屬部門。

然后,我們又引入了工程責(zé)任制概念,同理,我們每個工程也包含了一些信息,如源碼地址、開發(fā)語言、運維配置信息,以及我們的工程負責(zé)人,每個工程負責(zé)人也有歸屬部門,這樣我們也得到了一條責(zé)任鏈,工程-工程負責(zé)人-歸屬部門。

我們現(xiàn)在將產(chǎn)品與工程關(guān)聯(lián)起來,并規(guī)定每一個工程必須關(guān)聯(lián)一個產(chǎn)品,這樣就保證了每一個產(chǎn)品都能找到實實在在的生產(chǎn)源碼。

而做到這些還不夠,我們還需要一個東西,把這些全部穿起來,于是我們使用jira構(gòu)建了四種標準的需求發(fā)布流程:
- 新產(chǎn)品上線流程:新產(chǎn)品第一次上線使用該流程,涉及需求評審,產(chǎn)品原型評審,安全評審,技術(shù)方案評審等
 
- 產(chǎn)品大版本迭代上線流程:產(chǎn)品重大變更使用該流程,涉及需求評審,產(chǎn)品原型評審,安全評審,技術(shù)方案評審等
 
- 產(chǎn)品功能迭代上線流程:產(chǎn)品功能模塊日常迭代使用該流程
 
- bug修復(fù)流程:修復(fù)bug快速上線使用該流程
 

現(xiàn)在有了產(chǎn)品樹、產(chǎn)品責(zé)任制、工程責(zé)任制,以及需求發(fā)布流程,我們最后就建立了需求-產(chǎn)品-工程的標準研發(fā)生產(chǎn)線。

?3、一鍵生成可部署工程
要做到這點,我們需要實現(xiàn)四個功能:
1)創(chuàng)建源碼倉庫:根據(jù)用戶填寫git祖名和工程名自動創(chuàng)建git倉庫。
2)提供各種技術(shù)棧的工程模版:根據(jù)用戶填寫的開發(fā)語言提供對應(yīng)的工程模版,并在創(chuàng)建git倉庫后,完成初始化,提交到git倉庫。
3)生成部署配置信息:根據(jù)用戶填寫的基本信息和系統(tǒng)預(yù)設(shè)配置動態(tài)生成流水線配置信息。
4)生成配置中心信息:生成各環(huán)境配置中心信息。
?4、CI/CD流水線

流水線做的其實總結(jié)起來就四件事:拉取源碼,編譯構(gòu)建,將制品上傳制品庫,將制品部署到服務(wù)器。

而為了使這個過程可靠、可控以及規(guī)范,我們加入了校驗任務(wù),校驗一些準入準出。

其次,再加入了測試任務(wù),如自動化測試等。

然后就形成了這么一條流水線:

最后應(yīng)用到各個環(huán)境后,便成為了以下的流水線:

流水線具有如下功能:
1)支持虛擬機容器兩種發(fā)布方式
虛擬機發(fā)布,在完成編譯構(gòu)建之后,把生成的制品上傳到文件服務(wù)器,這個文件服務(wù)器就相當(dāng)于是虛擬機發(fā)布工程的制品庫,文件服務(wù)器上保存了這個工程發(fā)布的歷史制品版本,在上傳到文件服務(wù)器后,接著會從文件服務(wù)器將制品同步到代碼源,最后,虛擬機服務(wù)器上的守護進程會檢測代碼的代碼是否發(fā)生變更,如果變更,便主動拉取代碼,然后重啟服務(wù)。
而容器發(fā)布,則會在編譯構(gòu)建完成之后,根據(jù)用戶提供的dockerfile文件構(gòu)建鏡像,然后將鏡像上傳到公司內(nèi)部的hub倉庫,接著組裝元數(shù)據(jù),調(diào)用容器云接口,部署到k8s集群。這里的容器云是豬八戒網(wǎng)自己基于k8s做的二次封裝,主要的功能,就是將元數(shù)據(jù)拿來處理生成deploy文件,然后調(diào)用k8s執(zhí)行部署操作。

容器發(fā)布&虛擬機發(fā)布構(gòu)建打包示意圖
2)多分支開發(fā),主干上線
在測試階段,可以用各種分支進行開發(fā)測試,測試通過后,就必須合并到主干,然后用主干進行發(fā)布上線。

3)一次構(gòu)建處處使用
考慮到設(shè)置的分支策略,于是我們規(guī)定,測試環(huán)境的制品只能用于測試環(huán)境使用,這樣一來就需要在預(yù)發(fā)布再進行一次構(gòu)建操作,而此次的制品因為是經(jīng)過測試而合并到主干的代碼生成的,所以認為是穩(wěn)定可靠的,于是在后續(xù)的環(huán)境中,將不需要再次構(gòu)建,而直接使用預(yù)發(fā)布生成的制品。

4)使用Jenkins作為后臺構(gòu)建作業(yè)機器
采用多master,多slave的Jenkins集群方案,其中master只做調(diào)度,slave執(zhí)行確定任務(wù),我們預(yù)先在jenkins master上創(chuàng)建了流水線對應(yīng)的job,圖中的左邊是我們自研的流水線服務(wù),用Java編寫的,通過調(diào)用jenkins API觸發(fā)構(gòu)建,jenkins master調(diào)度slave節(jié)點執(zhí)行job,然后左邊的流水線服務(wù)定時調(diào)Jenkins API獲取構(gòu)建狀態(tài)和結(jié)果,實時更新推送記錄的狀態(tài)和日志。

現(xiàn)在,我們將需求發(fā)布流程和流水線結(jié)合起來,就能得到下圖所示的標準生產(chǎn)過程。

?5、線網(wǎng)故障快速回滾機制
上面我們講了流水線,現(xiàn)在來講一下,如果上線出現(xiàn)了問題,如何進行回滾。不知道大家有沒有注意到上圖的一個細節(jié),那就是在預(yù)發(fā)布的時候,有一個任務(wù)——打tag,而這個操作就是我們實現(xiàn)回滾的關(guān)鍵。
這個打tag主要做了兩件事情:
- 在git上生成tag,代表此次的代碼是穩(wěn)定的,可以上線的;
 
- 保存了一條記錄,代碼版本和制品版本以及本次tag的記錄。
 
現(xiàn)在我們看看這種情況:


比如現(xiàn)在我們發(fā)布了一次線上,發(fā)布的tag版本是v1.3.35,對應(yīng)的代碼版本是a,鏡像是A,這次發(fā)布是成功的,沒問題的。
然后我們又發(fā)布了一個版本到線上,v1.3.36,對應(yīng)的版本是a,鏡像是A,當(dāng)發(fā)布到線上后,發(fā)現(xiàn)服務(wù)異常,于是需要進行回滾操作,選擇上一次成功的版本v1.3.35,因為保存了這個版本對應(yīng)的代碼信息和鏡像信息,所以當(dāng)選擇這個版本時就能找到這個正確的制品,然后觸發(fā)一次流水線,就進行了回滾。整個過程可以控制到幾十秒內(nèi),讓線網(wǎng)故障導(dǎo)致的損失降到最低。
最后,我們看一下整個devops的生態(tài)鏈:

至此,我們的devops第一階段完成。
1)帶來的意義與價值:
- 研發(fā)過程標準化,責(zé)任制管理研發(fā)生產(chǎn)資料,交付過程更可靠;
 
- 提供多種工程模板,無需從零搭建工程,降低開發(fā)成本的同時,統(tǒng)一技術(shù)棧,規(guī)范代碼研發(fā);
 
- CI/CD自動化,支持高頻構(gòu)建(支持500+研發(fā)人員日常構(gòu)建),降低運維成本(運維同學(xué)從40人減少到10人);
 
- 線網(wǎng)故障快速回滾,提升全站可用率。
 
2)不足之處:
- 流水線執(zhí)行過程不夠靈活,導(dǎo)致負載過高,耗費更多資源;
 
- 工程全生命周期管理缺失關(guān)鍵路徑,大量工程處于散養(yǎng)狀態(tài);
 
基礎(chǔ)服務(wù)和工具眾多,需要多個平臺間切換,增加開發(fā)人員負擔(dān);
沒有高效自助執(zhí)行的研發(fā)類工作流,大量實際工作需要人工處理;
缺乏成本管控手段,服務(wù)器成本居高不下。
三、從DevOps到一站式研發(fā)平臺
我們針對以上不足做了以下改造:
?1、重構(gòu)流水線
1)把流水線的任務(wù)拆解成一個個獨立的原子任務(wù),并將原子人按功能劃分為校驗類和執(zhí)行類。

2)根據(jù)工程的開發(fā)語言,發(fā)布方式,以及推送環(huán)境,預(yù)設(shè)了一套流水線任務(wù)列表。

可以看到,這里面有兩種箭頭,分別代碼同步任務(wù)和異步任務(wù):
- 同步任務(wù)串行執(zhí)行,若失敗會阻斷流程;
 
- 異步任務(wù)并行執(zhí)行,若失敗,不會阻斷流程。
 
3)自研了Jenkins rabbitMQ插件,實現(xiàn)流水線服務(wù)與Jenkins之間通過消息隊列通信。

重構(gòu)之前,前面講到是通過調(diào)用Jenkins API的方式實現(xiàn)的,而重構(gòu)之后,流水線服務(wù)組裝好構(gòu)建信息后將消息發(fā)布到隊列里,jenkins 插件消費消息,然后調(diào)度slave執(zhí)行任務(wù),同時將狀態(tài)和結(jié)果生成消息也發(fā)布到隊列中,流水線服務(wù)消費消息更新日志和狀態(tài),這種方式極大地提升了成功率。
4)Jenkins slave節(jié)點容器化

重構(gòu)之前,所有的slave節(jié)點都是虛擬機,這樣就導(dǎo)致節(jié)點數(shù)量固定,要么造成資源浪費,要么無法滿足高并發(fā),而且維護成本較高,一旦涉及改動,需要人工更改每一個節(jié)點。
重構(gòu)之后,我們利用k8s 插件,鏈接我們的k8s集群,創(chuàng)建slave節(jié)點。利用k8s特性,可動態(tài)調(diào)整節(jié)點數(shù),既滿足高并發(fā),又不造成資源浪費,并且維護簡單,一旦涉及改動,只需要重新構(gòu)建slave鏡像應(yīng)用即可。
重構(gòu)之后的流水線有如下特點:
- 流水線執(zhí)行任務(wù)更加靈活,可按照實際情況動態(tài)調(diào)整執(zhí)行任務(wù),實現(xiàn)“因地制宜”;
 
- 提升了流水線執(zhí)行任務(wù)的成功率,實現(xiàn)高可用;
 
- 通過k8s特性實現(xiàn)Jenkins slave節(jié)點動態(tài)擴縮容,滿足高并發(fā)的同時,節(jié)約服務(wù)器資源。
 
?2、工程全生命周期管理
1)工程創(chuàng)建階段

定義六大工程類型,完全覆蓋所有研發(fā)需求,且配置簡單,一鍵創(chuàng)建。
2)工程研發(fā)階段

根據(jù)工程類型生成配套組件,研發(fā)階段全面賦能。
配套組件有工程權(quán)限管理、工程服務(wù)管理、工程資源管理、配置中心管理、調(diào)度任務(wù)管理、域名管理、安全管理等。
3)工程上線階段
統(tǒng)一需求發(fā)布流程,cicd流水線標準生產(chǎn),保證每一步的可靠性。
4)工程運行階段
實時監(jiān)控服務(wù),多種維度的異常告警機制。

5)工程下線階段

智能檢測中心檢測無用工程,360度檢查工程依賴項,一鍵下線,操作簡單。
由此我們實現(xiàn)了工程的全生命周期管理:

?3、整合基礎(chǔ)服務(wù)和工具

1)提供一站式查詢和使用
2)提供各類工具使用文檔
?4、高效研發(fā)工作流
1)三步自定義工作流模版

- 首先是自定義流程信息,填寫工作流的一些簡單信息;
 
- 然后是自定義表單內(nèi)容,我們提供了大量豐富的表單組件,如文本框、單選框、復(fù)選框等;
 
- 最后是自定義扭轉(zhuǎn)節(jié)點,可以設(shè)置每個節(jié)點的經(jīng)辦人,如果是系統(tǒng)執(zhí)行,則會根據(jù)工作流類型執(zhí)行對應(yīng)的后臺任務(wù)。
 
有了工作流模版,我們就可以創(chuàng)建工作流了。
2)三步創(chuàng)建工作流

- 首先是選擇工作流模版;
 
- 然后是填寫表單,就是對應(yīng)工作流設(shè)置的表單;
 
- 最后提交,即可完成工作流的創(chuàng)建。
 
3)實時記錄工作流狀態(tài)和執(zhí)行過程

當(dāng)創(chuàng)建完一條工作流后,這條工作流所有的執(zhí)行狀態(tài)以及過程都會被清晰地記錄下來,如圖從發(fā)起到各個節(jié)點的扭轉(zhuǎn),以及執(zhí)行結(jié)果、執(zhí)行時間。
4)數(shù)十種系統(tǒng)自動執(zhí)行節(jié)點任務(wù)

如果經(jīng)辦人是系統(tǒng),那么就會根據(jù)當(dāng)前工作流類型執(zhí)行對應(yīng)的系統(tǒng)任務(wù),我們預(yù)置了幾十種系統(tǒng)執(zhí)行任務(wù),基本覆蓋了所有的研發(fā)需求,如數(shù)據(jù)庫相關(guān)操作、運維相關(guān)操作。
5)實時通知經(jīng)辦人和進行催辦


在扭轉(zhuǎn)到某個節(jié)點的時候,我們通過系統(tǒng)通知和用戶催辦,盡可能地縮短工作流的辦理時間。
至此我們得到高效扭轉(zhuǎn)的研發(fā)工作流。

?5、研發(fā)成本管理系統(tǒng)
1)產(chǎn)品緯度統(tǒng)計各部門服務(wù)器成本費用。

前面我們提到,每一個工程都會關(guān)聯(lián)到一個產(chǎn)品,而每個產(chǎn)品都有歸屬部門,由此我們便能更具產(chǎn)品緯度統(tǒng)計部門的服務(wù)器成本費用,當(dāng)然,不僅是服務(wù)器成本費,也可以是其他費用,如代運維費,開發(fā)人員成本等,都可以照此計算。
2)監(jiān)控每個部門的服務(wù)器成本是否超過預(yù)算,若超過預(yù)算則不允許發(fā)布上線。

至此,我們的devops第二階段完成,總結(jié)一下成果和價值:
- 流水線2.0豐富靈活的原子任務(wù)支持各種業(yè)務(wù)場景,在支持高并發(fā)、高可用的同時,不造成資源浪費;
 
- 對工程進行全生命周期管理,保證研發(fā)資料100%掌控;
 
- 一站式管理基礎(chǔ)服務(wù)和工具,減少開發(fā)人員負擔(dān);
 
- 強大高效的工作流系統(tǒng),極大提升研發(fā)效率;
 
- 成本管理系統(tǒng),在記錄每一個產(chǎn)品的研發(fā)費用的同時,嚴格管控研發(fā)成本。
 
四、結(jié)語
DevOps實踐之路還在繼續(xù),因為不同公司有不同的業(yè)務(wù)場景,而同一公司的業(yè)務(wù)也會隨著時代的發(fā)展不斷變化,只有適合自己的才是最好的,只有能擁抱變化的才是最好的,但萬變不離其宗的,我覺得應(yīng)該有以下幾點:
1)DevOps應(yīng)該是以提高研發(fā)效率為目標的實踐,脫離了這個目標,做得再好也只是炫技。
2)DevOps應(yīng)該是緊貼業(yè)務(wù)的,因為業(yè)務(wù)的不同,要求的技術(shù)架構(gòu)也會有所不同,隨之而來,要求的交付方式也會有所不同。
3)DevOps應(yīng)該是以人為本的,我們應(yīng)該盡可能地將一切繁瑣的過程交給程序去執(zhí)行,而人只需要“坐享其成”或者做少量的決策即可。?















 
 
 









 
 
 
 