在石墨,我們之前使用 ELK 搭了一套監(jiān)控圖表,由于一些原因,比如:
成都創(chuàng)新互聯(lián)公司主要從事成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)豐寧,十余年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):028-86922220
1、Kibana 經(jīng)常查日志查掛
2、Kibana 的圖表不太美觀、不夠靈活
所以調(diào)研了一下,選擇用 StatsD + Grafana + InfluxDB 搭建一套新的監(jiān)控系統(tǒng)。
工具簡(jiǎn)介
StatsD 是一個(gè)使用 Node.js 開發(fā)的簡(jiǎn)單的網(wǎng)絡(luò)守護(hù)進(jìn)程,通過 UDP 或者 TCP 方式偵聽各種統(tǒng)計(jì)信息,包括計(jì)數(shù)器和定時(shí)器,并發(fā)送聚合信息到后端服務(wù),例如 Graphite、ElasticSearch、InfluxDB 等等,這里 列出了支持的 backend。
Grafana 是一個(gè)使用 Go 開發(fā)的開源的、功能齊全的、好看的儀表盤和圖表的編輯器,可用來(lái)做日志的分析與展示曲線圖(如 api 的請(qǐng)求日志),支持多種 backend,如 ElasticSearch、InfluxDB、OpenTSDB 等等。在線 DEMO。
InfluxDB 是一個(gè)使用 Go 語(yǔ)言開發(fā)的開源分布式時(shí)序、事件和指標(biāo)數(shù)據(jù)庫(kù),無(wú)需外部依賴,其設(shè)計(jì)目標(biāo)是實(shí)現(xiàn)分布式和水平伸縮擴(kuò)展。
啟動(dòng) docker-statsd-influxdb-grafana
我使用的 Docker 鏡像 docker-statsd-influxdb-grafana 一鍵啟動(dòng) StatsD + Grafana + InfluxDB,省去很多麻煩(此處省略一萬(wàn)字)。
因?yàn)槲冶緳C(jī)是 Mac,所以以下演示如何在 Mac 下使用 Docker 搭建(其他系統(tǒng)用法應(yīng)該差不多)。在此之前,先安裝 Docker,Mac 下雖然有 Kitematic,我們還是用命令行來(lái)演示。
啟動(dòng)一個(gè) docker-machine:
Desktop docker-machine startStarting "default"... (default) Check network to re-create if needed... (default) Waiting for an IP... Machine "default" was started. Waiting for SSH to be available... Detecting the provisioner... Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command. Desktop docker-machine env export DOCKER_TLS_VERIFY="1"export DOCKER_HOST="tcp://192.168.99.100:2376"export DOCKER_CERT_PATH="/Users/nswbmw/.docker/machine/machines/default"export DOCKER_MACHINE_NAME="default"# Run this command to configure your shell: # eval $(docker-machine env) Desktop eval $(docker-machine env) Desktop docker ps
記住分配的 DOCKER_HOST 后面會(huì)用,我這里為 192.168.99.100。
啟動(dòng) docker-statsd-influxdb-grafana,命令如下:
docker run -d \ --name docker-statsd-influxdb-grafana \ -p 3000:9000 \ -p 8083:8083 \ -p 8086:8086 \ -p 22022:22 \ -p 8125:8125/udp \ samuelebistoletti/docker-statsd-influxdb-grafana:latest
運(yùn)行 docker ps 查看:
配置 InfluxDB
瀏覽器打開:http://你的ip:8083。點(diǎn)擊右上角設(shè)置的圖標(biāo),添加 username 和 password 都為 datasource,點(diǎn)擊 Save 保存,如下所示:
注意:用戶名和密碼這里先填 datasource,后面會(huì)說(shuō)明。此外,這個(gè) docker 鏡像自動(dòng)為我們?cè)?InfluxDB 創(chuàng)建了名為 datasource 的 db。
配置 Grafana
瀏覽器打開:http://你的ip:3000。
1、輸入 user 和 password 都為 root,登錄
2、點(diǎn)擊左上角圖標(biāo) -> Data Source -> Add data source,進(jìn)入配置數(shù)據(jù)源頁(yè)面,如下填寫,點(diǎn)擊 Save:
注意:url 中替換成你分配的 ip。
使用 node-statsd
node-statsd 是一個(gè) statsd 的 Node.js client。創(chuàng)建以下測(cè)試代碼:
'use strict';const StatsD = require('node-statsd'), client = new StatsD({ host: '192.168.99.100', port: 8125}); setInterval(function () { const responseTime = Math.floor(Math.random() * 100); client.timing('api', responseTime, function (error, bytes) { if (error) { console.error(error); } else { console.log(`Successfully sent ${bytes} bytes, responseTime: ${responseTime}`); } }); }, 1000);
注意:host 改為你分配的 ip。
運(yùn)行以上代碼,每一秒鐘產(chǎn)生一個(gè) 0-99 之間的隨機(jī)值(模擬響應(yīng)時(shí)間,單位毫秒),發(fā)送到 StatsD,StatsD 會(huì)將這些數(shù)據(jù)寫入 InfluxDB 的 datasource 數(shù)據(jù)庫(kù)。
創(chuàng)建 Grafana 圖表
回到 Grafana 頁(yè)面。
1、點(diǎn)擊左上角圖標(biāo) -> Dashboards -> +New 進(jìn)入創(chuàng)建圖表頁(yè)
2、點(diǎn)擊左側(cè)的綠塊 -> Add Panel -> Graph 創(chuàng)建一個(gè)圖表
創(chuàng)建 API 請(qǐng)求量圖表
1、點(diǎn)擊 General -> Title 修改為 "API 請(qǐng)求量"
2、點(diǎn)擊 Metrics -> Add query,點(diǎn)擊如圖所示位置,選擇 "api.timer.count",ALIAS BY 填寫 "tps",如下所示:
3、點(diǎn)擊左上角保存(或用 ctrl + s),我選擇了顯示 5 分鐘內(nèi)的數(shù)據(jù),每 5s 刷新一次,如下所示:
創(chuàng)建 API 響應(yīng)時(shí)間圖表
1、點(diǎn)擊 +ADD ROW -> 點(diǎn)擊左側(cè)的綠塊 -> Add Panel -> Graph,創(chuàng)建一個(gè)圖表
2、點(diǎn)擊 General -> Title 修改為 "API 響應(yīng)時(shí)間"
3、點(diǎn)擊 Metrics -> Add query,點(diǎn)擊如圖所示位置,選擇 "api.timer.mean",ALIAS BY 填寫 "mean"
4、點(diǎn)擊 Add query,選擇 "api.timer.mean_90",ALIAS BY 填寫 "mean_90"
5、點(diǎn)擊 Add query,選擇 "api.timer.upper_90",ALIAS BY 填寫 "upper_90"
最終如下所示:
講解一下:
1、mean: 所有請(qǐng)求的平均響應(yīng)時(shí)間
2、mean_90: 去除最高響應(yīng)時(shí)間的 10% 請(qǐng)求后,剩余的 90% 請(qǐng)求的響應(yīng)時(shí)間的平均值
3、upper_90: 去除最高響應(yīng)時(shí)間的 10% 請(qǐng)求后,響應(yīng)時(shí)間最大的那個(gè)值
當(dāng)然這個(gè) 90% 是可以配置的,比如也可以設(shè)置為 95%,更多信息見:
1、https://github.com/etsy/stats...
2、https://github.com/etsy/stats...
注意事項(xiàng)
1、docker-statsd-influxdb-grafan 這個(gè) docker 鏡像里配置 StatsD 的配置在 /opt/statsd/config.js,里面寫死了 InfluxDB 的配置,所以如果改 InfluxDB 的 db 或者 username 或者 password,別忘了改這個(gè)配置。
2、在 InfluxDB 的 web 管理頁(yè)使用查詢語(yǔ)句,如你在 node-statsd 使用 client.timing('api') 并不會(huì)創(chuàng)建 api 的表,會(huì)創(chuàng)建如 api.timer.count 等等這樣的表,所以如下查詢是沒有結(jié)果的:select from api,可以在 datasource 下使用:select from /.*/ 查看 datasource 下所有數(shù)據(jù)。
3、在使用 node-statsd 時(shí),只發(fā)送了 timing 類型的數(shù)據(jù),此類型也會(huì)額外創(chuàng)建 counting 類型的數(shù)據(jù),所以這樣是多余的 client.increment('api')。
在 Koa 中使用
lib/statsd.js
'use strict';const StatsD = require('node-statsd');const config = require('config');module.exports = new StatsD({ host: config.statsd.host, port: config.statsd.port });
middlewares/statsd.js
'use strict';const statsdClient = require('../lib/statsd');module.exports = function () { return function *statsd(next) { const routerName = this.route ? this.route.handler.controller + '.' + this.route.handler.action : null; const start = Date.now(); yield next; const spent = Date.now() - start; if (routerName) { statsdClient.timing(`api.${routerName}`, spent); statsdClient.timing('api', spent); } }; };
app.js
app.use(require('./middlewares/statsd')());
我們用了 bay 框架(基于 Koa 二次開發(fā)),所以可以用 Koa 的所有中間件,bay 有一個(gè) route 屬性,包含 handler 和 action,所以可以直接拿來(lái)用,切記上面 routerName 不要直接用 this.path 等等(如: /users/:userId 這個(gè) api 每次 userId 都會(huì)不一樣,導(dǎo)致 InfluxDB 創(chuàng)建不同的表)。如果你用的 Koa 的話,可以在每個(gè) controller 或 route 里添加 routerName 屬性,如:this.routerName = 'xxx',然后將上面修改為:
const routerName = this.routerName;
一鍵導(dǎo)入數(shù)據(jù)
我們 API 有近百個(gè)接口,要是每次都去手動(dòng)創(chuàng)建并配置圖表那就費(fèi)老勁了,而且每次創(chuàng)建的圖表的配置都差不多,于是我尋思尋找一些捷徑。我發(fā)現(xiàn) Grafana 有 Template 的功能,然而嘗試了下并沒有搞明白怎么用。。我又發(fā)現(xiàn) Grafana 有 Import 的功能,于是先把配置好的圖表先導(dǎo)出 JSON,然后不斷復(fù)制粘貼修改,保存嘗試 Import 看下效果 ,最后成功。
注意:導(dǎo)出的 JSON 中 rows 代表了每一行,每個(gè) row 中有一個(gè) panels 數(shù)組存儲(chǔ)了每一個(gè) Graph(下圖一個(gè) row 有兩個(gè) Graph),每個(gè) Graph 有一個(gè) id 字段是遞增的(如:1、2、3...),targets 下每個(gè)曲線的 refId 是遞增的(如:A、B、C...),記得修正過來(lái),否則無(wú)法正常顯示圖表。
最終我寫了個(gè)腳本,運(yùn)行后生成了每個(gè)接口的 JSON 文件,30 多個(gè)接口導(dǎo)出了 30 多個(gè)文件,每次 Import 那也要 30 幾次。機(jī)智的我怎么可能就此放棄(其實(shí)是懶),應(yīng)該還有更省事的方法,我在瀏覽器中導(dǎo)入的時(shí)候,在控制臺(tái)看了下 Grafana 的網(wǎng)絡(luò)請(qǐng)求,發(fā)現(xiàn)導(dǎo)入時(shí)調(diào)用的是:
POST https://xxx:3006/api/dashboards/import
而且 JSON 文件的數(shù)據(jù)直接放在 post 請(qǐng)求體里,那這樣就好辦了,也不用生成文件了,最后生成的配置放到了一個(gè)數(shù)組里,用 co + co-request 循環(huán)調(diào)用上面那個(gè)接口導(dǎo)入就好了,真正做到一鍵導(dǎo)入數(shù)據(jù)。
以下是一個(gè) dashboard 及對(duì)應(yīng)的 JSON 配置:
{ "id": 32, "title": "API file", "tags": [], "style": "dark", "timezone": "browser", "editable": true, "hideControls": false, "sharedCrosshair": false, "rows": [ { "collapse": false, "editable": true, "height": "250px", "panels": [ { "aliasColors": {}, "bars": false, "datasource": "api-influxdb", "editable": true, "error": false, "fill": 2, "grid": { "threshold1": null, "threshold1Color": "rgba(216, 200, 27, 0.27)", "threshold2": null, "threshold2Color": "rgba(234, 112, 112, 0.22)" }, "id": 1, "isNew": true, "legend": { "avg": false, "current": false, "max": false, "min": false, "show": true, "total": false, "values": false }, "lines": true, "linewidth": 1, "links": [], "minSpan": 6, "nullPointMode": "connected", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], "span": 6, "stack": false, "steppedLine": false, "targets": [ { "alias": "tps", "dsType": "influxdb", "groupBy": [ { "params": [ "$interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "measurement": "api.file.show.timer.count", "policy": "default", "refId": "A", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [], "type": "mean" } ] ], "tags": [] } ], "timeFrom": null, "timeShift": null, "title": "api.file.show.count", "tooltip": { "msResolution": true, "shared": true, "sort": 0, "value_type": "cumulative" }, "type": "graph", "xaxis": { "show": true }, "yaxes": [ { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ] }, { "aliasColors": {}, "bars": false, "datasource": "api-influxdb", "editable": true, "error": false, "fill": 1, "grid": { "threshold1": null, "threshold1Color": "rgba(216, 200, 27, 0.27)", "threshold2": null, "threshold2Color": "rgba(234, 112, 112, 0.22)" }, "id": 2, "isNew": true, "legend": { "avg": false, "current": false, "max": false, "min": false, "show": true, "total": false, "values": false }, "lines": true, "linewidth": 2, "links": [], "minSpan": 5, "nullPointMode": "connected", "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], "span": 6, "stack": false, "steppedLine": false, "targets": [ { "dsType": "influxdb", "groupBy": [ { "params": [ "$interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "measurement": "api.file.show.timer.mean", "policy": "default", "refId": "A", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [], "type": "mean" } ] ], "tags": [], "alias": "mean" }, { "dsType": "influxdb", "groupBy": [ { "params": [ "$interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "measurement": "api.file.show.timer.mean_90", "policy": "default", "refId": "B", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [], "type": "mean" } ] ], "tags": [], "alias": "mean_90" }, { "dsType": "influxdb", "groupBy": [ { "params": [ "$interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "measurement": "api.file.show.timer.upper_90", "policy": "default", "refId": "C", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [], "type": "mean" } ] ], "tags": [], "alias": "upper_90" } ], "timeFrom": null, "timeShift": null, "title": "api.file.show.timer", "tooltip": { "msResolution": true, "shared": true, "sort": 0, "value_type": "cumulative" }, "type": "graph", "xaxis": { "show": true }, "yaxes": [ { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ] } ], "title": "Row" } ], "time": { "from": "now-1h", "to": "now" }, "timepicker": { "refresh_intervals": [ "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ], "time_options": [ "5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d" ] }, "templating": { "list": [] }, "annotations": { "list": [] }, "refresh": "5s", "schemaVersion": 12, "version": 2, "links": [], "gnetId": null}
Grafana 更多用法
目前我只簡(jiǎn)單地用 Grafana 來(lái)統(tǒng)計(jì):
1、api 總的平均響應(yīng)時(shí)間
2、api 每個(gè)接口的 tps 和平均響應(yīng)時(shí)間
3、未來(lái)還會(huì)加入 cpu 和內(nèi)存的使用情況等等
Grafana 還支持各種 plugin,如 grafana-zabbix 接入 zabbix 的監(jiān)控?cái)?shù)據(jù)等等。
最后
我們正在招聘!
[北京/武漢] 石墨文檔 做最美產(chǎn)品 - 尋找中國(guó)最有才華的工程師加入
網(wǎng)站欄目:使用StatsD+Grafana+InfluxDB搭建Node.js監(jiān)控系統(tǒng)
標(biāo)題網(wǎng)址:http://m.newbst.com/article30/gsegso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、服務(wù)器托管、營(yíng)銷型網(wǎng)站建設(shè)、微信小程序、域名注冊(cè)、App開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)