導(dǎo)語
6月23日慎皱,蘑菇街技術(shù)專家蔣志強(qiáng)在GITC全球互聯(lián)網(wǎng)技術(shù)大會上發(fā)表了題為《持續(xù)集成和發(fā)布在美聯(lián)的實(shí)踐》的演講莫瞬,介紹了蘑菇街和美麗說合并成立的美聯(lián)發(fā)布系統(tǒng)的演進(jìn)和特色罐栈。
演講嘉賓介紹
蘑菇街
發(fā)布作為應(yīng)用上線前的最后一個(gè)步驟外厂,一直以來都是運(yùn)維做的比較頻繁也是風(fēng)險(xiǎn)比較高的操作,發(fā)布系統(tǒng)不僅要做到提升發(fā)布效率豌骏,更重要的是保障發(fā)布過程中系統(tǒng)的穩(wěn)定龟梦,減少因發(fā)布導(dǎo)致的故障。本次演講主要包括三個(gè)部分窃躲。一是發(fā)布系統(tǒng)的演進(jìn)计贰,二是美聯(lián)發(fā)布系統(tǒng)的實(shí)踐之路,三是發(fā)布系統(tǒng)的特色蒂窒。
智能運(yùn)維 系統(tǒng)做決策
發(fā)布系統(tǒng)主要可以分成三個(gè)階段躁倒,首先就是人肉運(yùn)維,什么都要靠自己秧秉。然后進(jìn)化到自動(dòng)化運(yùn)維。自動(dòng)化運(yùn)維分兩個(gè)階段荧嵌,首先是使用一些開源的工具,搭建一套運(yùn)維體系砾淌、運(yùn)維工具啦撮。但是隨著公司業(yè)務(wù)的繼續(xù)發(fā)展拇舀,慢慢的會發(fā)現(xiàn),雖然開源軟件功能非常強(qiáng)大骄崩,但這些用開源軟件搭建的運(yùn)維工具不一定能適應(yīng)公司自己的需求聘鳞。但是如果要進(jìn)行二次開發(fā)或者功能的增加,成本也很大要拂,因?yàn)椴涣私馑唧w的實(shí)現(xiàn)邏輯脱惰。慢慢的就會進(jìn)入到一個(gè)自研運(yùn)維系統(tǒng)的過程。
最后拉一,智能運(yùn)維蔚润,我個(gè)人理解磅氨,智能運(yùn)維跟自動(dòng)化運(yùn)維最大的區(qū)別就是烦租,智能運(yùn)維可以自動(dòng)做一些決策除盏。自動(dòng)化運(yùn)維,所有的操作還是要手動(dòng)觸發(fā)窃祝,但是智能運(yùn)維踱侣,系統(tǒng)會自動(dòng)對各種數(shù)據(jù)進(jìn)行分析甩栈,做出合適的決策糕再。
舉個(gè)最簡單的例子突想,應(yīng)用擴(kuò)容究抓。雖然現(xiàn)在有一套比較完善的擴(kuò)容系統(tǒng),但是還是需要由PE自己去決定擴(kuò)容的時(shí)間和臺數(shù)绑嘹。但如果是智能運(yùn)維橘茉,把智能的擴(kuò)容系統(tǒng)做好以后,系統(tǒng)就可以自己根據(jù)監(jiān)控的數(shù)據(jù)或者其他系統(tǒng)的數(shù)據(jù)畅卓,自動(dòng)的決策是否需要進(jìn)行擴(kuò)容翁潘。當(dāng)然,這是我們的目標(biāo)拜马,我們正在朝這個(gè)目標(biāo)努力俩莽。
蘑菇街技術(shù)架構(gòu)演進(jìn)
蘑菇街創(chuàng)業(yè)初期的技術(shù)架構(gòu)旺坠,開發(fā)語言用了PHP价淌,運(yùn)行環(huán)境用了非常流行的四件套:Liunx瞒津、PHP、MySQL病毡、Nginx屁柏。技術(shù)架構(gòu)很簡單的分成三層有送,用的基本上全是開源僧家。只支持PHP的發(fā)布。PHP發(fā)布阵赠,整個(gè)邏輯比較簡單肌稻,只需要把代碼打包放到服務(wù)器上解壓爹谭。有了這個(gè)發(fā)布系統(tǒng),至少可以做到可以選擇需要發(fā)布的文件诺凡,并且知道需要發(fā)布的這個(gè)文件的版本號是多少绑洛。把運(yùn)維從發(fā)布上解放了出來。這個(gè)過程中參與發(fā)布的主要是開發(fā)而不是運(yùn)維脸候。
中期绑蔫,蘑菇街從一個(gè)單純的導(dǎo)購網(wǎng)站變成了一個(gè)電商的平臺。作為一個(gè)電商平臺携添,就必須要有的詳情頁篓叶、購物車缸托、下單、支付這些子系統(tǒng)矫限,包括一系列用來支撐的后臺的運(yùn)營系統(tǒng)和基礎(chǔ)服務(wù)。臃腫的PHP工程在一個(gè)代碼倉庫里面取董,如果把這么多電商業(yè)務(wù)都放在PHP這一個(gè)倉庫里面的話无宿,上百個(gè)開發(fā)對于同一個(gè)倉庫進(jìn)行開發(fā),不可想象经窖。所以要把業(yè)務(wù)進(jìn)行拆分梭灿,JAVA服務(wù)化冰悠。
我們把應(yīng)用逐漸拆成JAVA一個(gè)一個(gè)的服務(wù)化溉卓。為了適應(yīng)這個(gè)需求,就有了第一版的JAVA的發(fā)布系統(tǒng)伏尼。JAVA的發(fā)布系統(tǒng)和PHP有很大的不同尉尾,PHP只要解壓,而JAVA還要重啟容器辨图,這就涉及到在重啟之前要關(guān)閉報(bào)警肢藐、切走流量吆豹,否則應(yīng)用一重啟一發(fā)布,就會收到一大堆的報(bào)警短信凑阶,影響用戶的訪問速勇,這些都是不可接受的。發(fā)布系統(tǒng)的發(fā)布邏輯就會有很多的步驟养匈。
但是隨著時(shí)間的進(jìn)步,公司業(yè)務(wù)持續(xù)的發(fā)展积担,蘑菇街和美麗說合并猬仁,雖然兩家都是電商公司湿刽,但是在底層的運(yùn)維基礎(chǔ)上,還是有比較大的不同渴庆。多了很多不同類型的應(yīng)用雅镊,這些應(yīng)用都要納入到發(fā)布的管理里面來。
發(fā)布系統(tǒng)的構(gòu)建之路
首先要做運(yùn)維工具耸弄,最重要的一點(diǎn)就是要把標(biāo)準(zhǔn)化做好计呈。標(biāo)準(zhǔn)化跟發(fā)布相關(guān)的僚饭,主要有兩點(diǎn),一個(gè)是基礎(chǔ)環(huán)境苇瓣,和基礎(chǔ)環(huán)境的配置需要標(biāo)準(zhǔn)偿乖,OS贪薪、JDK、TOMCAT等等竣稽。另外,應(yīng)用要支持標(biāo)準(zhǔn)娃弓。為了支持這些標(biāo)準(zhǔn)化的落地岛宦,有一個(gè)應(yīng)用的配置管理中心會管理應(yīng)用最基礎(chǔ)的信息砾肺,人員角色、應(yīng)用類型变汪、啟停的命令裙盾、軟件包的信息。雖然在應(yīng)用規(guī)范里面定義了這個(gè)應(yīng)用的一些啟停的命令是有標(biāo)準(zhǔn)和默認(rèn)的模板的,但是為了兼容性更好鲤拿,功能更強(qiáng)大署咽,我們開放了自定義的功能,它是可以修改默認(rèn)的窒升。如果是完全標(biāo)準(zhǔn)的應(yīng)用慕匠,可以不做任何的修改可以用台谊。
發(fā)布系統(tǒng)的依賴是什么?如下圖所示酪呻。
架構(gòu)主要是兩塊玩荠,一塊是前端的,發(fā)布流程的控制闷尿、用戶的界面等眼溶,下面是Python寫的Worker,用于構(gòu)建灌旧、部署绰筛,中間通過MQ進(jìn)行解耦的操作和任務(wù)隊(duì)列铝噩,中間通過DB進(jìn)行數(shù)據(jù)的交換。
研發(fā)流程毛甲,首先是發(fā)布的流程具被,先線下進(jìn)行發(fā)布一姿,然后預(yù)發(fā)進(jìn)行發(fā)布,最后到線上進(jìn)行發(fā)布的時(shí)候艾栋,之前還有Beta發(fā)布蛉顽,確認(rèn)沒問題了才最終進(jìn)入線上的發(fā)布蜂林。線上的發(fā)布和預(yù)發(fā)的邏輯是一致的,但是從預(yù)發(fā)到線上就比較不一樣了矮锈,會有一個(gè)Check List檢查睁蕾,只有通過了才允許進(jìn)入線上發(fā)布。提交完以后會有一個(gè)審批流程瀑凝,審批流程會根據(jù)時(shí)間的不同粤咪、硬件的不同,發(fā)布流程不一樣宪塔。
默認(rèn)主干都是Master囊拜。假如有一個(gè)需求需要進(jìn)行開發(fā)冠跷,就會從Master拉出一個(gè)開發(fā)分支,進(jìn)行需求的開發(fā)抄囚。開發(fā)完成后上線橄务,開發(fā)系統(tǒng)就會從Master上拉出一個(gè)release分支進(jìn)行發(fā)布仪糖,最后合并到Master迫肖,完成代碼合并操作。
新建變更操作也是在發(fā)布系統(tǒng)中完成的故爵,我們希望開發(fā)可以不直接操作gitlab诬垂,因?yàn)椴僮鱣itlab風(fēng)險(xiǎn)比較大伦仍。另外,我們提供了兩種變更創(chuàng)建的方法隧枫,一種是新建分支官脓,還有一種是導(dǎo)入分支,從現(xiàn)有的開發(fā)分支中再創(chuàng)建一個(gè)分支出來孕暇,這時(shí)候可以用導(dǎo)入變更的功能赤兴。
而集成搀缠,部署環(huán)境有三個(gè),線下簸州、預(yù)發(fā)歧譬、線上瑰步。中間發(fā)布的基礎(chǔ)的信息,下面是發(fā)布的過程读虏,大致可以總結(jié)成三個(gè)袁滥,一個(gè)是代碼合并题翻,一個(gè)是編譯構(gòu)建,第三是部署塑荒。對于特殊的應(yīng)用類型齿税,可能中間還會多幾個(gè)其他的操作炊豪,比如Docker的發(fā)布,那么就在編譯構(gòu)建完成以后陌知,多加入一個(gè)Docker鏡像的構(gòu)建完成仆葡。如果帶有前端的文件,在代碼合并之后會有合并前端的操作把篓。
最下面一個(gè)是集成區(qū)腰涧,一個(gè)是待集成區(qū)。集成區(qū)就是當(dāng)前發(fā)布疗锐,待集成區(qū)就是變更開發(fā)完成以后费彼,提交發(fā)布就在待集成區(qū)箍铲,可以隨時(shí)加入集成區(qū)也可以隨時(shí)退出來。
不允許把變更拖入線上環(huán)境中关划,所有的線上發(fā)布都是預(yù)發(fā)過去祭玉,而且線上的release分支都是預(yù)發(fā)的release分支春畔。首先保證預(yù)發(fā)都是成功的律姨,然后預(yù)發(fā)右邊集成區(qū)的Check List全部通過臼疫,這也是為了保證業(yè)務(wù)的穩(wěn)定性烫堤,最后才能拖到線上凤价。
這時(shí)候我們遇到兩個(gè)問題利诺,第一個(gè)剩燥,你往上合的時(shí)候難免會遇到一些沖突灭红,有可能是兩個(gè)Feature分支之間發(fā)生了沖突变擒,也可能是Feature分支和Master之間發(fā)生了沖突。怎么解決這個(gè)沖突策添?沖突依靠自動(dòng)化系統(tǒng)來解決悠菜,現(xiàn)在看來還是不太可能悔醋。我們提供了一個(gè)思路,就是如果發(fā)生沖突猾愿,還是強(qiáng)制合蒂秘,但是會告訴這個(gè)分支沖突了淘太,手動(dòng)把這個(gè)release分支check out出來蒲牧,本地解決了再上去。但是松嘶,這樣解決一次沖突還可以翠订,下次再沖突了怎么辦?這涉及到release分支的更換策略官撼,解決過的沖突不會再造成沖突橙弱。只有撤下一個(gè)release分支的時(shí)候才會更換這個(gè)release分支棘脐。這個(gè)辦法也能很好的解決經(jīng)常發(fā)生沖突的問題蛀缝。
我們希望每一次的構(gòu)建環(huán)境都是干凈的屈梁,都是統(tǒng)一的∩诽В可能在一臺構(gòu)建的機(jī)器上革答,同時(shí)會構(gòu)建十幾個(gè)甚至是幾十個(gè)任務(wù)曙强,我希望它們之間是互相獨(dú)立的碟嘴,互不影響的。所以我們用Docker來進(jìn)行構(gòu)建错沃,每次構(gòu)建都會起一個(gè)Docker鏡像枢析,把代碼掛在這個(gè)鏡像里面致燥,在鏡像里面執(zhí)行構(gòu)建的命令嫌蚤。最后把產(chǎn)出拿出來。
部署的流程里面有很多的步驟會依賴到OpsAgent智政,要在目標(biāo)服務(wù)器上進(jìn)行操作续捂。機(jī)房那么多宦搬,不可能把所有機(jī)器都跟這臺發(fā)布機(jī)器進(jìn)行通道打通牙瓢。首先通過Agent檢查目標(biāo)機(jī)器的環(huán)境,應(yīng)用機(jī)器在SA交付以后间校,是不是安裝上了應(yīng)用需要的東西矾克,應(yīng)用的初始化環(huán)境,用戶有沒有創(chuàng)建憔足,部署的目錄有沒有創(chuàng)建胁附,基礎(chǔ)的環(huán)境要進(jìn)行檢查。然后下載更新報(bào)滓彰,關(guān)閉監(jiān)控控妻,Tesla下線揭绑、Web下線弓候,然后停止應(yīng)用,更新應(yīng)用洗做,再啟動(dòng)弓叛。這時(shí)候有一個(gè)健康檢查的邏輯,最后就是兩個(gè)上線诚纸,把流量引進(jìn)來撰筷,把監(jiān)控打開。
如果發(fā)布的發(fā)布策略是分批發(fā)布畦徘,這一個(gè)應(yīng)用有好多分組毕籽,在分批策略的時(shí)候不會把所有分組的所有機(jī)器放在同一批。后面的不自動(dòng)開始井辆,就是構(gòu)建完成之后关筒,到了發(fā)布界面就停住了,需要手動(dòng)的點(diǎn)發(fā)布按鈕進(jìn)行這一批次的發(fā)布杯缺。正常的JAVA的應(yīng)用蒸播,一臺機(jī)器發(fā)布在一分半到兩分鐘左右。分十批發(fā)的話,這么一次也只需要十五到二十分鐘袍榆。
每個(gè)應(yīng)用在規(guī)范里面都會強(qiáng)制要求有檢查健康胀屿。這個(gè)機(jī)器需要做一些操作,通過返回值是否Success包雀,來判斷是否成功宿崭。首先檢查核心依賴是不是能夠連上DB或者Cache,依賴的其他應(yīng)用是不是能夠正常的調(diào)用到才写。還有一些特殊的應(yīng)用需要預(yù)熱數(shù)據(jù)葡兑,不管是緩存中的數(shù)據(jù),或者是編譯器的預(yù)熱赞草。有了健康檢查以后讹堤,基本上可以保證應(yīng)用不會有太大的問題。
我們在不同的時(shí)間審批鏈?zhǔn)遣灰粯拥姆孔剩钇胀ǖ娜粘0l(fā)布蜕劝,周一到周四。如果是非工作時(shí)間轰异,下班以后或者是節(jié)假日岖沛,就需要緊急發(fā)布,這時(shí)候需要研發(fā)D進(jìn)行審批搭独。最特殊的婴削,例如6·16大促,為了保證穩(wěn)定牙肝,原則上除非是緊急bug唉俗,否則不允許做發(fā)布的,這時(shí)候要做發(fā)布需要CTO進(jìn)行審批配椭。這些時(shí)間都是在系統(tǒng)中可以調(diào)整的吧趣∩兀回滾主要分成兩種豌汇,一種是基于基線的回滾借卧。發(fā)布完成之后,會向基線插入一條數(shù)據(jù)敦姻,里面記錄了本次發(fā)布的master分支的版本瘾境。想回滾到哪一次,就按后面的按鈕幫你回滾到這個(gè)版本镰惦。
還有一個(gè)回滾的功能就是緊急回滾迷守,在發(fā)布過程中發(fā)現(xiàn)應(yīng)用有問題,需要進(jìn)行緊急的回滾旺入。每次都會把上一次的war包保存一份兑凿,直接把上次的替換過來重啟應(yīng)用凯力,這就是緊急的回滾。
蘑菇街發(fā)布系統(tǒng)的特色
首先礼华,實(shí)現(xiàn)研發(fā)流程的閉環(huán)沮协,研發(fā)流程從需求到發(fā)布,只有在變更創(chuàng)建開始到發(fā)布那一段是在發(fā)布系統(tǒng)里面的卓嫂。之前的需求、項(xiàng)目管理等等聘殖,都是在另外一個(gè)項(xiàng)目管理的PMO系統(tǒng)中實(shí)現(xiàn)晨雳。這兩個(gè)是獨(dú)立的系統(tǒng),開發(fā)的時(shí)候要在這兩個(gè)系統(tǒng)中來回切換奸腺,非常不方便餐禁。我們把它和項(xiàng)目管理系統(tǒng)進(jìn)行了整合。項(xiàng)目管理系統(tǒng)中可以創(chuàng)建關(guān)聯(lián)變更突照,在變更發(fā)布后更新需求帮非、項(xiàng)目狀態(tài),這樣就完成了項(xiàng)目管理的閉環(huán)讹蘑。這樣的好處就是末盔,在需求管理系統(tǒng)中可以很明確的看到,某一個(gè)需求是哪一天什么時(shí)候發(fā)布上線的座慰,在發(fā)布系統(tǒng)中又能很清楚的看到陨舱,某一次的發(fā)布是發(fā)布了哪些需求。
第二版仔,多機(jī)房多分組的構(gòu)建游盲。因?yàn)槲覀冇幸粋€(gè)需求,同一個(gè)應(yīng)用部署在不同的機(jī)房蛮粮,可以按照不同的機(jī)房不同的分組創(chuàng)建不同的參數(shù)益缎,部署的時(shí)候會根據(jù)應(yīng)用機(jī)器所在的機(jī)房或者分組使用對應(yīng)的包。
第三然想,多套環(huán)境莺奔。我們可以根據(jù)分組只發(fā)布到某一個(gè)分組,就相當(dāng)于變相提供了多套環(huán)境的功能又沾。
第四弊仪,Jar包檢測和Diff。Jar包沖突會有很多的問題杖刷,明明寫的時(shí)候是好的励饵,部署上去就是不正常。原則上我們不允許使用Snapshot的包滑燃,因?yàn)樗豢煽匾厶ar包的Diff,會告訴他哪些Jar包有沖突,哪些版本不能再使用典予√鸨酰基線和哪些Jar包的版本不一致。
第五瘤袖,二方庫的發(fā)布衣摩。有兩種,一種是某個(gè)應(yīng)用的子模塊捂敌,還有一種是像中間件的產(chǎn)品艾扮。發(fā)布系統(tǒng)能為線上的穩(wěn)定性做哪些保障?目前四個(gè)占婉,前端掃描泡嘴,前端應(yīng)用,JS的文件的掃描逆济,對接了前端團(tuán)隊(duì)的系統(tǒng)酌予,對這些前端文件進(jìn)行掃描。安全掃描奖慌,我們對接了一個(gè)安全的系統(tǒng)抛虫,對JAVA應(yīng)用進(jìn)行靜態(tài)的代碼掃描。集成測試简僧,我們有一個(gè)測試系統(tǒng)莱褒,在發(fā)布的時(shí)候同時(shí)對發(fā)布代碼進(jìn)行單元測試、接口測試涎劈。性能監(jiān)控和壓測广凸,這些系統(tǒng)不是發(fā)布系統(tǒng)本身提供的,都是對接了我們內(nèi)部團(tuán)隊(duì)的系統(tǒng)蛛枚,把它集成到系統(tǒng)中谅海。我們的Cobra系統(tǒng)會對代碼進(jìn)行掃描,如果掃描不通過也是不能發(fā)布的蹦浦。