一泡垃、背景
在互聯(lián)網(wǎng)分布式應(yīng)用中析珊,如果上線的新版本有bug又不能回滾止損,帶來(lái)的后果將是災(zāi)難性的蔑穴。因此做到上線可回滾以及上線前的checklist是保證服務(wù)穩(wěn)定性的基本要求忠寻。
在簡(jiǎn)單的場(chǎng)景里直接回滾到上一版?zhèn)€版本即可,但是如果涉及多個(gè)上下游和組件存和、考慮多版本兼容奕剃,就需要有好好設(shè)計(jì)下如何構(gòu)建可回滾的代碼,充分驗(yàn)證后還需要仔細(xì)檢查上線checklist捐腿,最大程度保證線上服務(wù)的穩(wěn)定性纵朋。
二、構(gòu)建向前兼容的代碼
回滾指的是程序或數(shù)據(jù)處理錯(cuò)誤叙量,將程序或數(shù)據(jù)恢復(fù)到上一次正確狀態(tài)的行為倡蝙。在回滾之后,程序依然能夠正常處理绞佩,稱為可回滾寺鸥。
不可回滾原因大多是舊程序不能處理新數(shù)據(jù)猪钮,同時(shí)新數(shù)據(jù)又不能丟棄,代碼回滾導(dǎo)致舊業(yè)務(wù)邏輯出錯(cuò)胆建。
保證向前兼容的手段:
1烤低、數(shù)據(jù)庫(kù)變更
新加字段:設(shè)置默認(rèn)值,默認(rèn)值要保證新舊代碼邏輯的語(yǔ)義一致性笆载。比如用戶表添加了用戶狀態(tài)扑馁,默認(rèn)值要設(shè)置為默認(rèn)有效。
刪除字段:新版本全量發(fā)布后凉驻,最后迭代2-3個(gè)版本后腻要,再刪除無(wú)用字段,同時(shí)要做好數(shù)據(jù)庫(kù)備份涝登。
新加唯一約束:首先要確保原有的數(shù)據(jù)值沒(méi)有重復(fù)的雄家,再添加唯一索引,可使用以下SQL驗(yàn)證:
select field,count(1) from table group by field having count(1) > 1
復(fù)雜數(shù)據(jù)庫(kù)字段變更:
復(fù)雜數(shù)據(jù)庫(kù)字段變更:
通常做法是:雙寫讀舊 -> 新字段離線驗(yàn)證 -> 舊字段全量拷貝至新字段 -> 雙讀舊為主diff -> 雙讀新為主diff -> 寫新讀新 -> 移除舊字段和邏輯胀滚;
出問(wèn)題之后趟济,只回滾程序,不回滾數(shù)據(jù)咽笼。
2顷编、對(duì)外提供服務(wù)API變動(dòng)設(shè)計(jì)
(1)對(duì)外提供RPC服務(wù)
入?yún)⑿录幼侄危涸O(shè)置為可選的,如果沒(méi)設(shè)置值剑刑,要有老業(yè)務(wù)邏輯兼容代碼
返回值字段:設(shè)置為可選的媳纬,如果是新版本肯定有值字段,需要在字段描述文檔里做好備注
如果入?yún)⒑头祷刂到Y(jié)果差異較大叛甫,建議新建一個(gè)RPC方法层宫,逐漸把業(yè)務(wù)方調(diào)用遷移到新方法
(2)對(duì)外提供HTTP服務(wù)
HTTP接口和RPC接口的不同是沒(méi)有強(qiáng)制的約束,數(shù)據(jù)交換大多采用Json形式其监,雖然靈活性強(qiáng)萌腿,但是約束力低給管理帶來(lái)很大的成本。因此HTTP接口文檔必須給出類似RPC一樣的規(guī)范抖苦,比如Swagger等工具毁菱,比如給指定字段名、類型锌历、是否必填贮庞、不填寫的默認(rèn)值等。
3究西、多版本發(fā)布
對(duì)于客戶端API服務(wù)或者基礎(chǔ)服務(wù)來(lái)說(shuō)窗慎,用戶升級(jí)會(huì)有很大的延遲,對(duì)于服務(wù)提供方來(lái)說(shuō)則需要盡量保證多版本同時(shí)可用。最好在最開(kāi)始設(shè)計(jì)接口的時(shí)候就添加version字段遮斥,保證以后的擴(kuò)展峦失。
4、靜態(tài)資源發(fā)布
因?yàn)殪o態(tài)資源一般放在CDN上緩存時(shí)間設(shè)置的比較長(zhǎng)术吗,比如1個(gè)月尉辑。這樣假設(shè)發(fā)布的版本有問(wèn)題,需要清理CDN緩存较屿,并且也需要清理瀏覽器緩存隧魄,而且因?yàn)榇嬖诎姹靖采w的問(wèn)題,即使覆蓋了也不一定保證是操作正確了隘蝎。
現(xiàn)在的工具默認(rèn)都會(huì)把md5設(shè)置到j(luò)s购啄、css文件名中,可有效的避免以上問(wèn)題嘱么。
三闸溃、上線checklist
盡管準(zhǔn)備工作做的再好,難免也會(huì)出現(xiàn)疏漏拱撵,因此指定上線前checklist,組內(nèi)同學(xué)交叉評(píng)審表蝙,保證上線前的最后一步拴测,是很有必要的。
發(fā)布策略一般是從下游逐步向上部署府蛇。而回滾方案相反集索,一般是從上游往下回滾。
通用的checklist需要考慮以下幾方面:
1汇跨、依賴
(1)存儲(chǔ)(數(shù)據(jù)庫(kù)务荆、分布式緩存)
是否有數(shù)據(jù)庫(kù)變更需要代碼上線前先提交,并且驗(yàn)證執(zhí)行成功穷遂。
(2)動(dòng)態(tài)配置
首先需要確認(rèn)代碼中的動(dòng)態(tài)配置有無(wú)默認(rèn)值函匕,沒(méi)有默認(rèn)值需要先在動(dòng)態(tài)配置系統(tǒng)上添加相關(guān)的配置。
(3)下游
確認(rèn)下游jar包版本是否正確蚪黑,線上最好使用release包盅惜。
確認(rèn)下游的業(yè)務(wù)方是否已經(jīng)上線。
(4)鑒權(quán)
美團(tuán)內(nèi)部的服務(wù)調(diào)用都有鑒權(quán)忌穿,檢查鑒權(quán)是否已經(jīng)添加并且已生效
2抒寂、上游
(1)Nginx層
確認(rèn)路由策略,在發(fā)布過(guò)程中會(huì)不會(huì)造成用戶不斷刷新頁(yè)面命中不同代碼邏輯掠剑,給用戶帶來(lái)用戶體驗(yàn)上的問(wèn)題和困擾屈芜。
(2)上游業(yè)務(wù)方
確保和上游業(yè)務(wù)方溝通好,等當(dāng)前服務(wù)全量發(fā)布后且觀察、驗(yàn)證沒(méi)問(wèn)題再通知上游發(fā)布井佑。
3属铁、業(yè)務(wù)內(nèi)部
通過(guò)代碼review,QA測(cè)試等方式保證上線對(duì)現(xiàn)有業(yè)務(wù)邏輯沒(méi)有影響毅糟。
如有必要可建設(shè)業(yè)務(wù)的checklist規(guī)范
代碼review:重點(diǎn)查看代碼規(guī)范红选、業(yè)務(wù)邏輯的正確性、對(duì)相關(guān)功能的影響
測(cè)試用例:測(cè)試相關(guān)代碼的正常邏輯姆另、邊界條件喇肋、其他功能不受影響,如果寫好測(cè)試用例請(qǐng)參考其他專業(yè)的文檔
自動(dòng)化測(cè)試:有條件的團(tuán)隊(duì)建議添加自動(dòng)化測(cè)試迹辐,來(lái)保證核心業(yè)務(wù)流程的正確性
四蝶防、可回滾發(fā)布
1、回滾方案
需考慮以下幾方面:
是否有動(dòng)態(tài)開(kāi)關(guān)明吩、流量控制可以一鍵恢復(fù)到舊版本邏輯
回滾代碼會(huì)不會(huì)導(dǎo)致上游調(diào)用失敗
回滾代碼:根據(jù)docker鏡像间学、Git commitId、Git tag回滾
2印荔、可回滾驗(yàn)證
方案設(shè)計(jì)要考慮可回滾性低葫;通常要在test或預(yù)上線環(huán)境驗(yàn)證(演練)可回滾性。同時(shí)QA同學(xué)要把可回滾作為質(zhì)量驗(yàn)收的一部分仍律。
3嘿悬、平滑升級(jí)
(1)線上驗(yàn)收
通過(guò)staging環(huán)境或線上灰度鏈路, 進(jìn)行線上驗(yàn)收水泉。
(2)藍(lán)綠發(fā)布
線上部署兩個(gè)集群善涨,每個(gè)集群都能抗住所有的流量,發(fā)布的時(shí)候一個(gè)集群接受用戶請(qǐng)求草则,等當(dāng)前集群全部發(fā)布成功后钢拧,再把流量全部遷移到新版本集群。
優(yōu)點(diǎn):可保證新版本特性同一時(shí)間對(duì)所有用戶生效炕横;保證系統(tǒng)高可用源内,一個(gè)集群出問(wèn)題,可快速切換到備用集群看锉;
缺點(diǎn):日常使用的機(jī)器只有一半姿锭,造成浪費(fèi);
藍(lán)綠發(fā)布的高配方案:部署兩套集群伯铣,通過(guò)Nginx層動(dòng)態(tài)切換集群呻此。
藍(lán)綠發(fā)布的低配方案:用動(dòng)態(tài)開(kāi)關(guān)控制新舊邏輯,等新版本全量上線后腔寡,再切換到新代碼邏輯焚鲜。
(3)灰度發(fā)布(金絲雀發(fā)布)
采用金絲雀部署,可以在生產(chǎn)環(huán)境的基礎(chǔ)設(shè)施中小范圍的部署新的應(yīng)用代碼。一旦應(yīng)用新發(fā)布忿磅,只有少數(shù)用戶被路由到它糯彬。最大限度的降低影響。檢測(cè)新版本沒(méi)問(wèn)題葱她,再逐步擴(kuò)大發(fā)布范圍直至全量撩扒。
實(shí)現(xiàn)灰度發(fā)布,需要部署系統(tǒng)設(shè)置分批發(fā)布吨些,同時(shí)必須有監(jiān)控搓谆、報(bào)警等相關(guān)的系統(tǒng)的配合。確定沒(méi)問(wèn)題后再擴(kuò)量豪墅。
還有一點(diǎn)需要注意的就是灰度路由策略泉手,一般服務(wù)中用的多是的隨機(jī)策略,這樣可能導(dǎo)致用戶在發(fā)布過(guò)程中不斷刷新頁(yè)面偶器,可能會(huì)來(lái)回命中新老版本邏輯斩萌,影響用戶體驗(yàn)。避免方式有以下幾種:
采用用戶ID做路由因子屏轰,問(wèn)題是新增颊郎、刪除機(jī)器時(shí),算法匹配的機(jī)器會(huì)變霎苗,就需要引入一致性Hash算法袭艺,復(fù)雜性較高
RPC路由策略一般不能執(zhí)行路由規(guī)則,就需要程序自己判斷叨粘。常用做法是采用動(dòng)態(tài)配置設(shè)置百分比值,算法如下:
userId % 100 < $dynamicTrafficPercent ? oldVersion : newVersion
(4)AB測(cè)試
AB測(cè)試雖然也有流量控制的功能瘤睹,但一般是用于驗(yàn)證不同版本之間的性能升敲、用戶體驗(yàn)、效果數(shù)據(jù)等的手段轰传。是上線后由運(yùn)營(yíng)控制的驴党。
AB測(cè)試和灰度發(fā)布最大的不同是AB測(cè)試是通過(guò)桶的方式分配流量。
4获茬、監(jiān)控報(bào)警
常用的發(fā)布相關(guān)的監(jiān)控報(bào)警包括:
新港庄、舊版本的流量及百分比
對(duì)外提供的接口的性能指標(biāo)、下游的性能指標(biāo)(TP50恕曲、TP90鹏氧、TP99、TP999)
異常指標(biāo)及報(bào)警
業(yè)務(wù)指標(biāo)
5佩谣、發(fā)布完成周知
PM: 周知PM關(guān)注業(yè)務(wù)指標(biāo)把还、客訴等
上游:如果上游依賴此發(fā)布,周知上游開(kāi)始發(fā)布
下游:周知下周注意流量增量及耗時(shí)等情況
五、上線步驟及規(guī)范
1吊履、不在高峰期上線,如必須上線,要發(fā)郵件申請(qǐng)卦尊,同時(shí)降低并發(fā)度
2漫萄、上線前周知
3、保證能回滾
4缀踪、發(fā)布過(guò)程采用分組發(fā)布(強(qiáng)制)
5居砖、上線過(guò)程中觀察系統(tǒng)指標(biāo)、業(yè)務(wù)指標(biāo)辜贵、異常指標(biāo)等悯蝉;如有異常立即禁用已發(fā)布機(jī)器
6、上線后周知PM托慨、上下游等
7鼻由、有異常第一時(shí)間回滾
六、總結(jié)
從本文可以看出厚棵,保證線上穩(wěn)定性是一個(gè)復(fù)雜且系統(tǒng)的工程蕉世,需要從技術(shù)規(guī)范、流程規(guī)范婆硬、周知狠轻、檢查、工具支持等各個(gè)方面來(lái)保證彬犯。
作者簡(jiǎn)介:木小豐向楼,美團(tuán)Java高級(jí)工程師,專注分享軟件研發(fā)過(guò)程中的實(shí)踐谐区、思考湖蜕。歡迎關(guān)注公共號(hào):Java研發(fā)
原文鏈接:構(gòu)建可回滾的應(yīng)用及上線checklist實(shí)踐
更多精彩文章: