混沌工程推動(dòng)可觀測(cè)性的最佳實(shí)踐 | IDCF

來(lái)源:混沌工程實(shí)踐?作者:水球潘/JohnnyPan
本文中,我們使用 DDD 領(lǐng)域建模的方法,設(shè)計(jì)了一個(gè)合理且復(fù)雜的微服務(wù)叫車系統(tǒng),并在該系統(tǒng)之上,利用OpenTelemetry進(jìn)行了可觀測(cè)性構(gòu)造實(shí)踐,最后采用“強(qiáng)化混沌工程”的方法論,借助故障注入手段,實(shí)現(xiàn)了用于驗(yàn)證可觀測(cè)性價(jià)值的最佳實(shí)踐,并將游戲沖關(guān)的玩法融入軟件開(kāi)發(fā)的生命周期中,提升應(yīng)用的排障能力,以此降低系統(tǒng)的MTTR。
一、一個(gè)類 Uber 叫車試點(diǎn)應(yīng)用

為了達(dá)到應(yīng)用可觀測(cè)性構(gòu)造的效果,需要設(shè)計(jì)一個(gè)足夠復(fù)雜的試點(diǎn)應(yīng)用,這里我選擇了這個(gè)類 Uber App 的設(shè)計(jì)和開(kāi)發(fā)。
1.1 領(lǐng)域建模
首先,通過(guò)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD的事件風(fēng)暴,集眾人之力完成該試點(diǎn)應(yīng)用的領(lǐng)域模型設(shè)計(jì):

隨后觀察領(lǐng)域中重復(fù)出現(xiàn)的名詞,找出限界上下文:

這樣,類 Uber 叫車試點(diǎn)應(yīng)用的領(lǐng)域模型建模完畢。
1.2 微服務(wù)架構(gòu)設(shè)計(jì)
接著,我們繼續(xù)設(shè)計(jì)試點(diǎn)應(yīng)用微服務(wù)化的系統(tǒng)架構(gòu):

類 Uber 叫車試點(diǎn)應(yīng)用有五個(gè)微服務(wù),其中四個(gè)是由限界上下文衍生出來(lái)的,包括:
- User 乘客?
- Match 叫車匹配
- Trip 載客旅程
- Payment 支付金流

二、OpenTelemetry 構(gòu)造可觀測(cè)性

平常在開(kāi)發(fā)環(huán)境中,遇到Bug的時(shí)候?yàn)榍蠓奖?,都?huì)開(kāi)啟調(diào)試器,借助中斷來(lái)確認(rèn)應(yīng)用的行為是否符合預(yù)期,但是,一旦應(yīng)用部署到各環(huán)境中,便不能再使用調(diào)試器來(lái)觀察應(yīng)用的行為了。
所謂的可觀測(cè)性,指的是應(yīng)用在被部署到某個(gè)線上環(huán)境后,其本身還能透過(guò)“某種機(jī)制”來(lái)讓開(kāi)發(fā)人員,便捷地觀測(cè)應(yīng)用本身在線上“各個(gè)時(shí)間點(diǎn)”的行為。




OpenTelemetry的發(fā)明并非要解決新的問(wèn)題,而是一個(gè)在可觀測(cè)性三大支柱的需求下,實(shí)現(xiàn)單一標(biāo)準(zhǔn)的框架。我們決定要嘗試OpenTelemetry這一項(xiàng)CNCF近期推出的技術(shù)框架。我們先來(lái)看一下在以前,我們都是如何做到分布式鏈路追蹤的?OpenTracing + Jaeger:


為了要在應(yīng)用中做分布式鏈路追蹤,以往我們都必須直接依賴廠商鏈路追蹤產(chǎn)品的SDK來(lái)去埋點(diǎn),但如此一來(lái),未來(lái)如果決定要更換廠商時(shí),則應(yīng)用會(huì)有很大一部分需要重寫。OpenTelemetry扮演的就正是那一層抽象的標(biāo)準(zhǔn)接口,如果應(yīng)用依賴的是OpenTelemetry的API,那未來(lái)要更換廠商時(shí),就只需要改變OpenTelemetry的配置就行,任何一行代碼都不需要重寫。2.3 Java Agent 的自動(dòng)采集首先要先到Github下載OpenTelemetry最新釋出的Java Agent JAR。https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases接下來(lái)只需要將這個(gè)JAR,在Dockerfile中將其COPY到鏡像中,然后在CMD中有關(guān)Java的執(zhí)行指令中,添加javaagent參數(shù),將其指向JAR的位置,一并執(zhí)行就行。


- 第一種方法:由環(huán)境變量去設(shè)置 include / exclude 的類別或方法。?
- 第二種方法:使用 @WithSpan 來(lái)增加想要被放進(jìn) Trace 的方法。


一直以來(lái),SRE 都希望能夠最小化可觀測(cè)性構(gòu)造實(shí)踐,以減少其所帶來(lái)的額外維護(hù)成本。因此,我們需要一種新的最佳實(shí)踐,來(lái)證明可觀測(cè)性構(gòu)造的價(jià)值。
三、推動(dòng)可觀測(cè)性的最佳實(shí)踐

經(jīng)過(guò)了思考與設(shè)計(jì),最后我參考了混沌工程的精神,開(kāi)發(fā)出一個(gè)方法,可以告訴我“目前的可觀測(cè)性實(shí)踐是否仍有缺陷”?我把這個(gè)方法稱之為“強(qiáng)化混沌工程”。3.1 叫車流量的自動(dòng)化模擬但是,目前僅有一個(gè)類 Uber 的叫車系統(tǒng),還需要模擬乘客和司機(jī)的行為,來(lái)自動(dòng)化地產(chǎn)生合理的叫車流量。
如上所附的動(dòng)畫(huà)所示,在經(jīng)過(guò)數(shù)日的開(kāi)發(fā)后,終于做出了一個(gè)簡(jiǎn)單的自動(dòng)化叫車流量產(chǎn)生系統(tǒng)。動(dòng)畫(huà)中呈現(xiàn)的是五個(gè)司機(jī)和一個(gè)乘客的情況模擬。| 序號(hào) | 描述 |
|---|---|
| 1 | 乘客進(jìn)行叫車匹配 |
| 2 | 系統(tǒng)完成匹配,并且匹配到了某一司機(jī) |
| 3 | 該名司機(jī)開(kāi)始此次載客服務(wù),并且朝著乘客的上車地點(diǎn)開(kāi)車,移動(dòng)的過(guò)程中不斷地向伺服器更新自身座標(biāo) |
| 4 | 乘客進(jìn)行叫車匹配 |
| 5 | 系統(tǒng)完成匹配,并且匹配到了某一司機(jī) |
| 6 | 該名司機(jī)開(kāi)始此次服務(wù),并朝著乘客的上車地點(diǎn)開(kāi)車,移動(dòng)的過(guò)程中不斷地向服務(wù)端更新自身座標(biāo) |
| 7 | 乘客不斷接收到司機(jī)最新的座標(biāo) |
| 8 | 司機(jī)抵達(dá)乘客的上車地點(diǎn),確認(rèn)乘客上車后,司機(jī)將狀態(tài)調(diào)整成“已上車” |
| 9 | 司機(jī)開(kāi)車前往目的地,移動(dòng)的過(guò)程中不斷地向服務(wù)端更新自身座標(biāo) |
| 10 | 司機(jī)抵達(dá)乘客欲前往的目的地,結(jié)束了服務(wù) |
| 11 | 叫車流程結(jié)束,乘客將自己的座標(biāo)更新到了隨機(jī)的位置并開(kāi)始了下一次的叫車匹配 |
看似簡(jiǎn)單的叫車流程,其背后的工作其實(shí)是必須由五個(gè)微服務(wù)以及 RabbitMQ 來(lái)協(xié)作完成的,現(xiàn)在我們已經(jīng)能夠創(chuàng)造大量且合理的叫車流量,總算可以來(lái)開(kāi)始實(shí)踐“強(qiáng)化混沌工程”了。
3.2 強(qiáng)化混沌工程
從 SRE 的視角上,可觀測(cè)性構(gòu)造的價(jià)值在于,我們要花多少時(shí)間才能夠察覺(jué)并修復(fù)好生產(chǎn)系統(tǒng)發(fā)現(xiàn)的問(wèn)題,即 MTTR。
現(xiàn)在已經(jīng)有了“自動(dòng)化叫車流量生成器”,也有評(píng)判可觀測(cè)性構(gòu)造的標(biāo)準(zhǔn)MTTR,那剩下我最缺的就是“故障”了。
微服務(wù)等分布式系統(tǒng)在開(kāi)發(fā)和運(yùn)維上帶來(lái)更高的門檻和復(fù)雜度,因此混沌工程便也開(kāi)始被不斷提倡。講白一點(diǎn),混沌工程就是“有目的地對(duì)待測(cè)系統(tǒng)搞破壞來(lái)提早揭露系統(tǒng)的問(wèn)題”。
為了揭露系統(tǒng)的問(wèn)題,我們需要先對(duì)待測(cè)系統(tǒng)定義其穩(wěn)態(tài)。以類 Uber 叫車試點(diǎn)應(yīng)用來(lái)說(shuō),穩(wěn)態(tài)便是“能夠完整且順暢地執(zhí)行每一個(gè)叫車流程”。
借鑒了混沌工程的思維,將其運(yùn)用到可觀測(cè)性場(chǎng)景中的話,則是“要有目的地在系統(tǒng)中搞破壞來(lái)提早揭露可觀測(cè)性構(gòu)造的缺陷”。
對(duì)此,由于使用場(chǎng)景較為特殊,是否有現(xiàn)存的第三方工具,能夠滿足類 Uber 叫車試點(diǎn)應(yīng)用定制化的混沌場(chǎng)景,我并沒(méi)有太大的自信,因此我決定自己實(shí)作一個(gè)能夠?qū)崿F(xiàn)混沌工程的技術(shù)架構(gòu),以下圖所示:

新開(kāi)了一個(gè)服務(wù)在圖正中央,稱之為Chaos-Server。
而在應(yīng)用各個(gè)服務(wù)中都會(huì)執(zhí)行一個(gè)Chaos Client ,即Chaos Agent。
Chaos-Server和各個(gè)Chaos-Client互相溝通,傳遞指令,來(lái)實(shí)現(xiàn)整個(gè)混沌工程的流程。
我們只需要在Chaos的操作頁(yè)上對(duì)Chaos-Server下達(dá)命令就好。
3.3 沖關(guān)游戲的基本玩法
我們把混沌工程的流程設(shè)計(jì)成了一款沖關(guān)游戲,下面是基本的玩法:
| 序號(hào) | 步驟描述 |
|---|---|
| 1 | 部署好Chaos-Server和Chaos-Client,啟動(dòng)自動(dòng)化叫車流量生成器。 |
| 2 | 瀏覽Chaos操作頁(yè) /api/chaos ,這個(gè)頁(yè)面可以用來(lái)對(duì)Chaos-Server下達(dá)指令。 |
| 3 | 開(kāi)始一個(gè)新的關(guān)卡:每一個(gè)關(guān)卡都可以由一串隨意的字串來(lái)產(chǎn)生。 |
| 4 | 調(diào)用 API,如/api/chaos/fun/56a8d709-9c22-489e-b44e-6d86f81796b2,其中的隨意字串就是新關(guān)卡的唯一標(biāo)識(shí)。 |
| 5 | 一群特定未知的Chaos就被埋好在應(yīng)用的各個(gè)服務(wù)中了。 |
| 6 | 接下來(lái)繼續(xù)在Chaos操作頁(yè)上進(jìn)行,操作頁(yè)上會(huì)顯示所有候選的Chaos名單,并且還會(huì)顯示在這些Chaos中,有幾個(gè)真的被激活了。 |
| 7 | 沖關(guān)開(kāi)始后,自動(dòng)化叫車流量生成器便會(huì)進(jìn)入到“不穩(wěn)定”的狀態(tài),由于Chaos的緣故,叫車流程將會(huì)受到影響而被阻斷。因此我們可以開(kāi)始進(jìn)行排障了。 |
| 8 | 在排障過(guò)程中,利用可觀測(cè)性構(gòu)造去觀察應(yīng)用行為,看看能否在最短的時(shí)間內(nèi)找出問(wèn)題來(lái)。 |
| 9 | 一旦找到任何潛在的問(wèn)題,對(duì)照Chaos操作頁(yè)上的名字,選擇最可疑的那一個(gè)將其殺掉。 |
| 10 | 由于Chaos的名字直接以破壞的內(nèi)容進(jìn)行命名,因此我們能根據(jù)名字來(lái)去進(jìn)行揣測(cè)。 |
| 11 | 如果殺掉成功,則Chaos的數(shù)量會(huì)少一個(gè)。反之,則會(huì)顯示訊息:”You are mis-killing trip.SaveTripDelay, he is not the mole!”。 |
| 12 | 反反覆覆地游玩,直到將所有Chaos趕盡殺絕為止。 |
| 13 | 如果最后發(fā)現(xiàn)無(wú)法通關(guān),則代表可觀測(cè)性不夠完整,此時(shí)應(yīng)該要記下筆記,對(duì)原先的穩(wěn)態(tài)假說(shuō)進(jìn)行修正,并再次進(jìn)行同一個(gè)關(guān)卡。 |
3.4 體驗(yàn)一次真正的沖關(guān)游戲
首先到Grafana的儀表盤上,可以看到三個(gè)基本面板:上方為 Metrics,包括:左上為即時(shí)的叫車匹配數(shù)量,右上為應(yīng)用中的錯(cuò)誤數(shù)量;而下方則顯示即時(shí)的 Logs。

開(kāi)始一個(gè)新的關(guān)卡 56a8d709-9c22-489e-b44e-6d86f81796b2:

發(fā)現(xiàn)叫車流程整個(gè)卡住了,看來(lái),Chaos是真的開(kāi)始在搞破壞了…。

一段時(shí)間之后,便在Slack頻道上收到了一個(gè)錯(cuò)誤告警,告訴我們是時(shí)候去排障了。

同時(shí)也在面板上發(fā)現(xiàn)上方叫車匹配的數(shù)量急劇下滑,而下方也開(kāi)始產(chǎn)生出了錯(cuò)誤日志:

搜索帶 “ERROR” 的日志,使用Loki的查詢語(yǔ)法:

點(diǎn)擊其中一個(gè)錯(cuò)誤日志,并且從日志的 traceID 欄,直接開(kāi)啟右半部 Jaeger 的 trace 頁(yè)面。

直接從 trace 上觀察,可以看到整個(gè)微服務(wù)的上半部是順利的,但到下方調(diào)用 /api/drivers/{driverId} 這個(gè) API 時(shí)發(fā)生了錯(cuò)誤。
同時(shí),觀察這個(gè) trace 的執(zhí)行時(shí)間,竟然高達(dá)了12秒!發(fā)現(xiàn)瓶頸為 /api/users/{userId} 這個(gè)API。

點(diǎn)擊 span 可以看到更多詳細(xì)信息,而從 DriverController.setDriverStatus 這個(gè)方法的 span 中可以看見(jiàn) exception.message 明目張膽地告訴你,它就是Chaos!

回到操作頁(yè)面上,把對(duì)應(yīng)到的兩個(gè)Chaos殺掉:
- user.SetDriverStatusAPIBlocked
- user.FindUserDelay





四、結(jié)束語(yǔ)

本文中,我們使用 DDD 領(lǐng)域建模的方法,設(shè)計(jì)了一個(gè)合理且復(fù)雜的微服務(wù)叫車系統(tǒng),并在該系統(tǒng)之上,利用OpenTelemetry進(jìn)行了可觀測(cè)性構(gòu)造實(shí)踐,最后采用“強(qiáng)化混沌工程”的方法論,借助提早揭露實(shí)踐瑕疵的故障注入手段,實(shí)現(xiàn)了用于驗(yàn)證可觀測(cè)性構(gòu)造價(jià)值的最佳實(shí)踐方式,以游戲沖關(guān)的玩法融入軟件開(kāi)發(fā)的生命周期中,提升我們對(duì)應(yīng)用的排障能力,以此降低可觀測(cè)性構(gòu)造的MTTR。
IDCF DevOps黑客馬拉松,獨(dú)創(chuàng)端到端DevOps體驗(yàn),精益創(chuàng)業(yè)+敏捷開(kāi)發(fā)+DevOps流水線的完美結(jié)合,2021年僅有的3場(chǎng)公開(kāi)課,數(shù)千人參與并一致五星推薦的金牌訓(xùn)練營(yíng),追求卓越的你一定不能錯(cuò)過(guò)!9月11-12日,上海站,企業(yè)組隊(duì)參賽&個(gè)人參賽均可,一年等一回,錯(cuò)過(guò)等一年,趕緊上車~??
