1. 程式人生 > >基於Prometheus和Grafana打造業務監控看板

基於Prometheus和Grafana打造業務監控看板

## 前言 業務監控對許許多多的場景都是十分有意義,業務監控看板可以讓我們比較直觀的看到當前業務的實時情況,然後運營人員可以根據這些情況及時對業務進行調整操作,避免業務出現大問題。 老黃曾經遇到過一次比較尷尬的“事故”。 其中一條業務線,服務著的其中一個商家,把大部分流量切到另外一個地方去了,而我們的運營人員在當天卻是完全不知情,第二天看了昨天的統計報表之後才發現這個商家的量少了很多,才能跟進協調處理。 ps: 當時實時報表比較欠缺,都是第二天凌晨生成昨天的資料報表,也沒有告警機制。 後面就弄了個大螢幕做了業務監控的實時看板,看一眼就知道有什麼風吹草動了。 先來看一下最終的效果圖。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171124537-837099042.png) 這個圖裡面主要包含了下面幾個內容。 1. 總的訂單數量 2. 退單的數量 3. 建立訂單的頻率 4. 不同渠道的訂單量 5. 不同渠道的退單量 下面就介紹一下如何實現這樣的業務監控。 ## 搭建基礎設施 這裡涉及的基礎設施就有兩個,一個是prometheus,另一個是grafana。 先啟動prometheus,這裡直接用docker啟動。 ```ps $base = Split-Path -Parent $MyInvocation.MyCommand.Definition $prometheusyml = Join-Path $base prometheus.yml $fileconfig = Join-Path $base "config" write-host $prometheusyml write-host $fileconfig docker run ` --name prom ` -p 9090:9090 ` -v ${prometheusyml}:/etc/prometheus/prometheus.yml ` -v ${fileconfig}:/etc/prometheus/fileconfig ` prom/prometheus:v2.20.1 ``` 下面是prometheus.yml ```yml global: scrape_interval: 15s evaluation_interval: 15s alerting: alertmanagers: - static_configs: - targets: # - alertmanager:9093 rule_files: scrape_configs: - job_name: 'file_ds' file_sd_configs: - refresh_interval: 10s files: - ./fileconfig/*.yml ``` > 這裡用了基於檔案的發現機制,沒有用靜態的。更多其他方式,參見 https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config 這個時候prometheus已經是執行起來了。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171139267-1815434911.png) 再來就是grafana了,啟動這個更加簡單。 ```ps docker run -d --name grafana -p 3000:3000 grafana/grafana:7.1.3 ``` 執行完,訪問 localhost:3000 就可以看到登入介面了。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171149279-262448056.png) ## 確定業務指標(metrics) 確定指標可以說是整個業務監控中最最最最最為主要的一個環節了,只有明確了我們要監控什麼,我們才可以在業務上去進行埋點,拿到想要的資料。 這個其實和我們平時面對的需求是一個樣的,需求明確了,做出來的東西才可能是我們想要的,需求不明確,做出來的東西可能就不會是我們想要的了。 為了幫助大家簡單的理解相關的內容,這裡舉個監控的例子,監控不同渠道的下單和退單量。 涉及到量的,在一天內基本上是屬於只增不減的,這個時候我們一般會選用 **counter** 型別來處理。 一個是下單,一個是退單,所以這裡定義兩個 - yyyorder_created_total - yyyorder_canceled_total > counter型別的,一般在命名的時候最好都用_total作為結尾。 還有不同渠道呢? 渠道我們就用 lable 來標識一下。 最後展現格式大致如下: ``` yyyorder_created_total{appkey="mt",opreator="cw"} 1 yyyorder_canceled_total{appkey="pdd",opreator="cw"} 2 ``` > 這裡也要注意一個問題,確定指標的時候,也要避免定義太多指標出來,如果可以,考慮用label去進行區分同性質的內容。 ## 業務埋點 在明確了業務指標之後,就要在對應的業務上去進行埋點操作,會對業務程式碼有一定的侵入性,當然如果業務程式碼寫得足夠好,耦合的東西少,或許可以藉助AOP來埋點,從而降低侵入性。 後面就寫個簡單的例子來模擬業務埋點這一塊。 建立一個ASP.NET Core的專案,並安裝**prometheus-net.AspNetCore**這個nuget包。 ```xml
``` 其次是啟用 ASP.NET Core exporter middleware ```cs public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { // 這一句。 endpoints.MapMetrics(); endpoints.MapControllers(); }); } ``` 最後就是埋點了。 ```cs [ApiController] [Route("")] public class HomeController : ControllerBase { private static readonly Counter OrderCreatedCount = Metrics .CreateCounter("yyyorder_created_total", "Number of created orders.", new CounterConfiguration { LabelNames= new [] { "appkey", "opreator" } }); private static readonly Counter OrderCanceledCount = Metrics .CreateCounter("yyyorder_canceled_total", "Number of canceled orders.", new CounterConfiguration { LabelNames = new[] { "appkey", "opreator" } }); [HttpGet] public string Get() { var appKeys = new[] { "ali", "pdd", "mt" }; var opreators = new[] { "cw", "pz" }; var rd = new Random((int)DateTimeOffset.Now.ToUnixTimeMilliseconds()).Next(0, 2000); var appKeyidx = rd % 3; var opreatoidx = rd % 2; OrderCreatedCount.WithLabels(appKeys[appKeyidx], opreators[opreatoidx]).Inc(); var cRd = new Random((int)DateTimeOffset.Now.ToUnixTimeMilliseconds()).NextDouble(); if (cRd < 0.3d) { OrderCanceledCount.WithLabels(appKeys[appKeyidx], opreators[opreatoidx]).Inc(); } return "ok"; } } ``` 上面這個控制器中,建立了兩個**Counter**,就是上面確定業務指標中定義好的。 這裡是每訪問一次,就建立一個訂單,同時生成一個隨機數,如果是小於0.3,那麼就當它是退單的,這樣就可以把兩種指標都模擬出來了。 程式剛啟動是有部分預設指標的。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171207157-1511278739.png) 當我們訪問埋點的地址後,可以發現我們自定義的業務指標也已經有資料了。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171217948-1371730882.png) 到這裡,資料已經有了,我們要怎麼呈現呢? 要想呈現資料,我們需要先讓prometheus來儲存我們的業務指標資料。 ## 資料寫入 把資料寫入prometheus有兩鍾方式,一種是pull,一種是push。 pull是讓prometheus主動去拉取我們產生的資料,只要我們暴露一個地址出來即可,這中也是比較推薦的做法。 push方式要藉助pushgateway,埋點資料要先主動推送到pushgateway,後面在由pushgateway把資料寫進prometheus。 預設情況下,當我們用了`endpoints.MapControllers();`之後,就會把資料暴露在 **http://ip:port/metrics** 這個地址上。 知道要用pull的方式後,還要做什麼呢?當然就是要去配置promethues了。 前面我們的 `scrape_configs` 是通過檔案去自動發現的,所以只要在掛載的路徑上面加一個對應的yml檔案就可以了。 老黃這裡加了一個`nc-service.yml`,具體內容如下: ```yml - labels: service: nc project: demo targets: - 192.168.1.103:9874 - 192.168.1.103:9875 ``` 這個時候就可以在Targets裡面看到我們這兩個地址的資訊了。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171230918-1923297201.png) 通過prometheus的預設介面,也可以發現數據已經正常讀取了。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171242503-1603359750.png) 後面就是真正的資料查詢和展示了。 ## 資料展示 通過上面的步驟,我們已經保證資料可以正常寫入和查詢了,現在就在grafana中建立一個業務監控看板了。 在grafana中先配置我們的資料來源。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171253401-1344691162.png) 這裡填上我們prometheuse的地址儲存就可以了,可以看到那個綠色的提示,告訴我們這個資料來源是正常工作的了。 先來一個總的訂單數。 建立一個新的dashboard,再建立一個Panel。 我們在panel中填寫我們的資訊還有就是選擇要的圖形。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171309628-944402272.png) 然後就是寫上查詢條件,就可以看到我們要的結果了。 訂單總數這個查詢如下: ``` sum(ceil(increase(yyyorder_created_total[1d]))) ``` 裡面用到了, sum、ceil、increase這三個函式。 其中 **increase** 是用來統計一段時間範圍內的增量。後面帶了 [1d] 這個範圍表明這裡是檢視1天內的增量。 **ceil**是用來把increase的結果進行四捨五入的,可能有人會好奇,怎麼還會要四捨五入呢? 看看下面這個圖,大家就會發現,非常多的小數點。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171326945-271560792.png) 其實這個和prometheus的統計方法是有關係的,這裡不展開,先這樣用著。 **sum** 這個是用來求和的,指標中還包含了很多label,我們還要把每個label的求和,才是真正的結果。 所以這裡就得到了下面這個結果。 ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171338759-1901176339.png) 退單總數的查詢和訂單總數一樣,只是把名字換成退單的即可。 ``` sum(ceil(increase(yyyorder_canceled_total[1d]))) ``` 再來看看各渠道的訂單統計。 既然是看各渠道的統計,那麼這裡就要用到前面定義好的label了。appkey代表的就是渠道,那麼我們基於它去分一下組就可以了。 就得到下面的查詢。 ``` sum by (appkey) (ceil(increase (yyyorder_created_total[1d]))) ``` 結果如下: ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171354341-605647121.png) 同理,各渠道退單的也是一樣的寫法 ``` sum by (appkey) (ceil(increase (yyyorder_canceled_total[1d]))) ``` ps: 如果想把訂單和退單的放在一個圖裡面,可以加多個查詢。 示例如下: ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171410698-1711300149.png) 現在有了所有渠道的總量,各個渠道獨立的總量,那麼我們有辦法知道某個時間段的趨勢嗎? 這個是肯定有的,且聽老黃慢慢道來。 有上面這個疑問,多半是經歷過,某個時間段量非常多,但是有的時間段又幾乎為零,玩的就是心跳呀。 我們可以把這個稱之為時間段內的訂單增長情況。 這裡需要用到**rate**函式,這個就是幫助我們統計增長速率的函式。 它統計的是每秒的平均增長率,這個粒度有點太細,所以我們會在這個基礎上乘以60,放大到一分鐘。 然後在看它sum的結果,最後才四捨五入。 ``` ceil(sum(rate(yyyorder_created_total[5m]) * 60)) ``` 結果如下: ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200816171423610-872635277.png) 從這個結果可以看出,訂單大部分時間都沒有增長,只在中間那個時間段,有部分單進來。 到這裡主要的各個小panel已經完成了,剩下的就是在dashboard裡面調整位置,大小那些了。 ## 總結 這樣打造出來的監控看板還是挺不錯的,不過還是要注意下面幾個問題的 1. prometheus是把資料儲存在本地的,總是會達到上限的,這裡要麼是定期刪,要麼是寫到遠端儲存。 2. prometheus自己獨立的查詢語法可能剛開始會比較不適應,查不出自己想要的結果,這裡多查查資料,多實踐基本問題也不大。 3. 業務埋點這一個塊,還是要儘可能的減少對現有業務程式碼的侵入性。 4. 業務指標一定要確定好,不然埋點痛苦,查詢也痛苦。 這裡還沒有涉及到告警相關的內容,後面有時間再寫一個告警相關的。 文中示例程式碼: [https://github.com/catcherwong-archive/2020/tree/master/08/PromDemo](PromDemo) 本文首發於我的個人公眾號 **不才老黃** ,不定期釋出一些內容,有興趣的可以關注一下喲! ![](https://img2020.cnblogs.com/blog/558945/202008/558945-20200814201156451-840453165.jpg)