這才是DevOps演進及CI/CD實踐的正確打開方式!
一、前言
從2016年底發布的第一個版本到如今能夠完全支撐豬八戒網500+研發人員的日常研發工作,DevOps團隊經歷了不斷的試錯和改進總結。本文側重于解決方案,更多細節可以關注八戒技術團隊公眾號獲取,希望對即將實踐DevOps和正在實踐DevOps的團隊有所幫助。
二、從0到1構建DevOps
?1、背景介紹
2015年,歷經10年步步為營,穩定發展的豬八戒網厚積薄發,迎來了業務的快速增長,隨之而來的就是公司人員的壯大,研發團隊從幾十人擴張到了幾百人,而正是人才的引入和業務增長的迫切需求,使得豬八戒網開始了一場轟轟烈烈的改革運動,而這樣的運動,在過去的十年里已經搞了6次,因為公司的取經文化,我們將這樣的運動稱之為“騰云行動”。
而這次的騰云行動,我們主要做了兩件事情:
第一件事是服務拆解,把龐大的單體應用根據業務劃分,模塊功能劃分,拆解成一個個獨立的小應用進行部署;
- 第二件事是服務重構,將之前由PHP編寫的程序用Java重構了80%以上,同時引入以SOA為核心框架的架構體系。
而這兩件事情,將交付周期從之前的月,縮短至周,甚至為天。這無疑對我們的交付能力提出了嚴峻的考驗。
于是,經過充分的調研和準備,以及慎重的決定,在2016年第三季度,研發團隊抽調了部分運維人員和開發人員組建了devops團隊,這個團隊的目標只有一個,那就是滿足頻繁的交付,隨時隨地地交付。
而要實現這個目標,就必須要做到以下幾點:
- 構建標準化的研發流程,使整個交付過程可靠和規范。
- 能一鍵生成可部署工程,一來是為了避免開發重復造輪子,縮短他們的開發周期,二來是統一技術棧,規范研發,降低維護成本。
- 打造自動化的CI/CD流水線,替代人工部署,大幅度提升交付效率。
- 建立線網故障快速回滾機制,給高速生產可能出現的差錯提供應對措施,提升全站可用性。
?2、標準化的研發流程
我們將豬八戒網的業務拆成了一條條業務線,現在將這些業務線抽象成一條條產品線,然后產品線下面有子產品線,子產品線下面就是具體的產品,從而形成了一棵層次分明、業務清晰的產品樹。
接著我們引入了產品責任制的概念,我們可以看到,每一個產品都包含了一些基本信息,這里需要注意的是,每一個產品都必須有一個產品經理,而產品經理也是有歸屬部門的,于是我們就得到了一條責任鏈,產品-產品經理-歸屬部門。
然后,我們又引入了工程責任制概念,同理,我們每個工程也包含了一些信息,如源碼地址、開發語言、運維配置信息,以及我們的工程負責人,每個工程負責人也有歸屬部門,這樣我們也得到了一條責任鏈,工程-工程負責人-歸屬部門。
我們現在將產品與工程關聯起來,并規定每一個工程必須關聯一個產品,這樣就保證了每一個產品都能找到實實在在的生產源碼。
而做到這些還不夠,我們還需要一個東西,把這些全部穿起來,于是我們使用jira構建了四種標準的需求發布流程:
- 新產品上線流程:新產品第一次上線使用該流程,涉及需求評審,產品原型評審,安全評審,技術方案評審等
- 產品大版本迭代上線流程:產品重大變更使用該流程,涉及需求評審,產品原型評審,安全評審,技術方案評審等
- 產品功能迭代上線流程:產品功能模塊日常迭代使用該流程
- bug修復流程:修復bug快速上線使用該流程
現在有了產品樹、產品責任制、工程責任制,以及需求發布流程,我們最后就建立了需求-產品-工程的標準研發生產線。
?3、一鍵生成可部署工程
要做到這點,我們需要實現四個功能:
1)創建源碼倉庫:根據用戶填寫git祖名和工程名自動創建git倉庫。
2)提供各種技術棧的工程模版:根據用戶填寫的開發語言提供對應的工程模版,并在創建git倉庫后,完成初始化,提交到git倉庫。
3)生成部署配置信息:根據用戶填寫的基本信息和系統預設配置動態生成流水線配置信息。
4)生成配置中心信息:生成各環境配置中心信息。
?4、CI/CD流水線
流水線做的其實總結起來就四件事:拉取源碼,編譯構建,將制品上傳制品庫,將制品部署到服務器。
而為了使這個過程可靠、可控以及規范,我們加入了校驗任務,校驗一些準入準出。
其次,再加入了測試任務,如自動化測試等。
然后就形成了這么一條流水線:
最后應用到各個環境后,便成為了以下的流水線:
流水線具有如下功能:
1)支持虛擬機容器兩種發布方式
虛擬機發布,在完成編譯構建之后,把生成的制品上傳到文件服務器,這個文件服務器就相當于是虛擬機發布工程的制品庫,文件服務器上保存了這個工程發布的歷史制品版本,在上傳到文件服務器后,接著會從文件服務器將制品同步到代碼源,最后,虛擬機服務器上的守護進程會檢測代碼的代碼是否發生變更,如果變更,便主動拉取代碼,然后重啟服務。
而容器發布,則會在編譯構建完成之后,根據用戶提供的dockerfile文件構建鏡像,然后將鏡像上傳到公司內部的hub倉庫,接著組裝元數據,調用容器云接口,部署到k8s集群。這里的容器云是豬八戒網自己基于k8s做的二次封裝,主要的功能,就是將元數據拿來處理生成deploy文件,然后調用k8s執行部署操作。
容器發布&虛擬機發布構建打包示意圖
2)多分支開發,主干上線
在測試階段,可以用各種分支進行開發測試,測試通過后,就必須合并到主干,然后用主干進行發布上線。
3)一次構建處處使用
考慮到設置的分支策略,于是我們規定,測試環境的制品只能用于測試環境使用,這樣一來就需要在預發布再進行一次構建操作,而此次的制品因為是經過測試而合并到主干的代碼生成的,所以認為是穩定可靠的,于是在后續的環境中,將不需要再次構建,而直接使用預發布生成的制品。
4)使用Jenkins作為后臺構建作業機器
采用多master,多slave的Jenkins集群方案,其中master只做調度,slave執行確定任務,我們預先在jenkins master上創建了流水線對應的job,圖中的左邊是我們自研的流水線服務,用Java編寫的,通過調用jenkins API觸發構建,jenkins master調度slave節點執行job,然后左邊的流水線服務定時調Jenkins API獲取構建狀態和結果,實時更新推送記錄的狀態和日志。
現在,我們將需求發布流程和流水線結合起來,就能得到下圖所示的標準生產過程。
?5、線網故障快速回滾機制
上面我們講了流水線,現在來講一下,如果上線出現了問題,如何進行回滾。不知道大家有沒有注意到上圖的一個細節,那就是在預發布的時候,有一個任務——打tag,而這個操作就是我們實現回滾的關鍵。
這個打tag主要做了兩件事情:
- 在git上生成tag,代表此次的代碼是穩定的,可以上線的;
- 保存了一條記錄,代碼版本和制品版本以及本次tag的記錄。
現在我們看看這種情況:
比如現在我們發布了一次線上,發布的tag版本是v1.3.35,對應的代碼版本是a,鏡像是A,這次發布是成功的,沒問題的。
然后我們又發布了一個版本到線上,v1.3.36,對應的版本是a,鏡像是A,當發布到線上后,發現服務異常,于是需要進行回滾操作,選擇上一次成功的版本v1.3.35,因為保存了這個版本對應的代碼信息和鏡像信息,所以當選擇這個版本時就能找到這個正確的制品,然后觸發一次流水線,就進行了回滾。整個過程可以控制到幾十秒內,讓線網故障導致的損失降到最低。
最后,我們看一下整個devops的生態鏈:
至此,我們的devops第一階段完成。
1)帶來的意義與價值:
- 研發過程標準化,責任制管理研發生產資料,交付過程更可靠;
- 提供多種工程模板,無需從零搭建工程,降低開發成本的同時,統一技術棧,規范代碼研發;
- CI/CD自動化,支持高頻構建(支持500+研發人員日常構建),降低運維成本(運維同學從40人減少到10人);
- 線網故障快速回滾,提升全站可用率。
2)不足之處:
- 流水線執行過程不夠靈活,導致負載過高,耗費更多資源;
- 工程全生命周期管理缺失關鍵路徑,大量工程處于散養狀態;
基礎服務和工具眾多,需要多個平臺間切換,增加開發人員負擔;
沒有高效自助執行的研發類工作流,大量實際工作需要人工處理;
缺乏成本管控手段,服務器成本居高不下。
三、從DevOps到一站式研發平臺
我們針對以上不足做了以下改造:
?1、重構流水線
1)把流水線的任務拆解成一個個獨立的原子任務,并將原子人按功能劃分為校驗類和執行類。
2)根據工程的開發語言,發布方式,以及推送環境,預設了一套流水線任務列表。
可以看到,這里面有兩種箭頭,分別代碼同步任務和異步任務:
- 同步任務串行執行,若失敗會阻斷流程;
- 異步任務并行執行,若失敗,不會阻斷流程。
3)自研了Jenkins rabbitMQ插件,實現流水線服務與Jenkins之間通過消息隊列通信。
重構之前,前面講到是通過調用Jenkins API的方式實現的,而重構之后,流水線服務組裝好構建信息后將消息發布到隊列里,jenkins 插件消費消息,然后調度slave執行任務,同時將狀態和結果生成消息也發布到隊列中,流水線服務消費消息更新日志和狀態,這種方式極大地提升了成功率。
4)Jenkins slave節點容器化
重構之前,所有的slave節點都是虛擬機,這樣就導致節點數量固定,要么造成資源浪費,要么無法滿足高并發,而且維護成本較高,一旦涉及改動,需要人工更改每一個節點。
重構之后,我們利用k8s 插件,鏈接我們的k8s集群,創建slave節點。利用k8s特性,可動態調整節點數,既滿足高并發,又不造成資源浪費,并且維護簡單,一旦涉及改動,只需要重新構建slave鏡像應用即可。
重構之后的流水線有如下特點:
- 流水線執行任務更加靈活,可按照實際情況動態調整執行任務,實現“因地制宜”;
- 提升了流水線執行任務的成功率,實現高可用;
- 通過k8s特性實現Jenkins slave節點動態擴縮容,滿足高并發的同時,節約服務器資源。
?2、工程全生命周期管理
1)工程創建階段
定義六大工程類型,完全覆蓋所有研發需求,且配置簡單,一鍵創建。
2)工程研發階段
根據工程類型生成配套組件,研發階段全面賦能。
配套組件有工程權限管理、工程服務管理、工程資源管理、配置中心管理、調度任務管理、域名管理、安全管理等。
3)工程上線階段
統一需求發布流程,cicd流水線標準生產,保證每一步的可靠性。
4)工程運行階段
實時監控服務,多種維度的異常告警機制。
5)工程下線階段
智能檢測中心檢測無用工程,360度檢查工程依賴項,一鍵下線,操作簡單。
由此我們實現了工程的全生命周期管理:
?3、整合基礎服務和工具
1)提供一站式查詢和使用
2)提供各類工具使用文檔
?4、高效研發工作流
1)三步自定義工作流模版
- 首先是自定義流程信息,填寫工作流的一些簡單信息;
- 然后是自定義表單內容,我們提供了大量豐富的表單組件,如文本框、單選框、復選框等;
- 最后是自定義扭轉節點,可以設置每個節點的經辦人,如果是系統執行,則會根據工作流類型執行對應的后臺任務。
有了工作流模版,我們就可以創建工作流了。
2)三步創建工作流
- 首先是選擇工作流模版;
- 然后是填寫表單,就是對應工作流設置的表單;
- 最后提交,即可完成工作流的創建。
3)實時記錄工作流狀態和執行過程
當創建完一條工作流后,這條工作流所有的執行狀態以及過程都會被清晰地記錄下來,如圖從發起到各個節點的扭轉,以及執行結果、執行時間。
4)數十種系統自動執行節點任務
如果經辦人是系統,那么就會根據當前工作流類型執行對應的系統任務,我們預置了幾十種系統執行任務,基本覆蓋了所有的研發需求,如數據庫相關操作、運維相關操作。
5)實時通知經辦人和進行催辦
在扭轉到某個節點的時候,我們通過系統通知和用戶催辦,盡可能地縮短工作流的辦理時間。
至此我們得到高效扭轉的研發工作流。
?5、研發成本管理系統
1)產品緯度統計各部門服務器成本費用。
前面我們提到,每一個工程都會關聯到一個產品,而每個產品都有歸屬部門,由此我們便能更具產品緯度統計部門的服務器成本費用,當然,不僅是服務器成本費,也可以是其他費用,如代運維費,開發人員成本等,都可以照此計算。
2)監控每個部門的服務器成本是否超過預算,若超過預算則不允許發布上線。
至此,我們的devops第二階段完成,總結一下成果和價值:
- 流水線2.0豐富靈活的原子任務支持各種業務場景,在支持高并發、高可用的同時,不造成資源浪費;
- 對工程進行全生命周期管理,保證研發資料100%掌控;
- 一站式管理基礎服務和工具,減少開發人員負擔;
- 強大高效的工作流系統,極大提升研發效率;
- 成本管理系統,在記錄每一個產品的研發費用的同時,嚴格管控研發成本。
四、結語
DevOps實踐之路還在繼續,因為不同公司有不同的業務場景,而同一公司的業務也會隨著時代的發展不斷變化,只有適合自己的才是最好的,只有能擁抱變化的才是最好的,但萬變不離其宗的,我覺得應該有以下幾點:
1)DevOps應該是以提高研發效率為目標的實踐,脫離了這個目標,做得再好也只是炫技。
2)DevOps應該是緊貼業務的,因為業務的不同,要求的技術架構也會有所不同,隨之而來,要求的交付方式也會有所不同。
3)DevOps應該是以人為本的,我們應該盡可能地將一切繁瑣的過程交給程序去執行,而人只需要“坐享其成”或者做少量的決策即可。?