您好,登錄后才能下訂單哦!
Consul故障分析與優(yōu)化是怎么樣的,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
從微服務(wù)平臺(tái)的角度出發(fā)希望提供統(tǒng)一的服務(wù)注冊(cè)中心,讓任何的業(yè)務(wù)和團(tuán)隊(duì)只要使用這套基礎(chǔ)設(shè)施,相互發(fā)現(xiàn)只需要協(xié)商好服務(wù)名即可;還需要支持業(yè)務(wù)做多DC部署和故障切換。由于在擴(kuò)展性和多DC支持上的良好設(shè)計(jì),我們選擇了Consul,并采用了Consul推薦的架構(gòu),單個(gè)DC內(nèi)有Consul Server和Consul Agent,DC之間是WAN模式并且相互對(duì)等,結(jié)構(gòu)如下圖所示:
注:圖中只畫(huà)了四個(gè)DC,實(shí)際生產(chǎn)環(huán)境根據(jù)公司機(jī)房建設(shè)以及第三方云的接入情況,共有十幾個(gè)DC。
愛(ài)奇藝內(nèi)部的容器應(yīng)用平臺(tái)QAE與Consul進(jìn)行了集成。由于早期是基于Mesos/Marathon體系開(kāi)發(fā),沒(méi)有Pod容器組概念,無(wú)法友好的注入sidecar的容器,因此我們選擇了微服務(wù)模式中的第三方注冊(cè)模式,即由QAE系統(tǒng)實(shí)時(shí)向Consul同步注冊(cè)信息,如下圖所示;并且使用了Consul的external service模式,這樣可以避免兩個(gè)系統(tǒng)狀態(tài)不一致時(shí)引起故障,例如Consul已經(jīng)將節(jié)點(diǎn)或服務(wù)實(shí)例判定為不健康,但是QAE沒(méi)有感知到,也就不會(huì)重啟或重新調(diào)度,導(dǎo)致沒(méi)有健康實(shí)例可用。
其中QAE應(yīng)用與服務(wù)的關(guān)系表示例如下:
每個(gè)QAE應(yīng)用代表一組容器,應(yīng)用與服務(wù)的映射關(guān)系是松耦合的,根據(jù)應(yīng)用實(shí)際所在的DC將其關(guān)聯(lián)到對(duì)應(yīng)Consul DC即可,后續(xù)應(yīng)用容器的更新、擴(kuò)縮容、失敗重啟等狀態(tài)變化都會(huì)實(shí)時(shí)體現(xiàn)在Consul的注冊(cè)數(shù)據(jù)中。
微服務(wù)平臺(tái)API網(wǎng)關(guān)是服務(wù)注冊(cè)中心最重要的使用方之一。網(wǎng)關(guān)會(huì)根據(jù)地區(qū)、運(yùn)營(yíng)商等因素部署多個(gè)集群,每個(gè)網(wǎng)關(guān)集群會(huì)根據(jù)內(nèi)網(wǎng)位置對(duì)應(yīng)到一個(gè)Consul集群,并且從Consul查詢(xún)最近的服務(wù)實(shí)例,如下圖所示:
這里我們使用了Consul的PreparedQuery功能,對(duì)所有服務(wù)優(yōu)先返回本DC服務(wù)實(shí)例,如果本DC沒(méi)有則根據(jù)DC間RTT由近到遠(yuǎn)查詢(xún)其它DC數(shù)據(jù)。
Consul故障
Consul從2016年底上線(xiàn)開(kāi)始,已經(jīng)穩(wěn)定運(yùn)行超過(guò)三年時(shí)間,但是最近我們卻遇到了故障,收到了某個(gè)DC多臺(tái)Consul Server不響應(yīng)請(qǐng)求、大量Consul Agent連不上Server的告警,并且沒(méi)有自動(dòng)恢復(fù)。Server端觀(guān)察到的現(xiàn)象主要有:
raft協(xié)議不停選舉失敗,無(wú)法獲得leader;
HTTP&DNS查詢(xún)接口大量超時(shí),觀(guān)察到有些超過(guò)幾十秒才返回(正常應(yīng)當(dāng)是毫秒級(jí)別返回);
goroutine快速線(xiàn)性上升,內(nèi)存同步上升,最終觸發(fā)系統(tǒng)OOM;在日志中沒(méi)能找到明確的問(wèn)題,從監(jiān)控metrics則觀(guān)察到PreparedQuery的執(zhí)行耗時(shí)異常增大,如下圖所示:
此時(shí)API網(wǎng)關(guān)查詢(xún)服務(wù)信息也超時(shí)失敗,我們將對(duì)應(yīng)的網(wǎng)關(guān)集群切到了其它DC,之后重啟Consul進(jìn)程,恢復(fù)正常。
故障分析
經(jīng)過(guò)日志排查,發(fā)現(xiàn)故障前發(fā)生過(guò)DC間的網(wǎng)絡(luò)抖動(dòng)(RTT增加,伴隨丟包),持續(xù)時(shí)間大約1分鐘,我們初步分析是DC間網(wǎng)絡(luò)抖動(dòng)導(dǎo)致正常收到的PreparedQuery請(qǐng)求積壓在Server中無(wú)法快速返回,隨著時(shí)間積累越來(lái)越多,占用的goroutine和內(nèi)存也越來(lái)越多,最終導(dǎo)致Server異常。
跟隨這個(gè)想法,嘗試在測(cè)試環(huán)境復(fù)現(xiàn),共有4個(gè)DC,單臺(tái)Server的PreparedQuery QPS為1.5K,每個(gè)PreparedQuery查詢(xún)都會(huì)觸發(fā)3次跨DC查詢(xún),然后使用tc-netem工具模擬DC間的RTT增加的情況,得到了以下結(jié)果:
當(dāng)DC間RTT由正常的2ms變?yōu)?00ms之后,Consul Server的goroutine、內(nèi)存確實(shí)會(huì)線(xiàn)性增長(zhǎng),PreparedQuery執(zhí)行耗時(shí)也線(xiàn)性增長(zhǎng),如下圖所示:
雖然goroutine、內(nèi)存在增長(zhǎng),但是在OOM之前,Consul Server的其它功能未受影響,Raft協(xié)議工作正常,本DC的數(shù)據(jù)查詢(xún)請(qǐng)求也能正常響應(yīng);
在DC間RTT恢復(fù)到2ms的一瞬間,Consul Server丟失leader,接著Raft不停選舉失敗,無(wú)法恢復(fù)。
以上操作能夠穩(wěn)定的復(fù)現(xiàn)故障,使分析工作有了方向。首先基本證實(shí)了goroutine和內(nèi)存的增長(zhǎng)是由于PreparedQuery請(qǐng)求積壓導(dǎo)致的,而積壓的原因在初期是網(wǎng)絡(luò)請(qǐng)求阻塞,在網(wǎng)絡(luò)恢復(fù)后仍然積壓原因暫時(shí)未知,這時(shí)整個(gè)進(jìn)程應(yīng)當(dāng)是處于異常狀態(tài);那么,為什么網(wǎng)絡(luò)恢復(fù)之后Consul反而故障了呢?Raft只有DC內(nèi)網(wǎng)絡(luò)通信,為什么也異常了呢?是最讓我們困惑的問(wèn)題。
最開(kāi)始的時(shí)候?qū)⒅攸c(diǎn)放在了Raft問(wèn)題上,通過(guò)跟蹤社區(qū)issue,找到了hashicorp/raft#6852,其中描述到我們的版本在高負(fù)載、網(wǎng)絡(luò)抖動(dòng)情況下可能出現(xiàn)raft死鎖,現(xiàn)象與我們十分相似。但是按照issue更新Raft庫(kù)以及Consul相關(guān)代碼之后,測(cè)試環(huán)境復(fù)現(xiàn)時(shí)故障依然存在。
之后嘗試給Raft庫(kù)添加日志,以便看清楚Raft工作的細(xì)節(jié),這次我們發(fā)現(xiàn)Raft成員從進(jìn)入Candidate狀態(tài),到請(qǐng)求peer節(jié)點(diǎn)為自己投票,日志間隔了10s,而代碼中僅僅是執(zhí)行了一行metrics更新,如下圖所示:
因此懷疑metrics調(diào)用出現(xiàn)了阻塞,導(dǎo)致整個(gè)系統(tǒng)運(yùn)行異常,之后我們?cè)诎l(fā)布?xì)v史中找到了相關(guān)優(yōu)化,低版本的armon/go-metrics在Prometheus實(shí)現(xiàn)中采用了全局鎖sync.Mutex,所有metrics更新都需要先獲取這個(gè)鎖,而v0.3.3版本改用了sync.Map,每個(gè)metric作為字典的一個(gè)鍵,只在鍵初始化的時(shí)候需要獲取全局鎖,之后不同metric更新值的時(shí)候就不存在鎖競(jìng)爭(zhēng),相同metric更新時(shí)使用sync.Atomic保證原子操作,整體上效率更高。更新對(duì)應(yīng)的依賴(lài)庫(kù)之后,復(fù)現(xiàn)網(wǎng)絡(luò)抖動(dòng)之后,Consul Server可以自行恢復(fù)正常。
這樣看來(lái)的確是由于metrics代碼阻塞,導(dǎo)致了系統(tǒng)整體異常。但我們依然有疑問(wèn),復(fù)現(xiàn)環(huán)境下單臺(tái)Server 的PreparedQuery QPS為1.5K,而穩(wěn)定的網(wǎng)絡(luò)環(huán)境下單臺(tái)Server壓測(cè)QPS到2.8K時(shí)依然工作正常。也就是說(shuō)正常情況下原有代碼是滿(mǎn)足性能需求的,只有在故障時(shí)出現(xiàn)了性能問(wèn)題。
接下來(lái)的排查陷入了困境,經(jīng)過(guò)反復(fù)試驗(yàn),我們發(fā)現(xiàn)了一個(gè)有趣的現(xiàn)象:使用go 1.9編譯的版本(也是生產(chǎn)環(huán)境使用的版本)能復(fù)現(xiàn)出故障;同樣的代碼使用go 1.14編譯就無(wú)法復(fù)現(xiàn)出故障。經(jīng)過(guò)仔細(xì)查看,我們?cè)趃o的發(fā)布?xì)v史中找到了以下兩條記錄:
根據(jù)代碼我們找到了用戶(hù)反饋在go1.9~1.13版本,在大量goroutine同時(shí)競(jìng)爭(zhēng)一個(gè)sync.Mutex時(shí),會(huì)出現(xiàn)性能急劇下降的情況,這能很好的解釋我們的問(wèn)題。由于Consul代碼依賴(lài)了go 1.9新增的內(nèi)置庫(kù),我們無(wú)法用更低的版本編譯,因此我們將go 1.14中sync.Mutex相關(guān)的優(yōu)化去掉,如下圖所示,然后用這個(gè)版本的go編譯Consul,果然又可以復(fù)現(xiàn)我們的故障了。
回顧語(yǔ)言的更新歷史,go 1.9版本添加了公平鎖特性,在原有normal模式上添加了starvation模式,來(lái)避免鎖等待的長(zhǎng)尾效應(yīng)。但是normal模式下新的goroutine在運(yùn)行時(shí)有較高的幾率競(jìng)爭(zhēng)鎖成功,從而免去goroutine的切換,整體效率是較高的;而在starvation模式下,新的goroutine不會(huì)直接競(jìng)爭(zhēng)鎖,而是會(huì)把自己排到等待隊(duì)列末端,然后休眠等待喚醒,鎖按照等待隊(duì)列FIFO分配,獲取到鎖的goroutine被調(diào)度執(zhí)行,這樣會(huì)增加goroutine調(diào)度、切換的成本。在go 1.14中針對(duì)性能問(wèn)題進(jìn)行了改善,在starvation模式下,當(dāng)goroutine執(zhí)行解鎖操作時(shí),會(huì)直接將CPU時(shí)間讓給下一個(gè)等待鎖的goroutine執(zhí)行,整體上會(huì)使得被鎖保護(hù)部分的代碼得到加速執(zhí)行。
到此故障的原因就清楚了,首先網(wǎng)絡(luò)抖動(dòng),導(dǎo)致大量PreparedQuery請(qǐng)求積壓在Server中,同時(shí)也造成了大量的goroutine和內(nèi)存使用;在網(wǎng)絡(luò)恢復(fù)之后,積壓的PreparedQuery繼續(xù)執(zhí)行,在我們的復(fù)現(xiàn)場(chǎng)景下,積壓的goroutine量會(huì)超過(guò)150K,這些goroutine在執(zhí)行時(shí)都會(huì)更新metrics從而去獲取全局的sync.Mutex,此時(shí)切換到starvation模式并且性能下降,大量時(shí)間都在等待sync.Mutex,請(qǐng)求阻塞超時(shí);除了積壓的goroutine,新的PreparedQuery還在不停接收,獲取鎖時(shí)同樣被阻塞,結(jié)果是sync.Mutex保持在starvation模式無(wú)法自動(dòng)恢復(fù);另一方面raft代碼運(yùn)行會(huì)依賴(lài)定時(shí)器、超時(shí)、節(jié)點(diǎn)間消息的及時(shí)傳遞與處理,并且這些超時(shí)通常是秒、毫秒級(jí)別的,但metrics代碼阻塞過(guò)久,直接導(dǎo)致時(shí)序相關(guān)的邏輯無(wú)法正常運(yùn)行。
接著生產(chǎn)環(huán)境中我們將發(fā)現(xiàn)的問(wèn)題都進(jìn)行了更新,升級(jí)到go 1.14,armon/go-metrics v0.3.3,以及hashicorp/raft v1.1.2版本,使Consul達(dá)到一個(gè)穩(wěn)定狀態(tài)。此外還整理完善了監(jiān)控指標(biāo),核心監(jiān)控包括以下維度:
進(jìn)程:CPU、內(nèi)存、goroutine、連接數(shù)
Raft:成員狀態(tài)變動(dòng)、提交速率、提交耗時(shí)、同步心跳、同步延時(shí)
RPC:連接數(shù)、跨DC請(qǐng)求數(shù)
寫(xiě)負(fù)載:注冊(cè)&解注冊(cè)速率
讀負(fù)載:Catalog/Health/PreparedQuery請(qǐng)求量,執(zhí)行耗時(shí)
冗余注冊(cè)
根據(jù)Consul的故障期間的故障現(xiàn)象,我們對(duì)服務(wù)注冊(cè)中心的架構(gòu)進(jìn)行了重新審視。
在Consul的架構(gòu)中,某個(gè)DC Consul Server全部故障了就代表這個(gè)DC故障,要靠其它DC來(lái)做災(zāi)備。但是實(shí)際情況中,很多不在關(guān)鍵路徑上的服務(wù)、SLA要求不是特別高的服務(wù)并沒(méi)有多DC部署,這時(shí)如果所在DC的Consul故障,那么整個(gè)服務(wù)就會(huì)故障。
針對(duì)本身并沒(méi)有做多DC部署的服務(wù),如果可以在冗余DC注冊(cè),那么單個(gè)DC Consul故障時(shí),其它DC還可以正常發(fā)現(xiàn)。因此我們修改了QAE注冊(cè)關(guān)系表,對(duì)于本身只有單DC部署的服務(wù),系統(tǒng)自動(dòng)在其它DC也注冊(cè)一份,如下圖所示:
QAE這種冗余注冊(cè)相當(dāng)于在上層做了數(shù)據(jù)多寫(xiě)操作。Consul本身不會(huì)在各DC間同步服務(wù)注冊(cè)數(shù)據(jù),因此直接通過(guò)Consul Agent方式注冊(cè)的服務(wù)還沒(méi)有較好的冗余注冊(cè)方法,還是依賴(lài)服務(wù)本身做好多DC部署。
保障API網(wǎng)關(guān)
目前API網(wǎng)關(guān)的正常工作依賴(lài)于Consul PreparedQuery查詢(xún)結(jié)果在本地的緩存,目前的交互方式有兩方面問(wèn)題:
網(wǎng)關(guān)緩存是lazy的,網(wǎng)關(guān)第一次用到時(shí)才會(huì)從Consul查詢(xún)加載,Consul故障時(shí)查詢(xún)失敗會(huì)導(dǎo)致請(qǐng)求轉(zhuǎn)發(fā)失敗;
PreparedQuery內(nèi)部可能會(huì)涉及多次跨DC查詢(xún),耗時(shí)較多,屬于復(fù)雜查詢(xún),由于每個(gè)網(wǎng)關(guān)節(jié)點(diǎn)需要單獨(dú)構(gòu)建緩存,并且緩存有TTL,會(huì)導(dǎo)致相同的PreparedQuery查詢(xún)執(zhí)行很多次,查詢(xún)QPS會(huì)隨著網(wǎng)關(guān)集群規(guī)模線(xiàn)性增長(zhǎng)。
為了提高網(wǎng)關(guān)查詢(xún)Consul的穩(wěn)定性和效率,我們選擇為每個(gè)網(wǎng)關(guān)集群部署一個(gè)單獨(dú)的Consul集群,如下圖所示:
圖中紅色的是原有的Consul集群,綠色的是為網(wǎng)關(guān)單獨(dú)部署的Consul集群,它只在單DC內(nèi)部工作。我們開(kāi)發(fā)了Gateway-Consul-Sync組件,它會(huì)周期性的從公共Consul集群讀取服務(wù)的PreparedQuery查詢(xún)結(jié)果,然后寫(xiě)入到綠色的Consul集群,網(wǎng)關(guān)則直接訪(fǎng)問(wèn)綠色的Consul進(jìn)行數(shù)據(jù)查詢(xún)。這樣改造之后有以下幾方面好處:
從支持網(wǎng)關(guān)的角度看,公共集群的負(fù)載原來(lái)是隨網(wǎng)關(guān)節(jié)點(diǎn)數(shù)線(xiàn)性增長(zhǎng),改造后變成隨服務(wù)個(gè)數(shù)線(xiàn)性增長(zhǎng),并且單個(gè)服務(wù)在同步周期內(nèi)只會(huì)執(zhí)行一次PreparedQuery查詢(xún),整體負(fù)載會(huì)降低;
圖中綠色Consul只供網(wǎng)關(guān)使用,其PreparedQuery執(zhí)行時(shí)所有數(shù)據(jù)都在本地,不涉及跨DC查詢(xún),因此復(fù)雜度降低,不受跨DC網(wǎng)絡(luò)影響,并且集群整體的讀寫(xiě)負(fù)載更可控,穩(wěn)定性更好;
當(dāng)公共集群故障時(shí),Gateway-Consul-Sync無(wú)法正常工作,但綠色的Consul仍然可以返回之前同步好的數(shù)據(jù),網(wǎng)關(guān)還可以繼續(xù)工作;
由于網(wǎng)關(guān)在改造前后查詢(xún)Consul的接口和數(shù)據(jù)格式是完全一致的,當(dāng)圖中綠色Consul集群故障時(shí),可以切回到公共Consul集群,作為一個(gè)備用方案。
關(guān)于Consul故障分析與優(yōu)化是怎么樣的問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。