概述
在互聯(lián)網(wǎng)公司中,產(chǎn)品通常迭代比較頻繁的,尤其涉及到一些關(guān)鍵比較大項(xiàng)目需求時(shí),影響范圍比較大的時(shí)候,通常需要進(jìn)行灰度控制,通過(guò)引流一小部分流量到新版本,可以及時(shí)發(fā)現(xiàn)程序問(wèn)題,有效阻止大面積故障的發(fā)生。
業(yè)界上已經(jīng)有比較成熟的服務(wù)發(fā)布策略,比如藍(lán)綠發(fā)布、A/B 測(cè)試以及金絲雀發(fā)布、滾動(dòng)發(fā)布,這些發(fā)布策略主要專(zhuān)注于如何對(duì)單個(gè)服務(wù)進(jìn)行發(fā)布。在微服務(wù)體系架構(gòu)中,服務(wù)之間的依賴(lài)關(guān)系錯(cuò)綜復(fù)雜,有時(shí)某個(gè)功能發(fā)版依賴(lài)多個(gè)服務(wù)同時(shí)升級(jí)上線。我們希望可以對(duì)這些服務(wù)的新版本同時(shí)進(jìn)行小流量灰度驗(yàn)證,這就是微服務(wù)架構(gòu)中特有的全鏈路灰度場(chǎng)景,通過(guò)構(gòu)建從網(wǎng)關(guān)到整個(gè)后端服務(wù)的環(huán)境隔離來(lái)對(duì)多個(gè)不同版本的服務(wù)進(jìn)行灰度驗(yàn)證。
灰度發(fā)布類(lèi)型
灰度發(fā)布如果按照端來(lái)分的話,可以分為web前端、客戶端、服務(wù)端灰度。
無(wú)論是哪種灰度,一般需要滿足以下2點(diǎn)要求:
- 需要一個(gè)放量配置,給產(chǎn)品/運(yùn)營(yíng)等工作人員配置放量策略;
- 需要做到同一個(gè)用戶始終訪問(wèn)的是同一個(gè)版本的代碼,如果同個(gè)用戶上個(gè)請(qǐng)求訪問(wèn)的是A版本,下個(gè)請(qǐng)求訪問(wèn)的是B版本,就可能會(huì)出問(wèn)題。
作為用戶體驗(yàn)的入口,web前端的灰度是比較常用的方案。我們前端每次發(fā)布一個(gè)新版本,就把資源增量式地上傳到CDN,然后給它分配一個(gè)唯一的版本號(hào),再把所有的版本號(hào)存儲(chǔ)起來(lái)。當(dāng)處理請(qǐng)求時(shí),根據(jù)動(dòng)態(tài)配置的分流策略來(lái)決定用戶使用哪個(gè)版本。
比如分流策略是放量10%,即新版本隨機(jī)放量給10%的用戶使用,當(dāng)用戶首次命中資源版本號(hào)時(shí),需要把用戶id和版本號(hào)的映射關(guān)系存儲(chǔ)起來(lái)(可存到cookie),這樣就能保證同個(gè)用戶上次請(qǐng)求和下次請(qǐng)求訪問(wèn)到的都是同個(gè)版本的代碼。
那如果線上有緊急bug需要修復(fù),又要重新發(fā)布新版本,該如何處理當(dāng)前灰度的狀態(tài)?是趕緊結(jié)束上一個(gè)灰度然后全量發(fā)布還是一起發(fā)上去同時(shí)灰度?一般來(lái)說(shuō),再有新版本發(fā)布或者放量策略發(fā)生變化時(shí),應(yīng)該重新分流灰度。
客戶端灰度
大家在使用App的時(shí)候,通常收到一些提示"是否體驗(yàn)升級(jí)新版本",通常收到該提示,說(shuō)明你被系統(tǒng)劃分為灰度用戶。通常來(lái)講web前端灰度是在客戶無(wú)感知的情況下平滑進(jìn)行,而APP灰度需要涉及到用戶的主動(dòng)安裝行為。
客戶端在啟動(dòng)時(shí),會(huì)向灰度系統(tǒng)發(fā)起請(qǐng)求,灰度系統(tǒng)根據(jù)客戶端傳過(guò)來(lái)的參數(shù)和當(dāng)前的放量策略來(lái)決定是否要給客戶端升級(jí)提醒。一般會(huì)根據(jù)以下幾種策略來(lái)決定給予用戶升級(jí)提醒:
- 根據(jù)用戶設(shè)備的系統(tǒng)和應(yīng)用版本;
- 根據(jù)渠道:發(fā)布到不同應(yīng)用市場(chǎng)的app都會(huì)被打上渠道標(biāo)簽,所以可以根據(jù)渠道來(lái)區(qū)分用戶;
- 根據(jù)設(shè)備ID和用戶ID。
通過(guò)設(shè)備ID主要是為了控制提醒頻率,用戶ID主要是為了區(qū)分出特性用戶,比如對(duì)活躍用戶發(fā)送提醒。
如果一個(gè)帶有bug的安裝包全量發(fā)布出去,一旦有問(wèn)題,我們只能快速定位問(wèn)題來(lái)提醒用戶安裝新版本,是否安裝取決于用戶,所以客戶端灰度發(fā)布是非常有必要的。
服務(wù)端灰度
服務(wù)端灰度分為兼容變更灰度和不兼容變更灰度。
1)兼容變更
兼容變更又可分為物理灰度和邏輯灰度。
- 物理灰度:物理灰度比較簡(jiǎn)單,根據(jù)機(jī)器維度進(jìn)行灰度,直接部署新老版本在不同機(jī)器,流量均勻地打在新老版本上面。這種方式雖然簡(jiǎn)單,但不適用于不兼容變更;
- 邏輯灰度:邏輯灰度就是根據(jù)更精確的流量策略來(lái)控制流量,這種灰度一般要寫(xiě)一定的灰度代碼。這種方式能比較精確地控制流量,但是增加了一定的灰度代碼,灰度完成后要?jiǎng)h除相關(guān)灰度代碼,有點(diǎn)麻煩。
2)不兼容變更
不兼容變更指的是更改了當(dāng)前功能,即接口邏輯跟之前版本發(fā)生很大變化,必須要前后端同時(shí)發(fā)布,否則會(huì)有一段時(shí)間服務(wù)不可用。
一般的做法是引入接口版本號(hào),新老版本接口并存,比如 /v1/api 和 /v2/api。前端使用/v2/api版本,當(dāng)過(guò)去一段穩(wěn)定期后(可以是登錄態(tài)時(shí)間失效后),就可下掉/v1/api版本。
灰度放量策略
流量策略一般分為以下幾種:
1. 按流量百分比
先到先得的方式比如限制10%的用戶體驗(yàn)的是新版本,90%的用戶體驗(yàn)的是老版本。先訪問(wèn)網(wǎng)站的用戶就優(yōu)先命中新版本,直到流量用完為止。
2. 按人群劃分
1)按用戶id、用戶ip、設(shè)備類(lèi)型比如可通過(guò)平時(shí)的埋點(diǎn)上報(bào)數(shù)據(jù)得知用戶的pv、uv、頁(yè)面平均訪問(wèn)時(shí)長(zhǎng)等數(shù)據(jù),根據(jù)用戶活躍度來(lái)讓用戶優(yōu)先體驗(yàn)新版本,進(jìn)而快速觀察使用效果。
2)按地域、性別、年齡等用戶畫(huà)像比如可通過(guò)用戶的性別、年齡等做下新老版本的對(duì)比效果來(lái)看看目標(biāo)用戶在新版本的使用年齡段,性別范圍是多少。
3. 按渠道劃分
比如根據(jù)用戶的注冊(cè)來(lái)源來(lái)放量。
灰度發(fā)布策略
灰度部署發(fā)布通常會(huì)分為三大類(lèi),主要包括藍(lán)綠發(fā)布、滾動(dòng)發(fā)布、灰度發(fā)布,這三大類(lèi)
藍(lán)綠發(fā)布
藍(lán)綠部署中,一共有兩套系統(tǒng):一套是正在提供服務(wù)系統(tǒng)(也就是上面說(shuō)的舊版),標(biāo)記為“綠色”;另一套是準(zhǔn)備發(fā)布的系統(tǒng),標(biāo)記為“藍(lán)色”。兩套系統(tǒng)都是功能完善的,并且正在運(yùn)行的系統(tǒng),只是系統(tǒng)版本和對(duì)外服務(wù)情況不同。正在對(duì)外提供服務(wù)的老系統(tǒng)是綠色系統(tǒng),新部署的系統(tǒng)是藍(lán)色系統(tǒng)。
藍(lán)綠發(fā)布示意圖
藍(lán)色系統(tǒng)不對(duì)外提供服務(wù),用來(lái)做啥?用來(lái)做發(fā)布前測(cè)試,測(cè)試過(guò)程中發(fā)現(xiàn)任何問(wèn)題,可以直接在藍(lán)色系統(tǒng)上修改,不干擾用戶正在使用的系統(tǒng)。藍(lán)色系統(tǒng)經(jīng)過(guò)反復(fù)的測(cè)試、修改、驗(yàn)證,確定達(dá)到上線標(biāo)準(zhǔn)之后,直接將用戶切換到藍(lán)色系統(tǒng),切換后的一段時(shí)間內(nèi),依舊是藍(lán)綠兩套系統(tǒng)并存,但是用戶訪問(wèn)的已經(jīng)是藍(lán)色系統(tǒng)。這段時(shí)間內(nèi)觀察藍(lán)色系統(tǒng)(新系統(tǒng))工作狀態(tài),如果出現(xiàn)問(wèn)題,直接切換回綠色系統(tǒng)。當(dāng)確信對(duì)外提供服務(wù)的藍(lán)色系統(tǒng)工作正常,不對(duì)外提供服務(wù)的綠色系統(tǒng)已經(jīng)不再需要的時(shí)候,藍(lán)色系統(tǒng)正式成為對(duì)外提供服務(wù)系統(tǒng),成為新的綠色系統(tǒng)。原先的綠色系統(tǒng)可以銷(xiāo)毀,將資源釋放出來(lái),用于部署下一個(gè)藍(lán)色系統(tǒng)。
藍(lán)綠發(fā)布特點(diǎn)
- 藍(lán)綠部署的目的是減少發(fā)布時(shí)的中斷時(shí)間能夠快速撤回發(fā)布
- 兩套系統(tǒng)沒(méi)有耦合的時(shí)候才能百分百保證不干擾
藍(lán)綠發(fā)布注意事項(xiàng)
藍(lán)綠部署只是上線策略中的一種,它不是可以應(yīng)對(duì)所有情況的萬(wàn)能方案。藍(lán)綠部署能夠簡(jiǎn)單快捷實(shí)施的前提假設(shè)是目標(biāo)系統(tǒng)是非常內(nèi)聚的,如果目標(biāo)系統(tǒng)相當(dāng)復(fù)雜,那么如何切換、兩套系統(tǒng)的數(shù)據(jù)是否需要以及如何同步等,都需要仔細(xì)考慮。當(dāng)你切換到藍(lán)色環(huán)境時(shí),需要妥當(dāng)處理未完成的業(yè)務(wù)和新的業(yè)務(wù)。如果你的數(shù)據(jù)庫(kù)后端無(wú)法處理,會(huì)是一個(gè)比較麻煩的問(wèn)題:
- 可能會(huì)出現(xiàn)需要同時(shí)處理“微服務(wù)架構(gòu)應(yīng)用”和“傳統(tǒng)架構(gòu)應(yīng)用”的情況,如果在藍(lán)綠部署中協(xié)調(diào)不好這兩者,還是有可能會(huì)導(dǎo)致服務(wù)停止。
- 需要提前考慮數(shù)據(jù)庫(kù)與應(yīng)用部署同步遷移/回滾的問(wèn)題。
- 藍(lán)綠部署需要有基礎(chǔ)設(shè)施支持。
- 在非隔離基礎(chǔ)架構(gòu)(VM 、 Docker等)上執(zhí)行藍(lán)綠部署,藍(lán)色環(huán)境和綠色環(huán)境有被摧毀的風(fēng)險(xiǎn)。
一般是取出一個(gè)或者多個(gè)服務(wù)器停止服務(wù),執(zhí)行更新,并重新將其投入使用。周而復(fù)始,直到集群中所有的實(shí)例都更新成新版本。
滾動(dòng)發(fā)布示意圖發(fā)布流程:
相對(duì)于藍(lán)綠發(fā)布需要一套完備的機(jī)器不同,滾動(dòng)發(fā)布只需要一臺(tái)機(jī)器(這兒這是為了理解,實(shí)際可能是多臺(tái)),我們只需要將部分功能部署在這臺(tái)機(jī)器上,然后去替換正在運(yùn)行的機(jī)器,如上圖,將更新后的功能部署在Server1上,然后Server1去替換正在運(yùn)行的Server,替換下來(lái)的物理機(jī)又可以繼續(xù)部署Server2的新版本,然后去替換正在工作的Server2,以此類(lèi)推,直到替換完所有的服務(wù)器,至此,服務(wù)更新完成。
滾動(dòng)發(fā)布特點(diǎn)
- 這種部署方式相對(duì)于藍(lán)綠部署,更加節(jié)約資源——它不需要運(yùn)行兩個(gè)集群、兩倍的實(shí)例數(shù)。我們可以部分部署,例如每次只取出集群的20%進(jìn)行升級(jí)。
- 回滾困難
- 滾動(dòng)發(fā)布沒(méi)有一個(gè)確定可行的環(huán)境。使用藍(lán)綠部署,我們能夠清晰地知道老版本是可行的,而使用滾動(dòng)發(fā)布,我們無(wú)法確定。
- 修改了現(xiàn)有的環(huán)境。
- 回滾困難。舉個(gè)例子,在某一次發(fā)布中,我們需要更新100個(gè)實(shí)例,每次更新10個(gè)實(shí)例,每次部署需要5分鐘。當(dāng)滾動(dòng)發(fā)布到第80個(gè)實(shí)例時(shí),發(fā)現(xiàn)了問(wèn)題,需要回滾,這個(gè)回滾卻是一個(gè)痛苦,并且漫長(zhǎng)的過(guò)程。
- 有的時(shí)候,我們還可能對(duì)系統(tǒng)進(jìn)行動(dòng)態(tài)伸縮,如果部署期間,系統(tǒng)自動(dòng)擴(kuò)容/縮容了,我們還需判斷到底哪個(gè)節(jié)點(diǎn)使用的是哪個(gè)代碼。盡管有一些自動(dòng)化的運(yùn)維工具,但是依然令人心驚膽戰(zhàn)。
- 因?yàn)槭侵鸩礁拢敲次覀冊(cè)谏暇€代碼的時(shí)候,就會(huì)短暫出現(xiàn)新老版本不一致的情況,如果對(duì)上線要求較高的場(chǎng)景,那么就需要考慮如何做好兼容的問(wèn)題。
灰度發(fā)布,也叫金絲雀發(fā)布。是指在黑與白之間,能夠平滑過(guò)渡的一種發(fā)布方式。AB test就是一種灰度發(fā)布方式,讓一部分用戶繼續(xù)用A,一部分用戶開(kāi)始用B,如果用戶對(duì)B沒(méi)有什么反對(duì)意見(jiàn),那么逐步擴(kuò)大范圍,把所有用戶都遷移到B上面來(lái)。灰度發(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時(shí)候就可以發(fā)現(xiàn)、調(diào)整問(wèn)題,以保證其影響度,而我們平常所說(shuō)的金絲雀部署也就是灰度發(fā)布的一種方式。具體到服務(wù)器上, 實(shí)際操作中還可以做更多控制,譬如說(shuō),給最初更新的10臺(tái)服務(wù)器設(shè)置較低的權(quán)重、控制發(fā)送給這10臺(tái)服務(wù)器的請(qǐng)求數(shù),然后逐漸提高權(quán)重、增加請(qǐng)求數(shù)。一種平滑過(guò)渡的思路, 這個(gè)控制叫做“流量切分”。
17世紀(jì),英國(guó)礦井工人發(fā)現(xiàn),金絲雀對(duì)瓦斯這種氣體十分敏感。空氣中哪怕有極其微量的瓦斯,金絲雀也會(huì)停止歌唱;而當(dāng)瓦斯含量超過(guò)一定限度時(shí),雖然魯鈍的人類(lèi)毫無(wú)察覺(jué),金絲雀卻早已毒發(fā)身亡。當(dāng)時(shí)在采礦設(shè)備相對(duì)簡(jiǎn)陋的條件下,工人們每次下井都會(huì)帶上一只金絲雀作為“瓦斯檢測(cè)指標(biāo)”,以便在危險(xiǎn)狀況下緊急撤離。
灰度發(fā)布示意圖過(guò)程:
- 準(zhǔn)備好部署各個(gè)階段的工件,包括:構(gòu)建工件,測(cè)試腳本,配置文件和部署清單文件。
- 將“金絲雀”服務(wù)器部署進(jìn)服務(wù)器中,測(cè)試。
- 從負(fù)載均衡列表中移除掉“金絲雀”服務(wù)器。
- 升級(jí)“金絲雀”應(yīng)用(排掉原有流量并進(jìn)行部署)。
- 對(duì)應(yīng)用進(jìn)行自動(dòng)化測(cè)試。
- 將“金絲雀”服務(wù)器重新添加到負(fù)載均衡列表中(連通性和健康檢查)。
- 如果“金絲雀”在線使用測(cè)試成功,升級(jí)剩余的其他服務(wù)器。(否則就回滾)
A/B測(cè)試和藍(lán)綠發(fā)布、滾動(dòng)發(fā)布以及金絲雀發(fā)布,完全是兩回事。
藍(lán)綠發(fā)布、滾動(dòng)發(fā)布和金絲雀是發(fā)布策略,目標(biāo)是確保新上線的系統(tǒng)穩(wěn)定,關(guān)注的是新系統(tǒng)的BUG、隱患。
A/B測(cè)試是效果測(cè)試,同一時(shí)間有多個(gè)版本的服務(wù)對(duì)外服務(wù),這些服務(wù)都是經(jīng)過(guò)足夠測(cè)試,達(dá)到了上線標(biāo)準(zhǔn)的服務(wù),有差異但是沒(méi)有新舊之分(它們上線時(shí)可能采用了藍(lán)綠部署的方式)。
A/B測(cè)試關(guān)注的是不同版本的服務(wù)的實(shí)際效果,譬如說(shuō)轉(zhuǎn)化率、訂單情況等。
A/B測(cè)試時(shí),線上同時(shí)運(yùn)行多個(gè)版本的服務(wù),這些服務(wù)通常會(huì)有一些體驗(yàn)上的差異,譬如說(shuō)頁(yè)面樣式、顏色、操作流程不同。相關(guān)人員通過(guò)分析各個(gè)版本服務(wù)的實(shí)際效果,選出效果最好的版本。
小結(jié)
綜上所述,三種方式均可以做到平滑式升級(jí),在升級(jí)過(guò)程中服務(wù)仍然保持服務(wù)的連續(xù)性,升級(jí)對(duì)外界是無(wú)感知的。那生產(chǎn)上選擇哪種部署方法最合適呢?這取決于哪種方法最適合你的業(yè)務(wù)和技術(shù)需求。如果你們運(yùn)維自動(dòng)化能力儲(chǔ)備不夠,肯定是越簡(jiǎn)單越好,建議藍(lán)綠發(fā)布,如果業(yè)務(wù)對(duì)用戶依賴(lài)很強(qiáng),建議灰度發(fā)布。如果是K8S平臺(tái),滾動(dòng)更新是現(xiàn)成的方案,建議先直接使用。
- 藍(lán)綠發(fā)布:兩套環(huán)境交替升級(jí),舊版本保留一定時(shí)間便于回滾。
- 灰度發(fā)布:根據(jù)比例將老版本升級(jí),例如80%用戶訪問(wèn)是老版本,20%用戶訪問(wèn)是新版本。
- 滾動(dòng)發(fā)布:按批次停止老版本實(shí)例,啟動(dòng)新版本實(shí)例。
為了滿足業(yè)務(wù)的迭代速度,開(kāi)發(fā)者開(kāi)始對(duì)原來(lái)的單體架構(gòu)進(jìn)行細(xì)粒度的拆分,將單體應(yīng)用中的服務(wù)模塊拆分成一個(gè)個(gè)獨(dú)立部署運(yùn)行的微服務(wù),并且這些微服務(wù)的生命周期由對(duì)應(yīng)的業(yè)務(wù)團(tuán)隊(duì)獨(dú)自負(fù)責(zé),有效的解決了單體架構(gòu)中存在的敏捷性不足、靈活性不強(qiáng)的問(wèn)題。
但任何架構(gòu)都不是銀彈,在解決舊問(wèn)題同時(shí)勢(shì)必會(huì)引入一些新的問(wèn)題。微服務(wù)體系中最令人頭疼的問(wèn)題,是如何對(duì)眾多微服務(wù)進(jìn)行高效、便捷的治理,主要表現(xiàn)在可見(jiàn)性、連接性和安全性這三個(gè)方面。
如何保證微服務(wù)體系中服務(wù)新版本升級(jí)過(guò)程中平滑無(wú)損,以及如何低成本的為多個(gè)微服務(wù)構(gòu)建流量隔離環(huán)境,方便開(kāi)發(fā)者同時(shí)對(duì)多個(gè)服務(wù)新版本進(jìn)行充分的灰度驗(yàn)證,避免故障的發(fā)生,是本章節(jié)主要會(huì)涉及到的內(nèi)容,以下內(nèi)容的討論主要以電商系統(tǒng)中的購(gòu)物車(chē)、訂單為例進(jìn)行說(shuō)明。
在分布式微服務(wù)架構(gòu)中,應(yīng)用中被拆分出來(lái)的子服務(wù)都是獨(dú)立部署、運(yùn)行和迭代的。單個(gè)服務(wù)新版本上線時(shí),我們?cè)僖膊恍枰獙?duì)應(yīng)用整體進(jìn)行發(fā)版,只需關(guān)注每個(gè)微服務(wù)自身的發(fā)布流程即可,如下:
為了驗(yàn)證服務(wù) Cart 的新版本,流量在整個(gè)調(diào)用鏈路上能夠通過(guò)某種方式有選擇的路由到 Cart 的灰度版本,這屬于微服務(wù)治理領(lǐng)域中流量治理問(wèn)題。常見(jiàn)的治理策略包括基于 Provider 和基于 Consumer 的方式。
- 基于 Provider 的治理策略。配置 Cart 的流量流入規(guī)則,User 路由到 Cart 時(shí)使用 Cart 的流量流入規(guī)則。
- 基于 Consumer 的治理策略。配置 User 的流量流出規(guī)則, User 路由到 Cart 時(shí)使用 User 的流量流出規(guī)則。
此外,使用這些治理策略時(shí)可以結(jié)合上面介紹的藍(lán)綠發(fā)布和灰度發(fā)布方案來(lái)實(shí)施真正的服務(wù)級(jí)別的版本發(fā)布。
全鏈路灰度
繼續(xù)考慮上面微服務(wù)體系中對(duì)服務(wù) Cart 進(jìn)行發(fā)布的場(chǎng)景,如果此時(shí)服務(wù) Order 也需要發(fā)布新版本,由于本次新功能涉及到服務(wù) Cart 和 Order 的共同變動(dòng),所以要求在灰度驗(yàn)證時(shí)能夠使得灰度流量同時(shí)經(jīng)過(guò)服務(wù) Cart 和 Order 的灰度版本。如下圖:
按照上一小節(jié)提出的兩種治理策略,我們需要額外配置服務(wù) Order 的治理規(guī)則,確保來(lái)自灰度環(huán)境的服務(wù) Cart 的流量轉(zhuǎn)發(fā)至服務(wù) Order 的灰度版本。這樣的做法看似符合正常的操作邏輯,但在真實(shí)業(yè)務(wù)場(chǎng)景中,業(yè)務(wù)的微服務(wù)規(guī)模和數(shù)量遠(yuǎn)超我們的例子,其中一條請(qǐng)求鏈路可能經(jīng)過(guò)數(shù)十個(gè)微服務(wù),新功能發(fā)布時(shí)也可能會(huì)涉及到多個(gè)微服務(wù)同時(shí)變更,并且業(yè)務(wù)的服務(wù)之間依賴(lài)錯(cuò)綜復(fù)雜,頻繁的服務(wù)發(fā)布、以及服務(wù)多版本并行開(kāi)發(fā)導(dǎo)致流量治理規(guī)則日益膨脹,給整個(gè)系統(tǒng)的維護(hù)性和穩(wěn)定性帶來(lái)了不利因素。
對(duì)于以上的問(wèn)題,開(kāi)發(fā)者結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景和生產(chǎn)實(shí)踐經(jīng)驗(yàn),提出了一種端到端的灰度發(fā)布方案,即全鏈路灰度。全鏈路灰度治理策略主要專(zhuān)注于整個(gè)調(diào)用鏈,它不關(guān)心鏈路上經(jīng)過(guò)具體哪些微服務(wù),流量控制視角從服務(wù)轉(zhuǎn)移至請(qǐng)求鏈路上,僅需要少量的治理規(guī)則即可構(gòu)建出從網(wǎng)關(guān)到整個(gè)后端服務(wù)的多個(gè)流量隔離環(huán)境,有效保證了多個(gè)親密關(guān)系的服務(wù)順利安全發(fā)布以及服務(wù)多版本并行開(kāi)發(fā),進(jìn)一步促進(jìn)業(yè)務(wù)的快速發(fā)展。
全鏈路灰度的解決方案
如何在實(shí)際業(yè)務(wù)場(chǎng)景中去快速落地全鏈路灰度呢?目前,主要有兩種解決思路,基于物理環(huán)境隔離和基于邏輯環(huán)境隔離。
物理環(huán)境隔離
物理環(huán)境隔離,顧名思義,通過(guò)增加機(jī)器的方式來(lái)搭建真正意義上的流量隔離,物理隔離更多的屬于藍(lán)綠發(fā)布方案。
這種方案需要為要灰度的服務(wù)搭建一套網(wǎng)絡(luò)隔離、資源獨(dú)立的環(huán)境,在其中部署服務(wù)的灰度版本。由于與正式環(huán)境隔離,正式環(huán)境中的其他服務(wù)無(wú)法訪問(wèn)到需要灰度的服務(wù),所以需要在灰度環(huán)境中冗余部署這些線上服務(wù),以便整個(gè)調(diào)用鏈路正常進(jìn)行流量轉(zhuǎn)發(fā)。此外,注冊(cè)中心等一些其他依賴(lài)的中間件組件也需要冗余部署在灰度環(huán)境中,保證微服務(wù)之間的可見(jiàn)性問(wèn)題,確保獲取的節(jié)點(diǎn) IP 地址只屬于當(dāng)前的網(wǎng)絡(luò)環(huán)境。
這個(gè)方案一般用于企業(yè)的測(cè)試、預(yù)發(fā)開(kāi)發(fā)環(huán)境的搭建,對(duì)于線上灰度發(fā)布引流的場(chǎng)景來(lái)說(shuō)其靈活性不夠。況且,微服務(wù)多版本的存在在微服務(wù)架構(gòu)中是家常便飯,需要為這些業(yè)務(wù)場(chǎng)景采用堆機(jī)器的方式來(lái)維護(hù)多套灰度環(huán)境。如果您的應(yīng)用數(shù)目過(guò)多的情況下,會(huì)造成運(yùn)維、機(jī)器成本過(guò)大,成本和代價(jià)遠(yuǎn)超收益;如果應(yīng)用數(shù)目很小,就兩三個(gè)應(yīng)用,這個(gè)方式還是很方便的,可以接受的。
邏輯環(huán)境隔離
另一種方案是構(gòu)建邏輯上的環(huán)境隔離,我們只需部署服務(wù)的灰度版本,流量在調(diào)用鏈路上流轉(zhuǎn)時(shí),由流經(jīng)的網(wǎng)關(guān)、各個(gè)中間件以及各個(gè)微服務(wù)來(lái)識(shí)別灰度流量,并動(dòng)態(tài)轉(zhuǎn)發(fā)至對(duì)應(yīng)服務(wù)的灰度版本。如下圖:
上圖可以很好展示這種方案的效果,我們用不同的顏色來(lái)表示不同版本的灰度流量,可以看出無(wú)論是微服務(wù)網(wǎng)關(guān)還是微服務(wù)本身都需要識(shí)別流量,根據(jù)治理規(guī)則做出動(dòng)態(tài)決策。當(dāng)服務(wù)版本發(fā)生變化時(shí),這個(gè)調(diào)用鏈路的轉(zhuǎn)發(fā)也會(huì)實(shí)時(shí)改變。相比于利用機(jī)器搭建的灰度環(huán)境,這種方案不僅可以節(jié)省大量的機(jī)器成本和運(yùn)維人力,而且可以幫助開(kāi)發(fā)者實(shí)時(shí)快速的對(duì)線上流量進(jìn)行精細(xì)化的全鏈路控制。
那么全鏈路灰度具體是如何實(shí)現(xiàn)呢?通過(guò)上面的討論,我們需要解決以下問(wèn)題:
- 鏈路上各個(gè)組件和服務(wù)能夠根據(jù)請(qǐng)求流量特征進(jìn)行動(dòng)態(tài)路由
- 需要對(duì)服務(wù)下的所有節(jié)點(diǎn)進(jìn)行分組,能夠區(qū)分版本
- 需要對(duì)流量進(jìn)行灰度標(biāo)識(shí)、版本標(biāo)識(shí)
- 需要識(shí)別出不同版本的灰度流量
接下來(lái),會(huì)介紹解決上述問(wèn)題需要用到的技術(shù)。
版本路由
無(wú)論是網(wǎng)關(guān)暴露的http接口還是dubbo接口,都會(huì)有相應(yīng)的版本號(hào),在進(jìn)行灰度發(fā)布設(shè)計(jì)的時(shí)候可充分考慮前端、網(wǎng)關(guān)、后端dubbo服務(wù)版本號(hào)傳遞。
標(biāo)簽路由
標(biāo)簽路由通過(guò)對(duì)服務(wù)下所有節(jié)點(diǎn)按照標(biāo)簽名和標(biāo)簽值不同進(jìn)行分組,使得訂閱該服務(wù)節(jié)點(diǎn)信息的服務(wù)消費(fèi)端可以按需訪問(wèn)該服務(wù)的某個(gè)分組,即所有節(jié)點(diǎn)的一個(gè)子集。服務(wù)消費(fèi)端可以使用服務(wù)提供者節(jié)點(diǎn)上的任何標(biāo)簽信息,根據(jù)所選標(biāo)簽的實(shí)際含義,消費(fèi)端可以將標(biāo)簽路由應(yīng)用到更多的業(yè)務(wù)場(chǎng)景中。
節(jié)點(diǎn)打標(biāo)
那么如何給服務(wù)節(jié)點(diǎn)添加不同的標(biāo)簽?zāi)兀吭谌缃窕馃岬脑圃夹g(shù)推動(dòng)下,大多數(shù)業(yè)務(wù)都在積極進(jìn)行容器化改造之旅。這里,我就以容器化的應(yīng)用為例,介紹在使用 Kube.NETes Service 作為服務(wù)發(fā)現(xiàn)和使用比較流行的 Nacos 注冊(cè)中心這兩種場(chǎng)景下如何對(duì)服務(wù) Workload 進(jìn)行節(jié)點(diǎn)打標(biāo)。
在使用 Kubernetes Service 作為服務(wù)發(fā)現(xiàn)的業(yè)務(wù)系統(tǒng)中,服務(wù)提供者通過(guò)向 ApiServer 提交 Service 資源完成服務(wù)暴露,服務(wù)消費(fèi)端監(jiān)聽(tīng)與該 Service 資源下關(guān)聯(lián)的 Endpoint 資源,從 Endpoint 資源中獲取關(guān)聯(lián)的業(yè)務(wù) Pod 資源,讀取上面的 Labels 數(shù)據(jù)并作為該節(jié)點(diǎn)的元數(shù)據(jù)信息。所以,我們只要在業(yè)務(wù)應(yīng)用描述資源 Deployment 中的 Pod 模板中為節(jié)點(diǎn)添加標(biāo)簽即可。
在使用 Nacos 作為服務(wù)發(fā)現(xiàn)的業(yè)務(wù)系統(tǒng)中,一般是需要業(yè)務(wù)根據(jù)其使用的微服務(wù)框架來(lái)決定打標(biāo)方式。如果 JAVA 應(yīng)用使用的 Spring Cloud 微服務(wù)開(kāi)發(fā)框架,我們可以為業(yè)務(wù)容器添加對(duì)應(yīng)的環(huán)境變量來(lái)完成標(biāo)簽的添加操作。比如我們希望為節(jié)點(diǎn)添加版本灰度標(biāo),那么為業(yè)務(wù)容器添加spring.cloud.nacos.discovery.metadata.version=gray,這樣框架向Nacos注冊(cè)該節(jié)點(diǎn)時(shí)會(huì)為其添加一個(gè)標(biāo)簽verison=gray。
流量染色
請(qǐng)求鏈路上各個(gè)組件如何識(shí)別出不同的灰度流量?答案就是流量染色,為請(qǐng)求流量添加不同灰度標(biāo)識(shí)來(lái)方便區(qū)分。我們可以在請(qǐng)求的源頭上對(duì)流量進(jìn)行染色,前端在發(fā)起請(qǐng)求時(shí)根據(jù)用戶信息或者平臺(tái)信息的不同對(duì)流量進(jìn)行打標(biāo)。如果前端無(wú)法做到,我們也可以在微服務(wù)網(wǎng)關(guān)上對(duì)匹配特定路由規(guī)則的請(qǐng)求動(dòng)態(tài)添加流量標(biāo)識(shí)。此外,流量在鏈路中流經(jīng)灰度節(jié)點(diǎn)時(shí),如果請(qǐng)求信息中不含有灰度標(biāo)識(shí),需要自動(dòng)為其染色,接下來(lái)流量就可以在后續(xù)的流轉(zhuǎn)過(guò)程中優(yōu)先訪問(wèn)服務(wù)的灰度版本。
邏輯環(huán)境隔離——基于 SDK
上面我們?cè)敿?xì)介紹了實(shí)現(xiàn)全鏈路灰度所需要的幾種技術(shù),如果想為現(xiàn)有的業(yè)務(wù)接入全鏈路灰度能力,不可避免的需要為業(yè)務(wù)使用的開(kāi)發(fā)框架 SDK 進(jìn)行改造。首先,需要支持動(dòng)態(tài)路由功能,對(duì)于 Spring Cloud、Dubbo 開(kāi)發(fā)框架,可以對(duì)出口流量實(shí)現(xiàn)自定義 Filter,在該 Filter 中完成流量識(shí)別以及標(biāo)簽路由。同時(shí)需要借助分布式鏈路追蹤技術(shù)完成流量標(biāo)識(shí)鏈路傳遞以及流量自動(dòng)染色。此外,需要引入一個(gè)中心化的流量治理平臺(tái),方便各個(gè)業(yè)務(wù)線的開(kāi)發(fā)者定義自己的全鏈路灰度規(guī)則。基于 SDK 實(shí)現(xiàn)方式的圖例如下:
邏輯環(huán)境隔離——基于 Java Agent
基于 SDK 方式的弊端在于需要業(yè)務(wù)進(jìn)行 SDK 版本升級(jí),甚至?xí)婕暗綐I(yè)務(wù)代碼的變動(dòng)。企業(yè)內(nèi)部各個(gè)微服務(wù)雖然使用同一種開(kāi)發(fā)框架,但很難保證框架版本是一致的,所以不得不為每一個(gè)版本維護(hù)一份全鏈路灰度的代碼。業(yè)務(wù)代碼與 SDK 代碼緊耦合,SDK 版本迭代會(huì)觸發(fā)業(yè)務(wù)不必要的發(fā)版變更,對(duì)業(yè)務(wù)的侵入性比較強(qiáng)。
另一種比較流行的方式是基于字節(jié)碼增強(qiáng)技術(shù)在編譯時(shí)對(duì)開(kāi)發(fā)框架進(jìn)行功能拓展,這種方案業(yè)務(wù)無(wú)感知,以無(wú)侵入方式為業(yè)務(wù)引入全鏈路灰度能力。基于 Java Agent 的實(shí)現(xiàn)方式的圖例如下:
但仍然無(wú)法避免是開(kāi)發(fā)者需要為業(yè)務(wù)使用版本不一致的開(kāi)發(fā)框架維護(hù)對(duì)應(yīng)的 Java Agent 的版本。如果您比較傾向于這種無(wú)侵入的方案但又不想自己來(lái)維護(hù),您可以選擇阿里云 MSE 服務(wù)治理產(chǎn)品,該產(chǎn)品就是一款基于 Java Agent 實(shí)現(xiàn)的無(wú)侵入式企業(yè)生產(chǎn)級(jí)服務(wù)治理產(chǎn)品,您不需要修改任何一行業(yè)務(wù)代碼,即可擁有不限于全鏈路灰度的治理能力,并且支持近 5 年內(nèi)所有的 Spring Boot、Spring Cloud 和 Dubbo。
邏輯環(huán)境隔離——基于 Service Mesh
在業(yè)務(wù)系統(tǒng)的微服務(wù)架構(gòu)中,如果存在大量使用不同的技術(shù)棧、語(yǔ)言棧的微服務(wù),Java Agent 的方式就無(wú)能為力了。我們可能需要為每一個(gè)語(yǔ)言的 SDK 編寫(xiě)和維護(hù)全鏈路灰度代碼,不僅需要不同語(yǔ)言棧的開(kāi)發(fā)者,而且涉及到語(yǔ)言無(wú)關(guān)的 bug 修復(fù)時(shí)需要全語(yǔ)言版本的 SDK 共同升級(jí),這種代價(jià)不見(jiàn)得比基于物理環(huán)境隔離方案小。
那有沒(méi)有一種與語(yǔ)言無(wú)關(guān)的方案呢?有,下一代微服務(wù)架構(gòu)服務(wù)網(wǎng)格,Service Mesh。它將分布式服務(wù)的通信層抽象為單獨(dú)的一層,在這一層中實(shí)現(xiàn)負(fù)載均衡、服務(wù)發(fā)現(xiàn)、認(rèn)證授權(quán)、監(jiān)控追蹤、流量控制等分布式系統(tǒng)所需要的功能。顯然,我們所需的全鏈路灰度能力也可以在這個(gè)流量治理基礎(chǔ)設(shè)施層來(lái)實(shí)現(xiàn)。幸運(yùn)的是,服務(wù)網(wǎng)格明星產(chǎn)品Istio以聲明式 API 資源對(duì)流量治理進(jìn)行了統(tǒng)一抽象,借助于 VirtualService 和 DestinationRule 治理規(guī)則可以很容易實(shí)現(xiàn)全鏈路灰度的效果,并且Istio集成了各種主流的分布式鏈路追蹤框架。基于 Service Mesh 的實(shí)現(xiàn)方式的圖例如下:
在實(shí)際生產(chǎn)環(huán)境中,服務(wù)多版本并行開(kāi)發(fā)是很常見(jiàn)的事情,而且版本迭代速度非常快。版本每次變更都需要修改 VirtualSerivice 資源中路由匹配規(guī)則,另外 VirtualSerivice 資源中并沒(méi)有提供容災(zāi)能力。比如存在一條路由規(guī)則訪問(wèn)服務(wù)提供方的某個(gè)灰度版本,如果目標(biāo)服務(wù)不存在該灰度版本或者不可用,按照目前 Istio 的實(shí)現(xiàn)是仍然將流量轉(zhuǎn)發(fā)至該版本,缺乏容災(zāi)機(jī)制。還有一種業(yè)務(wù)場(chǎng)景,如果我們希望對(duì)處于一定 UID 范圍的用戶流量轉(zhuǎn)發(fā)指定灰度環(huán)境,是無(wú)法通過(guò) Istio 現(xiàn)有的流量治理規(guī)則實(shí)現(xiàn)的。此時(shí),您可以選擇阿里云服務(wù)網(wǎng)格產(chǎn)品 ASM,是一個(gè)統(tǒng)一管理微服務(wù)應(yīng)用流量、兼容 Istio 的托管式平臺(tái)。ASM 針對(duì)上述兩個(gè)場(chǎng)景都有應(yīng)對(duì)方案,輕松解決您在多語(yǔ)言場(chǎng)景下的全鏈路灰度訴求。
三種方式對(duì)比
下表是三種方式對(duì)比,從多個(gè)方面進(jìn)行了對(duì)比:
- 如果您傾向于使用無(wú)侵入式的 Java Agent 的方式,但又擔(dān)心自建帶來(lái)的穩(wěn)定性問(wèn)題,您可以選擇 MSE 微服務(wù)治理產(chǎn)品,該產(chǎn)品是阿里巴巴內(nèi)部多年在微服務(wù)治理領(lǐng)域的沉淀的產(chǎn)出,經(jīng)歷了各種大促考驗(yàn)。
- 如果您傾向于使用語(yǔ)言無(wú)關(guān)、無(wú)侵入式的 Service Mesh 的方式,但又擔(dān)心自建帶來(lái)的穩(wěn)定性問(wèn)題,您可以選擇阿里云 ASM 產(chǎn)品,相比開(kāi)源 Istio,在功能性、穩(wěn)定性和安全性都有很大的提升。
在分布式應(yīng)用中,作為流量入口的網(wǎng)關(guān)是不可或缺的。在全鏈路灰度場(chǎng)景中,就要求微服務(wù)網(wǎng)關(guān)具備豐富的流量治理能力,支持服務(wù)多版本路由,支持對(duì)特定路由規(guī)則上的請(qǐng)求進(jìn)行動(dòng)態(tài)打標(biāo)。對(duì)于入口服務(wù)可見(jiàn)性問(wèn)題,網(wǎng)關(guān)需要支持多種服務(wù)發(fā)現(xiàn)方式。安全性問(wèn)題上,網(wǎng)關(guān)作為集群對(duì)外的入口可以對(duì)所有請(qǐng)求流量進(jìn)行認(rèn)證鑒權(quán),保障業(yè)務(wù)系統(tǒng)不被非法流量入侵。
在虛擬化時(shí)期的微服務(wù)架構(gòu)下,業(yè)務(wù)通常采用流量網(wǎng)關(guān) + 微服務(wù)網(wǎng)關(guān)的兩層架構(gòu),流量網(wǎng)關(guān)負(fù)責(zé)南北向流量調(diào)度和安全防護(hù),微服務(wù)網(wǎng)關(guān)負(fù)責(zé)東西向流量調(diào)度和服務(wù)治理。傳統(tǒng)的網(wǎng)關(guān)比如zuul、springcloud gateway以及Apache的shenyu網(wǎng)關(guān)都可以擴(kuò)展在一定程度上支持灰度發(fā)布。
在容器和 K8s 主導(dǎo)的云原生時(shí)代,Ingress 成為 K8s 生態(tài)的網(wǎng)關(guān)標(biāo)準(zhǔn),賦予了網(wǎng)關(guān)新的使命,使得流量網(wǎng)關(guān) + 微服務(wù)網(wǎng)關(guān)合二為一成為可能。阿里云 MSE 發(fā)布的云原生網(wǎng)關(guān)在能力不打折的情況下,將兩層網(wǎng)關(guān)變?yōu)橐粚樱粌H可以節(jié)省 50% 的資源成本,還可以降低運(yùn)維及使用成本。最重要的是,云原生網(wǎng)關(guān)支持與后端微服務(wù)治理聯(lián)動(dòng)實(shí)現(xiàn)端到端的全鏈路灰度。
灰度設(shè)計(jì)實(shí)踐
灰度發(fā)布的最終要解決的問(wèn)題,基于一定的流量測(cè)試,測(cè)試沒(méi)有問(wèn)題后會(huì)進(jìn)行全量放開(kāi),具體如下圖:
具體實(shí)現(xiàn)思路如下:
- 在代碼中做。
一套線上環(huán)境,代碼中做開(kāi)關(guān),對(duì)于不同的用戶走不同的邏輯
- 在接入層做。
多套(隔離的)線上環(huán)境,接入層針對(duì)不同用戶轉(zhuǎn)發(fā)到不同的環(huán)境中
兩種方案的優(yōu)缺點(diǎn):
方案
優(yōu)點(diǎn)
缺點(diǎn)
在代碼中做
靈活,粒度細(xì);一套代碼(環(huán)境)運(yùn)維成本低
灰度邏輯侵入代碼
在接入層做
無(wú)需(少)侵入代碼;風(fēng)險(xiǎn)小
多套線上環(huán)境,運(yùn)維成本高
靈活的灰度方案一般需要在接入層實(shí)現(xiàn),具體就是自定義負(fù)載均衡策略實(shí)現(xiàn)。
下面介紹在接入層使用的方式:
- Nginx層實(shí)現(xiàn)(使用nginx+lua)
- 網(wǎng)關(guān)層實(shí)現(xiàn)
- dubbo的灰度,項(xiàng)目中如果使用dubbo,有可能會(huì)需要dubbo服務(wù)的灰度實(shí)現(xiàn)。
負(fù)載均衡又可分為服務(wù)端負(fù)載均衡和客戶端負(fù)載均衡
服務(wù)器端負(fù)載均衡:例如Nginx,通過(guò)Nginx進(jìn)行負(fù)載均衡,先發(fā)送請(qǐng)求,然后通過(guò)負(fù)載均衡算法,在多個(gè)服務(wù)器之間選擇一個(gè)進(jìn)行訪問(wèn);即在服務(wù)器端再進(jìn)行負(fù)載均衡算法分配。
客戶端負(fù)載均衡:例如ribbon或者dubbo,客戶端會(huì)有一個(gè)服務(wù)器地址列表,在發(fā)送請(qǐng)求前通過(guò)負(fù)載均衡算法選擇一個(gè)服務(wù)器,然后進(jìn)行訪問(wèn),這是客戶端負(fù)載均衡;即在客戶端就進(jìn)行負(fù)載均衡算法分配。
基于Nginx灰度發(fā)布方案
基于前端不同的用戶的cookie:tts_version_id的值不同進(jìn)行灰度分流,具體nginx.conf配置如下圖:
如果用戶灰度分流的策略比較復(fù)雜,推薦兩個(gè)開(kāi)源的項(xiàng)目Openresty、ABTestingGateway。
Openresty
OpenResty® 是一個(gè)基于 Nginx 與 Lua 的高性能 Web 平臺(tái),其內(nèi)部集成了大量精良的 Lua 庫(kù)、第三方模塊以及大多數(shù)的依賴(lài)項(xiàng)。用于方便地搭建能夠處理超高并發(fā)、擴(kuò)展性極高的動(dòng)態(tài) Web 應(yīng)用、Web 服務(wù)和動(dòng)態(tài)網(wǎng)關(guān)。
Openresty學(xué)習(xí)地址:https://moonbingbing.gitbooks.io/openresty-best-practices/content/base/intro.html
ABTestingGateway
ABTestingGateway是新浪微博的開(kāi)源的項(xiàng)目,是一個(gè)可以動(dòng)態(tài)設(shè)置分流策略的灰度發(fā)布系統(tǒng),工作在7層,基于nginx和ngx-lua開(kāi)發(fā),使用 redis 作為分流策略數(shù)據(jù)庫(kù),可以實(shí)現(xiàn)動(dòng)態(tài)調(diào)度功能。
ABTestingGateway是在 nginx 轉(zhuǎn)發(fā)的框架內(nèi),在轉(zhuǎn)向 upstream 前,根據(jù) 用戶請(qǐng)求特征 和 系統(tǒng)的分流策略 ,查找出目標(biāo)upstream,進(jìn)而實(shí)現(xiàn)分流。
nginx實(shí)現(xiàn)的灰度系統(tǒng)中,分流邏輯往往通過(guò) rewrite 階段的 if 和rewrite 指令等實(shí)現(xiàn),優(yōu)點(diǎn)是性能較高,缺點(diǎn)是功能受限、容易出錯(cuò),以及轉(zhuǎn)發(fā)規(guī)則固定,只能靜態(tài)分流。針對(duì)這些缺點(diǎn), ABTestingGateway,采用ngx-lua 實(shí)現(xiàn)系統(tǒng)功能,通過(guò)啟用lua-shared-dict和lua-resty-lock作為系統(tǒng)緩存和緩存鎖,系統(tǒng)獲得了較為接近原生nginx轉(zhuǎn)發(fā)的性能。
git地址:https://github.com/CNSRE/ABTestingGateway
基于dubbo灰度發(fā)布方案
dubbo在設(shè)計(jì)之初對(duì)灰度發(fā)布就提供了很好的支持,它現(xiàn)在支持兩種路由方式:條件路由、標(biāo)簽路由。
條件路由主要支持以服務(wù)或Consumer應(yīng)用為粒度配置路由規(guī)則,源碼對(duì)應(yīng)的路由器是ConditionRouter.java。
標(biāo)簽路由主要是以Provider應(yīng)用為粒度配置路由規(guī)則,通過(guò)將某一個(gè)或多個(gè)服務(wù)的提供者劃分到同一個(gè)分組,約束流量只在指定分組中流轉(zhuǎn),從而實(shí)現(xiàn)流量隔離的目的,對(duì)應(yīng)源碼路由器是TagRouter.java。
Dubbo原生路由器支持的場(chǎng)景其實(shí)已經(jīng)很豐富的了,比如條件路由器,可以設(shè)置排除預(yù)發(fā)布機(jī)器、指定Consumer應(yīng)用訪問(wèn)指定的Provider應(yīng)用,這其實(shí)就是藍(lán)綠發(fā)布,還能設(shè)置黑白名單、按網(wǎng)段訪問(wèn)等等,這些所有的標(biāo)簽管理我們都可以再dubbo-admin進(jìn)行管理。如果你的灰度分流策略過(guò)于復(fù)雜以至于上述兩個(gè)路由不滿足,我們可以自定義實(shí)現(xiàn)一個(gè)Router,基于Dubbo SPI的擴(kuò)展成本非常低,我們只需定義自己的路由器并把整合到應(yīng)用就可以了。
灰度路由器是以provider應(yīng)用為粒度配置路由規(guī)則的,包含兩個(gè)過(guò)濾器,條件過(guò)濾器和權(quán)重過(guò)濾器(不是Dubbo的過(guò)濾器),它們的主要任務(wù)根據(jù)配置過(guò)濾出備用provoider。配置信息放在分布式配置上,首次加載放入應(yīng)用緩存,一旦有變更將自動(dòng)更新。這樣在發(fā)布時(shí)可以動(dòng)態(tài)調(diào)整灰度參數(shù),達(dá)到逐步擴(kuò)大流量和影響人群的目的。
dubbo官方文檔,對(duì)于路由這塊的支持介紹的比較詳細(xì),具體文檔可以參考:https://dubbo.apache.org/zh/docs3-v2/java-sdk/advanced-features-and-usage/service/routing/routing-rule/
基于SpringCloud灰度發(fā)布方案
Ribbon 提供了幾個(gè)負(fù)載均衡的組件,其目的就是讓請(qǐng)求轉(zhuǎn)給合適的服務(wù)器處理。
默認(rèn)輪詢(xún)
自定義策略需要繼承AbstractLoadBalancerRule
開(kāi)源方案:
自定義DiscoveryEnabledRule繼承PredicateBaseRule
首先在請(qǐng)求開(kāi)始處,實(shí)現(xiàn)自己的灰度邏輯,比如下面的demo根據(jù)請(qǐng)求url如果包含‘version’向holder中添加route為A的標(biāo)識(shí),否則添加route為B的標(biāo)識(shí)。 (Holder本質(zhì)是一個(gè)localThread)
在目標(biāo)服務(wù)添加matadateMap
PredicateBaseRule中使用google提供的pridicate,MetadataAwarePredicate中實(shí)現(xiàn)apply方法判斷發(fā)現(xiàn)的服務(wù)是否是目標(biāo)服務(wù)
最后基于SpringCloud的灰度發(fā)布推薦如下兩個(gè)開(kāi)源項(xiàng)目:
https://github.com/SpringCloud/spring-cloud-gray
https://github.com/Nepxion/Discovery
灰度發(fā)布devops工具平臺(tái)分布式鏈路追蹤
還有一個(gè)很重要的問(wèn)題是如何保證灰度標(biāo)識(shí)能夠在鏈路中一直傳遞下去呢?如果在請(qǐng)求源頭染色,那么請(qǐng)求經(jīng)過(guò)網(wǎng)關(guān)時(shí),網(wǎng)關(guān)作為代理會(huì)將請(qǐng)求原封不動(dòng)的轉(zhuǎn)發(fā)給入口服務(wù),除非開(kāi)發(fā)者在網(wǎng)關(guān)的路由策略中實(shí)施請(qǐng)求內(nèi)容修改策略。接著,請(qǐng)求流量會(huì)從入口服務(wù)開(kāi)始調(diào)用下一個(gè)微服務(wù),會(huì)根據(jù)業(yè)務(wù)代碼邏輯形成新的調(diào)用請(qǐng)求,那么我們?nèi)绾螌⒒叶葮?biāo)識(shí)添加到這個(gè)新的調(diào)用請(qǐng)求,從而可以在鏈路中傳遞下去呢?
從單體架構(gòu)演進(jìn)到分布式微服務(wù)架構(gòu),服務(wù)之間調(diào)用從同一個(gè)線程中方法調(diào)用變?yōu)閺谋镜剡M(jìn)程的服務(wù)調(diào)用遠(yuǎn)端進(jìn)程中服務(wù),并且遠(yuǎn)端服務(wù)可能以多副本形式部署,以至于一條請(qǐng)求流經(jīng)的節(jié)點(diǎn)是不可預(yù)知的、不確定的,而且其中每一跳的調(diào)用都有可能因?yàn)榫W(wǎng)絡(luò)故障或服務(wù)故障而出錯(cuò)。分布式鏈路追蹤技術(shù)對(duì)大型分布式系統(tǒng)中請(qǐng)求調(diào)用鏈路進(jìn)行詳細(xì)記錄,核心思想就是通過(guò)一個(gè)全局唯一的 traceid 和每一條的 spanid 來(lái)記錄請(qǐng)求鏈路所經(jīng)過(guò)的節(jié)點(diǎn)以及請(qǐng)求耗時(shí),其中 traceid 是需要整個(gè)鏈路傳遞的。
借助于分布式鏈路追蹤思想,我們也可以傳遞一些自定義信息,比如灰度標(biāo)識(shí)。業(yè)界常見(jiàn)的分布式鏈路追蹤產(chǎn)品都支持鏈路傳遞用戶自定義的數(shù)據(jù),其數(shù)據(jù)處理流程如下圖所示:
其中可以使用SkyWalking作為全鏈路跟蹤中間件,能夠很好的根據(jù)traceId進(jìn)行灰度鏈路跟蹤。
監(jiān)控與告警
一個(gè)好的系統(tǒng)或產(chǎn)品,僅實(shí)現(xiàn)其基本功能是遠(yuǎn)遠(yuǎn)不夠的,可觀測(cè)、易運(yùn)維也是必不可少的。主要包括發(fā)布應(yīng)用監(jiān)控、告警事件通知、全局發(fā)布狀態(tài)以及周期統(tǒng)計(jì)報(bào)表,這里強(qiáng)烈推薦Prometheus+Grafana作為云原生監(jiān)控告警平臺(tái)。
灰度發(fā)布期間新老兩個(gè)集群運(yùn)行兩個(gè)應(yīng)用版本,需要能夠?qū)π吕霞旱某觥⑷肓髁康腝PS、耗時(shí)、失敗率等指標(biāo)進(jìn)行監(jiān)控,通過(guò)對(duì)比新舊兩個(gè)版本集群的實(shí)時(shí)監(jiān)控?cái)?shù)據(jù),用戶可以快速的發(fā)現(xiàn)問(wèn)題。如下圖所示:
文章最后推薦一下我的知識(shí)星球,知識(shí)星球可以和我探討一下技術(shù)與管理,歡迎大家加入。