午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

 江海博覽 2024-10-10
dbaplus社群
dbaplus社群
2024-09-24 11:25優(yōu)質(zhì)科技領(lǐng)域創(chuàng)作者


背景

眾所周知,Metrics指標(biāo)數(shù)據(jù)是可觀測重要的基石之一,在2021年底的時候,B站基于Prometheus+Thanos方案,完成了統(tǒng)一監(jiān)控平臺的落地。但隨著B站業(yè)務(wù)迅猛發(fā)展,指標(biāo)數(shù)據(jù)級也迎來了爆炸式增長,不僅給現(xiàn)有監(jiān)控系統(tǒng)的穩(wěn)定(可用性, 云上監(jiān)控數(shù)據(jù)質(zhì)量等)帶來沖擊,也無法滿足公司層面可觀測穩(wěn)定性建設(shè)(1-5-10)目標(biāo)。當(dāng)前架構(gòu)面臨的痛點總結(jié)如下:

穩(wěn)定性差:由于當(dāng)時告警計算是基于Prometheus本地計算的,target監(jiān)控實例在調(diào)度時,一個應(yīng)用的所有實例必須要被同一個Prometheus采集,每當(dāng)一些大應(yīng)用發(fā)布時,pod name 等一些元數(shù)據(jù)指標(biāo)label就會發(fā)生變化,重新構(gòu)建timeseries 索引時,會占用大量內(nèi)存,導(dǎo)致Prometheus oom。

用戶查詢體驗差:頻發(fā)的oom告警,經(jīng)常導(dǎo)致數(shù)據(jù)斷點,再加上Prometheus+Thanos 查詢性能性能有限,經(jīng)常出現(xiàn)面板查詢慢/超時,open api查詢失敗,誤告警等。

云上監(jiān)控數(shù)據(jù)質(zhì)量差:由于我們使用了許多不同廠商的云主機(jī),即使同一廠商也有多個賬號和不同地域,有自建可用區(qū)和云上可用區(qū)打通的,也有不打通的,有專線的,也有非專線的,網(wǎng)絡(luò)拓?fù)鋸?fù)雜,經(jīng)常出現(xiàn)因網(wǎng)絡(luò)不通導(dǎo)致無監(jiān)控數(shù)據(jù)。同時每個賬號下都是一套獨(dú)立的Prometheus采集,因此存在多個云監(jiān)控數(shù)據(jù)源,用戶很難選擇需要的云數(shù)據(jù)源。

2.0 架構(gòu)設(shè)計

設(shè)計思路

采集存儲分離:由于Prometheus是采集存儲一體的,導(dǎo)致我們在target監(jiān)控實例調(diào)度分發(fā)時,很難快速彈性擴(kuò)縮,同時也不太容易調(diào)度到不同的采集節(jié)點。所以要實現(xiàn)采集存儲分離的架構(gòu),target實例可以動態(tài)調(diào)度,采集器可以彈性擴(kuò)縮。

存算分離:由于Prometheus也是存算一體的,但隨著業(yè)務(wù)發(fā)展,指標(biāo)數(shù)據(jù)量級和計算需求往往不是線性關(guān)系的,比如在存儲資源增加不到一倍的情況,計算資源(查詢需求)需要兩倍以上,因此,如果按照存算一體的方式采購服務(wù)器資源,勢必造成一些資源浪費(fèi)。所以要實現(xiàn)存算分離的架構(gòu),在寫入,存儲和查詢都可以分別進(jìn)行彈性擴(kuò)縮。

時序數(shù)據(jù)庫選型:我們了解到VictoriaMetrics(以下簡稱VM)已經(jīng)被越來越多的公司做為時序數(shù)據(jù)庫,同時經(jīng)過我們的調(diào)研,在寫入&查詢性能,分布式架構(gòu),VM運(yùn)維效率都滿足我們的需求。(關(guān)于VM一些選型優(yōu)勢,本文不在討論,下文會有介紹對VM的一些查詢優(yōu)化)

單元化容災(zāi):由于我們是大多數(shù)場景(95%)基于pull模式的,每個target監(jiān)控實例需要按照一定的調(diào)度規(guī)則,分配到不同的采集器。但是之前沒有一個標(biāo)準(zhǔn),有的場景按照cluster維度調(diào)度,有的場景按照idc調(diào)度,有的按照instance name維度,導(dǎo)致指標(biāo)數(shù)據(jù)無法做到從采集->傳輸->存儲→查詢 整個鏈路單元內(nèi)閉環(huán)。因此,我們制定了一個新標(biāo)準(zhǔn),全部按照zone維度調(diào)度,可實現(xiàn)同zone下,全鏈路單元內(nèi)容災(zāi)。

基于以上核心的設(shè)計思路,設(shè)計了監(jiān)控2.0架構(gòu)。首先先看下整體的功能架構(gòu):

功能架構(gòu)概覽

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

通過上面的功能架構(gòu)圖可以看出,Metrics 指標(biāo)數(shù)據(jù),不僅應(yīng)用在大盤、告警等基礎(chǔ)場景,一些業(yè)務(wù)場景、發(fā)布平臺都會依賴指標(biāo)數(shù)據(jù),做一些流程決策。因此2.0架構(gòu)的落地,存在以下幾個方面的挑戰(zhàn):監(jiān)控系統(tǒng)自身穩(wěn)定性,數(shù)據(jù)可用性,查詢性能,故障爆炸半徑等。

整體架構(gòu)

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

如上是整體的技術(shù)架構(gòu),下面重點從數(shù)據(jù)來源,數(shù)據(jù)采集,數(shù)據(jù)存儲,和數(shù)據(jù)查詢方面介紹:

1)數(shù)據(jù)來源

從大的分類來看,監(jiān)控覆蓋場景主要分為paas層(應(yīng)用監(jiān)控,在/離線組件和一些中間件監(jiān)控)和iaas層(自建機(jī)房服務(wù)器/云主機(jī),容器監(jiān)控和網(wǎng)絡(luò)監(jiān)控)。由于之前是基于pull的方式發(fā)現(xiàn)target監(jiān)控實例,主要存在兩方面問題:

發(fā)現(xiàn)target監(jiān)控實例存在延遲:因為pull的方式有一定的同步周期,比如30s, 當(dāng)一個新的監(jiān)控實例出現(xiàn),需要30s才能發(fā)現(xiàn),因此看監(jiān)控指標(biāo)要比預(yù)期晚30s。

運(yùn)維成本較大:當(dāng)pull 業(yè)務(wù)方提供的接口有問題時,業(yè)務(wù)方一般無法第一時間感知問題,需要我們?nèi)ブ鲃訁f(xié)同處理。

針對上面問題,我們將pull方式改成push方式,讓業(yè)務(wù)方主動push 需要被監(jiān)控的target實例。這樣可以實時的push,解決因pull 方式存在的30s延遲,同時,為了讓各個業(yè)務(wù)方更方面的管理target實例,在指標(biāo)平臺提供按集成任務(wù)申請監(jiān)控接入,并且實時顯示target實例采集狀態(tài),采集數(shù)量和采集耗時,進(jìn)一步提升監(jiān)控接入狀態(tài)的可見性。

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

2)數(shù)據(jù)采集

調(diào)度層

調(diào)度層主要負(fù)責(zé)采集job配置的生成和target監(jiān)控實例的分發(fā)。為了實現(xiàn)單元內(nèi)鏈路全閉環(huán),調(diào)度層分為一級調(diào)度(所有采集配置)和二級調(diào)度(本機(jī)房內(nèi)采集配置)

一級調(diào)度(Master)

從數(shù)據(jù)庫中,拿到全量的采集job配置和target監(jiān)控實例。根據(jù)采集調(diào)度配置(zone維度調(diào)度),在內(nèi)存中構(gòu)建各個二級調(diào)度所需的采集配置。

為了保證Master數(shù)據(jù)高可用,當(dāng)訪問依賴數(shù)據(jù)庫異常,內(nèi)存快照數(shù)據(jù)不更新。同時在其他異常的場景下,我們也做了一些內(nèi)存快照數(shù)據(jù)保護(hù)策略:如,當(dāng)一次性刪除>5k targets的job, 一次更新diff 量減少> 5k targets時,平臺會攔截保護(hù),做doubble check, 防止用戶誤操作。

目前通過一些手段,異步,內(nèi)存cache, 多協(xié)程等方式,Master 全量調(diào)度時間從50s降低到10s內(nèi)。

二級調(diào)度(Contractor)

根據(jù)采集集群名稱+版本號定時去Master拿本機(jī)房的采集配置。設(shè)計多套二級調(diào)度,為了防止故障爆炸半徑。

拿到本機(jī)房采集配置后,根據(jù)Collector 心跳,拿到當(dāng)前health 采集節(jié)點(Collector),根據(jù)實例和容量維度進(jìn)行調(diào)度,將采集配置分給對應(yīng)的采集節(jié)點。

當(dāng)Master采集配置或采集節(jié)點有更新時,會觸發(fā)新一輪的調(diào)度。為了保證target不隨機(jī)調(diào)度,在實現(xiàn)調(diào)度算法時,已經(jīng)分配給某個采集節(jié)點的配置,下次調(diào)度時,還是會優(yōu)先拿到該配置。

當(dāng)Contractor應(yīng)用發(fā)版,重啟等場景時,怎么保證指標(biāo)數(shù)據(jù)無斷點或抖動呢?

在Contractor 重啟時, 內(nèi)存會維護(hù)一個全局state變量,等待Collector全部上報targets完成后,才開始進(jìn)行targets調(diào)度,當(dāng)調(diào)度完成后,state設(shè)置為ready,Contractor接口才會對Collector提供訪問。

采集器

采集器Collector 是基于vmagent封裝了一層。主要有兩個功能,一個是定時上報心跳給Contractor, 二是拿到相關(guān)采集配置,call reload api,觸發(fā)vmagent開始采集。

當(dāng)我們灰度了一些量后,發(fā)現(xiàn)vmagent占用的內(nèi)存較高,通過heap pprof發(fā)現(xiàn), 在每次pull 抓取上報的指標(biāo)消耗內(nèi)存較多,后面開啟流式采集 promscrape.streamParse=true 后,內(nèi)存降低20%左右。

vmagent 自身會有隨機(jī)(采集間隔時間)平滑load機(jī)制。比如我們采集間隔配置了30s,當(dāng)vmagent拿到配置時,一個target最慢要30s才會有指標(biāo)數(shù)據(jù)。所以當(dāng)Collector擴(kuò)/縮容時,target會漂移到其他Collector采集時,會出現(xiàn)指標(biāo)斷點。因此我們設(shè)計了一個機(jī)制,Collector 監(jiān)聽到退出信號后,不立即退出,只是停止上報心跳,同時繼續(xù)采集一個周期后,才會退出,這樣可以保證指標(biāo)無斷點。

3)數(shù)據(jù)存儲

我們通過 vmstorage 進(jìn)行指標(biāo)的存儲。由于vmstorage涉及到存儲細(xì)節(jié)比較多,這里主要介紹索引結(jié)構(gòu)的存儲。首先認(rèn)識下 vmstorage 當(dāng)中幾個核心類型。

MetricName 存儲 label kv 的變量,寫入的時候會由 vminsert 將實際指標(biāo)數(shù)據(jù)當(dāng)中的 label 序列化成 MetricName 進(jìn)行發(fā)起寫入請求,同時在 vmselect 查詢的時候,拿到的結(jié)果集當(dāng)中的 label 信息也是由 MetricName 進(jìn)行序列化存儲,在實際的存儲當(dāng)中,MetricName 更類似 tv 的內(nèi)容元信息。MetricName 在 vmstorage 落盤的序列化結(jié)構(gòu)如下:

4 byte account id | 4 byte projectid | metricname(__name__) | 1 | tag1 k | 1 | tag1 v | 1 | .... | tagn k | 1 | tagn v | 1 | 2 |

MetricId 納秒時間戳,一簇 ts 的唯一鍵,在一簇 ts 第一次在 vmstorage 入庫的時候生成。8 byte。

TSID 也是一簇 ts 的唯一鍵。由指標(biāo)的租戶,指標(biāo)名id,job instance labelvalue id 和 MetricId 組成。TSID 在 vmstorage 落盤的序列化結(jié)構(gòu)如下

4 byte accountid | 4 byte projectid | 8 byte metricname(__name__) id | 4 byte job id | 4 byte instance id | 8 byte metricid

TSID 與 MetricId 都是一簇 ts 的唯一鍵,區(qū)別在與 MetricId 只是 8byte 的時間戳,而 TSID 的序列化信息在包含 MetricId 之外還包含其他非常多的標(biāo)識信息,當(dāng) TSID 通過字典序排序之后,同租戶相同名字的指標(biāo)將會在分布上連續(xù)在一起,綜合指標(biāo)查詢的特點,查詢的結(jié)果總是集中在同一個租戶下的同名指標(biāo),并且都會有相似的 label 條件。所以 TSID 實際上是 vmstorage 當(dāng)中 data 目錄下的 key, 在索引部分的查詢核心是根據(jù)查詢條件得到最后的 TSID。

在上述三個類型下,就能形成如下幾個抽象索引結(jié)構(gòu):

MetricName -> TSID:0 | MetricName | TSID

MetricId -> MetricName:3 | 4 byte accountid | 4 byte projectid | MetricId | MetricName

MetricId -> TSID:2 | 4 byte accountid | 4 byte projectid | MetricId | TSID

同時核心的倒排索引結(jié)構(gòu)抽象如下:

1 | 4 byte accountid | 4 byte projectid | metricname(__name__) | 1 | MetricId

1 | 4 byte accountid | 4 byte projectid | tag k | 1 | tag v | 1 | MetricId

結(jié)合倒排索引和索引3就可以得到這么一條流程:

根據(jù)查詢攜帶的租戶信息和指標(biāo)名稱與 label 信息,就可以組成所需要的倒排索引前綴,在字典序排序的索引下,就可以通過二分查找查得所需要的 MetricId 集合。將多個條件從倒排索引中查詢到的 MetricId 求交集,就能得到符合 label 查詢條件的 MetricId 集合,就能通過索引3組裝成響應(yīng)的索引前綴查詢到最后符合條件的 TSID。在 vmstorage 中執(zhí)行查詢時,索引部分的核心目標(biāo)就是根據(jù)條件求得最后所需要的 TSID 集合。

結(jié)合以上的抽象索引,vm 通過基于前綴的壓縮進(jìn)行存儲來達(dá)到減少磁盤占用的目的。因此相比于Prometheus,磁盤存儲成本大概有40%的降幅。

在正常的 vmstorage 使用中,vmstroage 提供了十分優(yōu)秀的性能,以某一個集群為例子,單臺 48c 256g 的 vm stroage 日常足夠支撐每秒 40w 點的寫入, 2w qps 的查詢。在這一過程中,通過調(diào)整應(yīng)用的 gogc 來平衡 cpu 占比來優(yōu)化 vm 的資源使用。默認(rèn)情況下,vmstorage 的 gogc 的值給到了 30,可能會存在比較頻繁的 gc,在查詢寫入飽和的情況下,如果與指標(biāo)點的 merge 發(fā)生在一起,那么查詢將會在短時間內(nèi)存在比較大的握手延遲而導(dǎo)致失敗。在內(nèi)存夠用的情況下,調(diào)大 vmstorage 的 gogc ,可以以一定的內(nèi)存為代價換來更穩(wěn)定 vmstorage 運(yùn)行。

4)數(shù)據(jù)查詢

promql 自動替換增強(qiáng)

在我們通過 grafana 訪問 victoriametrics 進(jìn)行日常指標(biāo)查詢的過程中,經(jīng)常會遇到某些 panel 返回數(shù)據(jù)過慢或者是直接返回了查詢覆蓋的數(shù)據(jù)量太大而直接失敗。這部分的 panel 常常在盡可能優(yōu)化了查詢條件后,仍舊無法通過正常手段查詢。

在這里以一個 promql 語句作為例子:

histogram_quantile(0.99, sum(rate(grpc_server_requests_duration_ms_bucket{app='$app',env=~'$env'}[2m])) by (le, method))

這條語句用來查詢某個env 下的某個 app 下的所有 grpc 接口調(diào)用 p99 耗時。在這條語句中,假設(shè)一個存在 app A,其下包含 2000 個實例,20 個接口, 50 個調(diào)用方,5 個 le 桶,以 30s 為間隔進(jìn)行一次上報,單個時間上的點符合條件的點就有 1000 w,這條語句還包括一個 2m 的時間窗口統(tǒng)計,那么本次查詢總體上符合點的數(shù)量就為 4000w。這樣的查詢語句,即使能夠避開查詢的容量限制,也會影響整體的查詢數(shù)據(jù)源的穩(wěn)定性。在常規(guī)的 grafana 使用習(xí)慣上,我們將會對這條語句的整體進(jìn)行一次預(yù)聚合,但是在這個語句的場景下,b 站處于活躍狀態(tài)的 app 數(shù)量為大幾千,而這條語句中只有兩位數(shù)的應(yīng)用存在查詢性能問題,如果直接對這條語句進(jìn)行預(yù)聚合的話,就會存在比較大的資源浪費(fèi)。同時,在這條語句中,如果將第一個參數(shù)分別修改為 0.5 和 0.9,那么可以直接查詢得到當(dāng)相應(yīng)的 p50 和 p90 指標(biāo),對著這條的優(yōu)化并不能幫助到 p50 和 p90的查詢,單次聚合的產(chǎn)出也不理想。因此,在這思考一個方式,能不能通過僅聚合存在性能問題的部分來解決包含這個語句的 panel 的查詢問題。

首先,從 vmselect 執(zhí)行這個語句的流程入手,這條語句在 vmselect 執(zhí)行的過程中,將會被解析成如下的一顆執(zhí)行樹:

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

首先來描述一下該語句的執(zhí)行流程,在解析得到這棵執(zhí)行樹之后,將會通過深度優(yōu)先的方式依次來執(zhí)行各個節(jié)點。具體流程如下

指標(biāo)數(shù)據(jù)查詢:在葉子節(jié)點處,根據(jù)指標(biāo)名稱和 label 的篩選條件從 vmstorage 當(dāng)中進(jìn)行分布式查詢得到需要的原始指標(biāo)數(shù)據(jù)

rate 函數(shù)執(zhí)行:從第1步中查詢到的指標(biāo)數(shù)據(jù)根據(jù) label 相同的指標(biāo)分為一個指標(biāo)簇,對相同的指標(biāo)簇內(nèi)的2m時間窗口中的最后一個點和第一個點相減,并除以他們之間的距離,得到 rate 函數(shù)的結(jié)果

sum 聚合函數(shù)執(zhí)行:最后根據(jù) sum 的group by 分區(qū)鍵將第2步中的數(shù)據(jù)根據(jù) le + method 的分區(qū)鍵進(jìn)行分區(qū),之后對分到同一個區(qū)內(nèi)的指標(biāo)進(jìn)行 sum 操作

histogram_quantile 函數(shù)執(zhí)行:最后遍歷第3步中得到的每個分區(qū)的數(shù)據(jù)進(jìn)行 p99 的計算,得到最后需要的耗時分布的結(jié)果

我們在這里繼續(xù)用前文提到的 app 舉例子,其下包含 2000 個實例,20 個接口, 50 個調(diào)用方,5 個 le 桶,以 30s 為間隔進(jìn)行一次上報,在第一步中查詢需要得到的指標(biāo)點數(shù)量就達(dá)到了 4000w。該 4000w 的數(shù)據(jù)在第二步會在時間 rate 函數(shù)的時間窗口計算當(dāng)中被減少到 1000w。到第三步的時候,這 1000w 的數(shù)據(jù)會根據(jù) le + method 的分區(qū)鍵被分成 100 個分區(qū),最后輸出的結(jié)果相應(yīng)的被縮減到 100 個。第 4 步的結(jié)果和輸入的數(shù)量相同。因此我們可以看到,在整顆樹的執(zhí)行過程中,第 1 步往往會成為真正執(zhí)行過程中的性能瓶頸,第 2 步雖然在輸出上大大減少了數(shù)量,但是仍會存在巨大的指標(biāo)輸出,并沒有根本解決性能問題。而如果能針對第 3 步的輸出結(jié)果去進(jìn)行預(yù)聚合并將預(yù)聚合的結(jié)果用到查詢中,該查詢將會直接變成一個查詢 20 個指標(biāo)點的簡單查詢。同時,再考慮一點,無論是 p50 p90 p99 的計算,到第 3 步的指標(biāo)輸出為止,底層的計算邏輯是完全一致的,那么如果我們將第 3 步的結(jié)果進(jìn)行預(yù)聚合并持久化下來,完全可以復(fù)用到任何參數(shù)的 histogram_quantile 計算分位耗時計算當(dāng)中,一次預(yù)聚合的性價比達(dá)到了相當(dāng)大的價值。

那么,我們假設(shè)已經(jīng)通過實時或者定時的方式將 app A 的這條 promql 進(jìn)行預(yù)聚合并將上述這個表達(dá)式的結(jié)果轉(zhuǎn)化為了指標(biāo) test_metric_app_A。

sum(rate(grpc_server_requests_duration_ms_bucket{app='A'}[2m])) by (le, method)

那么我們實際執(zhí)行查詢 app A 的 p99 分位耗時的時候,希望真正執(zhí)行的查詢語句如下

histogram_quantile(0.99, test_metric_app_A)

但是,在 grafana 的 panel 配置當(dāng)中,一個 panel 對應(yīng)原始查詢語句和多個這樣的優(yōu)化查詢的話,實際查詢時體驗將會變得非常奇怪,甚至對整體體驗會達(dá)到負(fù)優(yōu)化的效果,那么回到一開始原始語句的查詢執(zhí)行樹:

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

如果我們把黃色的部分的子樹,直接替換成 test_metric_app_A 的查詢樹就可以無縫完成查詢的優(yōu)化了。那么我們需要在 vmselect 上面在查詢之前就構(gòu)建如下的一個映射關(guān)系:

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

在正式執(zhí)行查詢的時候,我們在執(zhí)行樹解析完成的時候,從執(zhí)行樹的根節(jié)點就可以開始依次檢查收否存在完全滿足映射條件的 key 的執(zhí)行樹的子樹,只要符合,就可以將原始查詢語句的子樹替換成映射的 value 樹。

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

這樣看似是一個非常合適的方案,但是實際的查詢過程中的替換還是存在問題。首先我們的原始語句當(dāng)中,用到了 env 作為查詢條件,但是我們預(yù)聚合的結(jié)果采用的分區(qū)鍵是 le + method,在我們預(yù)聚合的結(jié)果指標(biāo)當(dāng)中并沒有 env,直接那這里的預(yù)聚合指標(biāo)進(jìn)行替換,會直接丟失掉查詢 env 的能力,因此此處的替換是不符合實際查詢的要求的。那么我們的預(yù)聚合語句應(yīng)該修改為:

sum(rate(grpc_server_requests_duration_ms_bucket{app='A'}[2m])) by (le, method, code)

這樣的預(yù)聚合,當(dāng)我們想重新將查詢語句的執(zhí)行樹進(jìn)行替換的時候,顯然就不能單純的將替換關(guān)系直接套在查詢的執(zhí)行樹上,反之,在聚合函數(shù)外層應(yīng)該加上一層新的聚合函數(shù)節(jié)點。

重新思考當(dāng)前的場景,當(dāng)我們存在如上的預(yù)聚合之后,我們需要的是在預(yù)聚合的基礎(chǔ)上自動增加一層聚合函數(shù),就能夠達(dá)到我們需要的目的。

我們新的映射關(guān)系變成下面的樣子:

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

當(dāng)我們得到這個映射關(guān)系之后,我們重新開始從根節(jié)點開始進(jìn)行遍歷的時候,會發(fā)現(xiàn)在 sum 這一層的分區(qū)鍵上存在不匹配的關(guān)系,但是,查詢的分區(qū)鍵集合(le, method)是映射的分區(qū)鍵(le, method, env)的一部分,直接使用也對語句的執(zhí)行沒有任何影響。在這個基礎(chǔ)上,可以直接在替換子樹的時候把新的子樹掛在這一層的聚合函數(shù)下面。替換結(jié)果如下:

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

達(dá)成如上的執(zhí)行樹替換之后,就可以在語義無損的前提下完成 promql 的自動替換。同時,在這個轉(zhuǎn)換的支持下,對于預(yù)聚合的指標(biāo)我們可以在不影響預(yù)聚合輸出數(shù)量的前提下盡可能的增加一些聚合的維度,這樣預(yù)聚合的結(jié)果可以作為子查詢用在更多的場景下。而這里的查詢轉(zhuǎn)換,對于用戶層面在使用 grafana 進(jìn)行查詢的時候,是完全無感知的。在查詢中對于使用了預(yù)聚合的 app,將會自動對解析后的執(zhí)行樹進(jìn)行替換,而對于本身就不需要進(jìn)行優(yōu)化的查詢條件的時候,將會直接根據(jù)原始查詢語句去進(jìn)行查詢,在盡可能節(jié)省預(yù)聚合所消耗的資源的前提下,對存在性能瓶頸的查詢語句進(jìn)行了優(yōu)化。再者,即使用戶修改了查詢語句,只要語句中包含預(yù)聚合了的子查詢,都能夠起到優(yōu)化的效果,達(dá)到了預(yù)聚合資源消耗的最大回報。

ps:(avg 聚合函數(shù)會存在一定的語義損耗,但是在大部分場景下的誤差可以忽略不計)

基于 promql 的flink 指標(biāo)預(yù)聚合

對于前文中提到的預(yù)聚合,B站原本主要的預(yù)聚合都是通過定時的批量查詢來進(jìn)行預(yù)聚合。這樣的查詢聚合對存儲側(cè) vmstroage 和查詢側(cè) vmselect 都有比較大的壓力,從而可能會影響到正常的指標(biāo)查詢需求。在預(yù)聚合的角度來看,執(zhí)行原生的 promql 聚合存在一定的內(nèi)存浪費(fèi),以上文提到的一條 promql 為例子:

sum(rate(grpc_server_requests_duration_ms_bucket{app='A'}[2m])) by (le, method, code)

在這個預(yù)聚合語句當(dāng)中,vmselect 將會從 vmstorage 當(dāng)中全量撈出符合
grpc_server_requests_duration_ms_bucket{app='A'} 的指標(biāo)并在內(nèi)存當(dāng)中保留全部的標(biāo)簽,直到全部的分布式查詢完成后,進(jìn)行計算,在最后的 sum 部分才會根據(jù) group by 的 le,method,code 進(jìn)行 label 收斂再得到最后的結(jié)果。這個過程中,vmselect 的內(nèi)存常常在執(zhí)行 sum 之前,就遇到了瓶頸,尤其是這里的 rate 函數(shù),設(shè)置了 2m 的時間窗口計算,這部分進(jìn)一步擴(kuò)大了 vmselect 的壓力。同時,這樣的查詢往往會成為慢查詢,這部分?jǐn)?shù)據(jù)的聚合也會出現(xiàn)明顯的延遲。同理,定時的大批量查詢也會增加 stroage 的壓力。

為了避免查詢請求對 vm 集群的壓力,同時盡可能保證預(yù)聚合的實效性,我們嘗試采用 flink 來進(jìn)行基于 promql 的指標(biāo)預(yù)聚合。

前文提到了 promql 在 vmselect 當(dāng)中的執(zhí)行方式。類似 sum(rate(
grpc_server_requests_duration_ms_bucket{app='A'}[2m])) by (le, method, code) 這樣的表達(dá)式,將會在 vmselect 當(dāng)中解析成如下的執(zhí)行樹。

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

我們在 flink 預(yù)聚合的配置側(cè)就可以類似的對這個 promql 進(jìn)行相應(yīng)的解析,解析成如上的執(zhí)行樹。在 flink 那一側(cè)就不需要關(guān)心原始 promql 語句的構(gòu)成,只需要得到這顆執(zhí)行樹的 json 即可,同時在配置側(cè)需要關(guān)心的是在解析期間得到的其中的時間窗口信息作為元數(shù)據(jù)保存在一起,flink 的 job 將會各自配置的時間窗口配置從配置中選取符合條件的預(yù)聚合配置執(zhí)行。

對于這顆執(zhí)行樹,我們站在 flink 的角度去進(jìn)行設(shè)計執(zhí)行流程。

數(shù)據(jù)過濾階段

這個階段類比我們進(jìn)行 vm 查詢指標(biāo),和實際vm查詢執(zhí)行一樣,用到的都是執(zhí)行的葉子節(jié)點,在配置時我們就根據(jù)每棵樹的葉子節(jié)點的指標(biāo)名稱 label 構(gòu)建 key,初步篩選實時 kafka 流中的指標(biāo)數(shù)據(jù),并在初步篩選完畢之后,進(jìn)一步根據(jù)剩余的指標(biāo) label 過濾條件進(jìn)行過濾,在第一階段只需要 check 執(zhí)行樹的葉子結(jié)點就可以從實時指標(biāo)流中得到所有符合 promql 查詢的指標(biāo)原始數(shù)據(jù)

數(shù)據(jù)分區(qū)階段

sum(rate(
grpc_server_requests_duration_ms_bucket{app='A'}[2m])) by (le, method, code) 的時候,我們常常在執(zhí)行這個 2m 的時間窗口聚合的時候需要在內(nèi)存中持有 2m 內(nèi)的全量原始數(shù)據(jù)而存在壓力,通常情況下,一個指標(biāo)點的label集合就將達(dá)到1-2k,對于預(yù)聚合是非常大的內(nèi)存壓力。優(yōu)化執(zhí)行聚合過程中,內(nèi)存中時間窗口中持有的指標(biāo)點的內(nèi)存是最核心的優(yōu)化方向。

在我們通過 flink 進(jìn)行分區(qū)時間窗口聚合的時候,我們正好需要依賴的就是執(zhí)行樹中的 sum 節(jié)點, sum 的 group by 鍵正是 flink 當(dāng)中需要的分區(qū)鍵,我們完全可以復(fù)用這里的邏輯,將指標(biāo)中對應(yīng)的label 提取作為分區(qū)鍵,將數(shù)據(jù)點作為 value 進(jìn)行緩存在 flink 當(dāng)中的時間窗口當(dāng)中進(jìn)行聚合。但是,此處就存在了與 vmselect 執(zhí)行語句的一樣的內(nèi)存瓶頸。我們回顧這條語句,
grpc_server_requests_duration_ms_bucket 這個指標(biāo),其實除了分區(qū)鍵當(dāng)中涉及的le method code之外的label,在執(zhí)行過程中實際都不是我們需要考慮的label,我們完全可以在此處對剩余的label全部丟棄掉,只保留一個 label 集合統(tǒng)一的 uuid 用來在 rate 的時候能夠 hash 到一個分桶里進(jìn)行聚合即可。同時對于分區(qū)鍵的組合,我們甚至可以直接丟棄掉label key的部分,因為我們的分區(qū)鍵全部都是按照順序排列的,我們的分區(qū)鍵只需要保留這兩個label 的具體value, 而對 label 的 key 直接進(jìn)行丟棄,在執(zhí)行的時候按照 promql 聲明的順序順序取即可,具體為promql id 和 label value,而我們的具體指標(biāo)數(shù)據(jù)也只需要保留點的label 集合uuid 和時間數(shù)值即可。此處的優(yōu)化可以直接解決聚合當(dāng)中的內(nèi)存壓力。

以這條語句所涉及的一個指標(biāo)為例子,一個指標(biāo)所生成的分區(qū)鍵為'promqlid(四字節(jié) 需要在時間窗口中定位具體的執(zhí)行樹) + le value + method value + code value',需要緩存在時間窗口的指標(biāo)點的內(nèi)容為 'label set uuid (四字節(jié)) + time(八字節(jié)) + value(八字節(jié))'。

最后一個點在內(nèi)存中固定的內(nèi)存占用為 20 字節(jié),而相同分區(qū)鍵下的點之間共享的分區(qū)鍵大小也只有4字節(jié) + 必須要的value串,達(dá)到了最小的內(nèi)存消耗。

時間窗口執(zhí)行階段

當(dāng)我們完成數(shù)據(jù)的篩選和分區(qū)之后進(jìn)入時間窗口的算子階段,我們只需要在這個階段對這顆執(zhí)行樹進(jìn)行一次深度優(yōu)先的調(diào)用即可。

這個階段中,由于 flink 的時間窗口限制,在具體的函數(shù)實現(xiàn)上會和 vm 存在一些細(xì)微的區(qū)別。

最明顯的例子為 increase 函數(shù),在 vm 對于該參數(shù)的實現(xiàn)中,是通過當(dāng)前時間窗口的最后一個點和前一個時間窗口的最后一個點進(jìn)行相減得到的結(jié)果,在flink中,由于已經(jīng)定死了緩存2分鐘的實時數(shù)據(jù),這個實現(xiàn)將會造成額外的內(nèi)存消耗,同時,在別的函數(shù)的實現(xiàn)上也會需要考慮額外的存儲所帶來的實現(xiàn)成本,因此我們選擇了 prometheus 的 increace 實現(xiàn)來達(dá)到我們的目的。在 prometheus 的 increase 實現(xiàn)中,通過同一窗口內(nèi)最后一個點和最后一個點在時間坐標(biāo)軸的一次函數(shù)的k值,來根據(jù)時間窗口的長度來預(yù)估這個期間的增量。

類似如此的場景,由于 vm 相比 prometheus 在很多涉及時間窗口的函數(shù)中引入了前一個時間窗口的數(shù)據(jù)來參與計算讓數(shù)據(jù)更加精準(zhǔn),這樣的優(yōu)化在 flink 將會導(dǎo)致額外的資源,所有類似的函數(shù) flink 將會參考prometheus 的設(shè)計來實現(xiàn)。

同時,由于我們在上一個階段中對絕大部分的label進(jìn)行了丟棄,在這里類似 topk 這樣需要前后保留原始label 信息的函數(shù)也存在不支持的場景。

數(shù)據(jù)上報階段

在前一個階段得到的預(yù)聚合指標(biāo)數(shù)據(jù)就是我們所需要的結(jié)果,可以直接 flink 的 sink 當(dāng)中通過 remotewrite 協(xié)議寫到 vminsert上。

經(jīng)過以上的設(shè)計,我們基于 promql 的 flink 預(yù)聚合可以快速根據(jù)實際存在查詢瓶頸的 promql 進(jìn)行快速配置生效,在實際場景下 100c 400g的 flink job 配置即可滿足每2分鐘3億個指標(biāo)點的時間窗口緩存和計算需求,相比定時預(yù)聚合,大大減少了預(yù)聚合的資源需求,這里的實現(xiàn)也滿足我們在透明查詢當(dāng)中對資源利用最大化的期望。

查詢優(yōu)化收益

查詢的自動優(yōu)化加上 flink 的專項預(yù)聚合,可以針對實際情況下的查詢情況,對特定的 promql 的集中進(jìn)行治理優(yōu)化,在非常小的資源消耗下,日均使 20s 以上的慢查詢減少了百分之90,查詢數(shù)據(jù)源資源減少百分之50,以非常小的代價帶來非常高的收益。

5)數(shù)據(jù)可視化

目前我們主要使用grafana構(gòu)建監(jiān)控大盤,由于歷史原因,grafana使用的一直是比較老的版本(v6.7.x),但是新版grafana有非常多的功能迭代和性能優(yōu)化,因此我們決定升級grafana版本到v9.2.x。但版本升級存在如下挑戰(zhàn):

v6.7.x升級到v9.2.x存在多個break change(除了官方描述的一些break change,同時也包括一些自定義數(shù)據(jù)源/pannel 插件存在break change)。

老版本grafana部署成本高:之前是物理機(jī)部署,且部署方式相對黑盒,同時我們增加nginx+grafana auth服務(wù)做auth proxy 認(rèn)證,因此部署運(yùn)維成本進(jìn)一步提高。

面對版本升級存在的挑戰(zhàn)和問題,我們做了以下事項:

在升級前,我們做了大量的測試和驗證,通過一些腳本fix因為break change造成的一些數(shù)據(jù)不兼容,通過opensearch數(shù)據(jù)源插件替換新版本再支持的es插件。

在部署方式上,首先用git倉庫管理整個grafana部署編譯腳本,讓每次版本變更不再黑盒,同時將nginx+grafana auth+grafana構(gòu)建成all-in-one鏡像, 實現(xiàn)容器化部署,進(jìn)一步降低部署成本。

新版本的grafana, 如果prometheus數(shù)據(jù)源使用版本>v2.37.x,變量的獲取方式默認(rèn)從series api改成 label values api, 查詢性能會提升10倍左右(2s->200ms)。

升級完成后,面板加載性能和整體用戶查詢體驗大幅提升。

6)整體收益

從Prometheus全部切到VM架構(gòu)后, p90查詢耗時降低10倍以上

目前支持了170w+ 采集對象,全部按照zone維度調(diào)度, 采集, 實現(xiàn)單元內(nèi)容災(zāi)

僅增加磁盤資源情況下, 應(yīng)用監(jiān)控全量采集間隔從60s調(diào)整到30s,實現(xiàn)1-5-10中的1分鐘發(fā)現(xiàn)

指標(biāo)斷流、oom告警等異常,降低90%以上

目前寫入吞吐44M/s,查詢吞吐48k/s, 通過查詢優(yōu)化, 查詢再加速,p90查詢耗時降低到ms級(300ms)

云監(jiān)控方案

當(dāng)前云上監(jiān)控存在以下痛點:

因有各個云廠商,或者同一個云廠商,因地域不同造成網(wǎng)絡(luò)環(huán)境不通,導(dǎo)致采集失敗,出現(xiàn)無監(jiān)控數(shù)據(jù)的情況。

每個賬號下都是一套獨(dú)立的Prometheus采集,因此存在多個云監(jiān)控數(shù)據(jù)源,用戶很難選擇需要的云數(shù)據(jù)源。

云監(jiān)控方案整體和idc采集方案類似,為了方便用戶統(tǒng)一數(shù)據(jù)源查詢和統(tǒng)一告警計算,我們將Prometheus采集的云上數(shù)據(jù),通過remote write回源到idc存儲集群。架構(gòu)如下:

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

比較idc采集方案,云上監(jiān)控有以下幾點不同:

  • Contractor支持從公網(wǎng)pull本zone所需的采集配置。

  • 為什么使用Prometheus而不是vmagent采集?

改造成本低:歷史上云上數(shù)據(jù),都是Prometheus采集的,同時當(dāng)前Collector實現(xiàn)層面,采集器是可插拔的,使用Prometheus采集覆蓋成本低。

Prometheus本地數(shù)據(jù):當(dāng)前云上采集數(shù)據(jù),一份通過remote write 回源到idc存儲集群,同時本地會存1d 數(shù)據(jù),當(dāng)remote write 出現(xiàn)故障的時候,可查詢本地數(shù)據(jù)。

  • 為什么要引入vm-auth 組件?

因為云上數(shù)據(jù)回源到idc, 走的是公網(wǎng),直接回源remote write 到vm-insert,會存在安全風(fēng)險,所以我們加了vm-auth,對回源流量做租戶認(rèn)證和流量調(diào)度。vm-auth配置如下:

監(jiān)控架構(gòu)淘汰Prometheus后,P90查詢性能提升了10倍!

收益

云上監(jiān)控全部按照zone調(diào)度后,云上數(shù)據(jù)質(zhì)量大幅提升,云上監(jiān)控?zé)o數(shù)據(jù)等oncall 降低90%以上。

統(tǒng)一數(shù)據(jù)源查詢,20+ 云上數(shù)據(jù)源收斂到1個。

未來規(guī)劃

支持更長時間Metrics指標(biāo)數(shù)據(jù)存儲:當(dāng)前數(shù)據(jù)默認(rèn)存儲時間是15d, 希望給業(yè)務(wù)方提供更長時間的數(shù)據(jù), 用作分析,復(fù)盤,資源預(yù)估等場景。

支持更細(xì)粒度的指標(biāo)埋點:目前應(yīng)用監(jiān)控默認(rèn)是30s一個點, 少部分場景支持了5s,希望后面提供更細(xì)粒度的埋點,覆蓋更多的場景,為業(yè)務(wù)可觀測、排障助力。

自監(jiān)控能力增強(qiáng):目前有一套自監(jiān)控鏈路,跟運(yùn)維平臺聯(lián)動,提供了基礎(chǔ)的監(jiān)控自身系統(tǒng)的監(jiān)控和告警,希望后面可以覆蓋全所有的自監(jiān)控場景&運(yùn)維SOP。

指標(biāo)平臺迭代:目前指標(biāo)平臺主要提供了監(jiān)控接入,采集配置管理和監(jiān)控對象查詢等基礎(chǔ)能力,未來規(guī)劃增加寫入/查詢封禁、白名單等能力,同時希望后面可以借助大模型的能力,通過指標(biāo)元信息增強(qiáng),實現(xiàn)text2promql(自然語言翻譯成PromQL,以及自動生成PromQL注釋)

作者丨通用工程

來源丨公眾號:嗶哩嗶哩技術(shù)(ID:bilibili-TC)

dbaplus社群歡迎廣大技術(shù)人員投稿,投稿郵箱:editor@dbaplus.cn

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多