從源頭解決 Service Mesh 問題最徹底!
我在 Shopee 維護(hù)一個 Service Mesh 系統(tǒng),大部分的 RPC 調(diào)用要經(jīng)過這個系統(tǒng),這個系統(tǒng)每分鐘要處理上千萬的請求。我們在本文中就把它叫做 Oitsi 系統(tǒng)吧,方便描述一些。干的事情其實(shí)和 Istio 是差不多的。
Oitsi 將對 RPC 調(diào)用設(shè)置了很多錯誤碼,類似于 HTTP 協(xié)議的 404, 502 等等。Application 報出來的錯誤碼在一個區(qū)間,Oitsi 內(nèi)部產(chǎn)生的錯誤在另一個區(qū)間,比如 0-1000,類似于 System Internal Error,監(jiān)控這些錯誤碼可以讓我們知道這個系統(tǒng)的運(yùn)行情況。
這個系統(tǒng)自從接手之后就有一個問題,就是它每時每刻都在報出來很多內(nèi)部錯誤,比如發(fā)生內(nèi)部超時,路由信息找不到,等等,每分鐘有上萬個錯誤。然而,系統(tǒng)的運(yùn)行是完全正常的。
Oitsi 系統(tǒng)在正常情況下的錯誤
從這個脫敏之后的監(jiān)控可以看到,經(jīng)常有一些錯誤一下子動輒上萬,除了圖中幾 K 的那些錯誤,在 1K 以下有更多密集的錯誤,只不過它們都被其他巨量的錯誤給拉平了,在這張圖不明顯。
這就給我們造成了很多問題:到底是 Oitsi 真出了問題,還是屬于“正常的錯誤”?很難判斷,每次發(fā)生這種情況都費(fèi)時費(fèi)力。大部分情況都是排查一番,然后發(fā)現(xiàn)是用戶“濫用”造成的問題,不需要關(guān)心。而它又掩蓋了很多真實(shí)的問題,比如一個新的版本發(fā)布之后偶爾會有一些內(nèi)部的錯誤,是不應(yīng)該發(fā)生的,卻被真實(shí)的問題掩蓋住了?;谶@樣的監(jiān)控數(shù)據(jù)我們也無法設(shè)置告警,因?yàn)檫@些噪音太多了,即使有告警,也和沒有一樣。
這讓我想起之前在螞蟻的工作,我們有類似的問題。我有一年多的時間都在一個叫做“故障定位”的項(xiàng)目上。在螞蟻我們也有很多告警(99%的)都是無效的,給 On Call 的同事帶來很多噪音和打擾。在螞蟻的思路是:開發(fā)一個“智能系統(tǒng)”(AI Ops),當(dāng)告警發(fā)生的時候,自動地判斷這個告警是不是噪音,是不是真正的問題,問題出在了哪里。拿到 Oitsi 的例子上說,當(dāng)現(xiàn)在一個錯誤的數(shù)量突增,那么這個智能故障定位系統(tǒng)就去檢查 Oitsi 的一些指標(biāo)是否正常,導(dǎo)致告警的服務(wù)具體是什么,它之前是不是一直有類似的監(jiān)控曲線模式,如果有,說明它一直在發(fā)生,是正常的,我們可以不管。
這樣做了一年,效果還是不怎么樣。我倒是發(fā)現(xiàn),很多告警的規(guī)則本身就有問題,比如一個請求量每分鐘只有兩位數(shù)的服務(wù),領(lǐng)導(dǎo)的要求是 “1分鐘發(fā)現(xiàn)故障,5分鐘定位故障”,不要說自動定位,就算是人去判斷都不靠譜。為了達(dá)成這個目標(biāo),監(jiān)控團(tuán)隊設(shè)置了很多非常敏銳的告警,交給定位團(tuán)隊說:“我們負(fù)責(zé)發(fā)現(xiàn)問題,你們負(fù)責(zé)定位問題。如果出問題了,1分鐘之內(nèi)有告警觸發(fā),那么我們的工作就達(dá)標(biāo)了。但是至于沒有問題我們也觸發(fā)了很多噪音告警,就是你們的工作了。” 它們的 KPI 確實(shí)是完成了,只要有故障必定有告警。但事實(shí)是,在很多情況下,告警發(fā)出來,大家打開監(jiān)控,盯著監(jiān)控:“在等等看,看下一分鐘,有請求進(jìn)來了,服務(wù)沒問題!”
所以這一年工作里,我有一個想法,就是在源頭解決問題比使用高級的魔法系統(tǒng)去解決問題要簡單、徹底很多。我們真的需要這么多人來開發(fā)一個“魔法系統(tǒng)”來幫我們診斷這種問題嗎?
比如監(jiān)控配置的不對,那就優(yōu)化監(jiān)控。監(jiān)控為什么配置的不對?監(jiān)控系統(tǒng)太難用,UI 讓人捉摸不透,配置了告警無法調(diào)試,監(jiān)控只能保存7天的數(shù)據(jù),不能基于歷史的監(jiān)控數(shù)據(jù)配置告警。很多人為了“規(guī)則”,對服務(wù)配上了告警然后就走了,至于后面告警觸發(fā)了,也不去響應(yīng)。
回到 Oitsi 的問題上,我找了幾個服務(wù),發(fā)現(xiàn)這些 Oitsi 內(nèi)部錯誤上并不能完全說是“正常的錯誤”,畢竟它是錯誤,沒有錯誤會是正常的。只能說它沒有導(dǎo)致線上問題而已。它們是可以被修復(fù)的。于是一個月前,我決定從源頭去解決這些問題。把所有不應(yīng)該報告出來的錯誤都消滅掉。
乍一看這么多錯誤數(shù),用那么多團(tuán)隊在用,看起來是難以管理的,性價比非常低的工作。但是畢竟也沒有人催我要快點(diǎn)完成,我可以一點(diǎn)一點(diǎn)去做。做一點(diǎn)錯誤就少一些(只要我解決問題的速度比新的問題出現(xiàn)的速度快)。
于是我按照下面的流程開始處理:
- 在 Jira(我們內(nèi)部的工單系統(tǒng))建立一個專題 tag,叫做 oitsi-abuse,后面的工單可以關(guān)聯(lián)這個 tag,這樣,可以在處理的時候方便參考之前的 Case;
- 創(chuàng)建一個監(jiān)控,專門針對錯誤做一個面板,點(diǎn)擊面板右側(cè)的 Legend 可以直接跳到服務(wù)的監(jiān)控面板,在服務(wù)的監(jiān)控面板上顯示下游,并且關(guān)聯(lián) CMDB 的 PIC(Person in charge);
- 這樣,我從錯誤數(shù)最高的服務(wù)開始,查看監(jiān)控,看下游服務(wù),以及機(jī)器上的日志,看相關(guān)的錯誤碼是什么時候開始的,到底是什么引起的,確定了是服務(wù)的問題就創(chuàng)建工單給這個服務(wù)的負(fù)責(zé)人,然后跟他聯(lián)系,說明這個有什么問題,會對我們的監(jiān)控、告警造成什么影響,需要修復(fù);
- 等他確認(rèn)問題,然后要求提供一個 ETA(預(yù)計修復(fù)的時間),把 ETA 寫到工單中,到了時間去檢查確認(rèn);
- 如果是 Oitsi 本身的問題,去找 Oitsi 開發(fā)同事排查問題;
- 等所有的問題都解決了的話,對錯誤設(shè)置告警,一有錯誤就去聯(lián)系開發(fā)。一般情況下,都是他們做的配置變更或者發(fā)布引起了問題。這樣對于業(yè)務(wù)其實(shí)是更加健康的,我們發(fā)現(xiàn)問題的能力更強(qiáng)了。
就這樣,其實(shí)這樣坐下來就發(fā)現(xiàn)只有那么幾類問題,排查的速度越來越快。中間還發(fā)現(xiàn)一個庫,它會去對 Oitsi 服務(wù)做心跳檢查,這個檢查設(shè)置不當(dāng)會有一些錯誤。很多引用了這個庫的應(yīng)用都有一只在報錯誤的問題。但是我們系統(tǒng)本身其實(shí)已經(jīng)做了探活可以保證心跳之類的問題了,溝通之后這個庫的心跳檢查行為可以下線。于是庫發(fā)布了新的版本,我找所有的引用者去升級版本,很多錯誤一下子就消失了,非常有成就感。
這項(xiàng)工作的進(jìn)度比我想象中的要快,一個多月,聯(lián)系了 20 多個團(tuán)隊。雖然說也遇到了一些很扯的事情,明明是服務(wù) A 的問題,就直接讓我去找下游,讓我們排查半天,最后又說回來找服務(wù) A 負(fù)責(zé)人,拉了個群,擺出來日志,才承認(rèn)是自己的問題,開始排查。但是大部分團(tuán)隊都非常配合,說明問題之后馬上去排查,發(fā)現(xiàn)問題下一個版本就修復(fù)了。如此默契的合作讓我感到驚訝又幸福!現(xiàn)在,系統(tǒng)錯誤維持在 200 以下了,并且現(xiàn)有的錯誤都已經(jīng)找到了根因,還有3個服務(wù)待修復(fù)。最晚的會在 2 個周之后發(fā)布修復(fù)??梢灶A(yù)見到在不遠(yuǎn)的未來,這個系統(tǒng)將會成為一個 0 錯誤的系統(tǒng)!

今天系統(tǒng)報出的錯誤,還是有一些服務(wù)在一直報錯,不過已經(jīng)大大減少了。
這項(xiàng)工作雖然不涉及任何的 KPI 之類的,也沒有什么技術(shù)含量,還都是一些“溝通”的工作,但是卻帶給我很大的成就感。我相信它也會在未來節(jié)省我很多時間。比如說我們評估系統(tǒng)的 SLI 和 SLO,由于 false alarm 太多,導(dǎo)致要花很多工作確定 down time 有多少,現(xiàn)在直接通過監(jiān)控就可以確定了。
這項(xiàng)工作帶給我的一些感想:
- 從源頭解決問題最徹底;
- 不要害怕溝通;
- 錯誤的發(fā)生都有原因,排查下去,零就是零,一就是一(從這個 Case 看,也確實(shí)所有的錯誤都可以被解決的);
- 每個公司都有臟活,累活(畢業(yè)去的第一家公司維護(hù)爬蟲,也有很多臟活、累活),這些都需要有人去做。
需要補(bǔ)充一下,我并不是完全否定做故障定位的思路。畢竟之前在螞蟻,有四五個組在做相同的東西,我們(和其他做一樣?xùn)|西的組)嘗試過非常多的思路,也有很多人因?yàn)檫@些晉升了(你說去聯(lián)系了無數(shù)個團(tuán)隊,排查了很多問題,這有什么 impact 呢?你說自己做了一個“智能定位”系統(tǒng),晉升就穩(wěn)了吧。)。印象比較深刻的是有個項(xiàng)目制定了上千個(他們稱為)決策樹,簡單來說就是:如果發(fā)生這個,就去檢查這個。頗有成效,很多配置不當(dāng)?shù)母婢捅贿@種規(guī)則給過濾掉了(雖然我覺得直接改報警要好一些)。我非常佩服他們的毅力。
說了這么多濕貨,再說點(diǎn)干貨。我們其實(shí)還有一個問題沒有解決。如果讀者有思路,歡迎評論。
在 Service Mesh 中,所有的服務(wù)都是通過 Agent 來調(diào)用的。比如 App1 要調(diào)用 App2,它會把請求發(fā)到本地的 Agent 中,由 Agent 去調(diào)用 App2 所在機(jī)器的 Agent。
這里,超時的問題就難處理。比如我們設(shè)置了 1s 超時。假如說 server 端的 Application 超時了,那么 Server 段的 Agent 可以報告一個應(yīng)用超時錯誤,不算做我們 Oitsi 系統(tǒng)錯誤。但是對于客戶端的 Agent 呢?它無法知道到底是 Server 的應(yīng)用超時了,還是 Server 的 Agent 超時了。所以對于 Server 超時的情況下,客戶端的 Agent 總會報出一個內(nèi)部超時錯誤。

這種錯誤,我們當(dāng)前還是無法區(qū)分是否是由應(yīng)用引起的。