本节将会讲解如何使用 Telegraf(StatsD) + InfluxDB + Grafana 搭建一套完整的监控系统。

7.1.1 Telegraf(StatsD) + InfluxDB + Grafana 简介

Telegraf 是一个使用 Go 语言开发的代理程序,可收集系统和服务或者其他来源(inputs)的数据,并将其写入 InfluxDB(outputs)数据库,支持多种 inputs 和 outputs 插件。StatsD 是一个使用 Node.js 开发的网络守护进程,通过 UDP 或者 TCP 方式收集各种统计信息,包括计数器和定时器等。

InfluxDB 是一个使用 Go 语言开发的开源的分布式时序、事件和指标数据库,无需外部依赖,其设计目标是实现分布式和水平伸缩扩展。

Grafana 是一个使用 Go 语言开发的开源的、功能齐全的、漂亮的仪表盘和图表的编辑器,可用来做日志的分析与展示曲线图(如 api 的请求日志),支持多种 backend,如 ElasticSearch、InfluxDB、OpenTSDB 等等。

工作流程:Telegraf 将 StatsD(inputs)和 InfluxDB(outputs)结合起来,即发往 StatsD 的数据,最终通过 Telegraf 写入了 InfluxDB,然后 Grafana 读取 InfluxDB 的数据展示成图表。

7.1.2 启动 docker-statsd-influxdb-grafana

我们使用 Docker 一键启动 Telegraf(StatsD)+ InfluxDB + Grafana,节省搭建环境的时间。

  1. $ docker run -d \
  2. --name docker-statsd-influxdb-grafana \
  3. -p 3003:3003 \
  4. -p 3004:8083 \
  5. -p 8086:8086 \
  6. -p 22022:22 \
  7. -p 8125:8125/udp \
  8. samuelebistoletti/docker-statsd-influxdb-grafana:latest

端口映射关系如下:

  1. Host Container Service
  2. -----------------------------------
  3. 3003 3003 grafana
  4. 3004 8083 influxdb-admin
  5. 8086 8086 influxdb
  6. 8125 8125 statsd
  7. 22022 22 sshd

7.1.3 熟悉 InfluxDB

容器启动后,浏览器访问 localhost:3004(以下称为 influxdb-admin),如下所示:

Telegraf + InfluxDB + Grafana(上) - 图1

InfluxDB 的基本概念如下:

  • database:数据库。
  • measurement:数据库中的表。
  • point:表里面的一行数据,由时间戳(time)、数据(field)和标签(tag)组成
    • time:每条数据记录的时间戳,是数据库中的主索引(会自动生成)。
    • field:各种记录的值(没有索引的属性)。
    • tag:各种有索引的属性。

InfluxDB 采用了类 SQL 的查询语法,例如:

  • show databases:列出所有数据库。
  • show measurements:列出当前数据库的所有表。
  • select * from xxx:列出 xxx 表的所有数据。

我们在 Query 中输入:

  1. show databases

查询结果如下:

Telegraf + InfluxDB + Grafana(上) - 图2

_interal 是 InfluxDB 内部使用的数据库,telegraf 是我们当前 Docker 容器启动后默认创建的测试数据库。

7.1.4 配置 Grafana

浏览器打开 localhost:3003,如下所示:

Telegraf + InfluxDB + Grafana(上) - 图3

输入用户名 root 和密码 root 登录,进入初始化配置页,单击 “Add data source”,如下填写:

Telegraf + InfluxDB + Grafana(上) - 图4

单击 “Save & Test” 保存配置。目前配置好了 Grafana 默认的 datasource 是名为 api 的 InfluxDB,接下来创建测试代码,产生测试数据。

7.1.5 node-statsd

node-statsd 是一个 statsd 的 Node.js client。创建以下测试代码:

  1. const StatsD = require('node-statsd')
  2. const statsdClient = new StatsD({
  3. host: 'localhost',
  4. port: 8125
  5. })
  6. setInterval(() => {
  7. const responseTime = Math.floor(Math.random() * 100)
  8. statsdClient.timing('api', responseTime, function (error, bytes) {
  9. if (error) {
  10. console.error(error)
  11. } else {
  12. console.log(`Successfully sent ${bytes} bytes, responseTime ${responseTime}ms`)
  13. }
  14. })
  15. }, 1000)

运行以上代码,每一秒钟会产生一个 0~99 之间的随机值(模拟响应时间,单位为毫秒),发送到 StatsD,StatsD 会通过 Telegraf 将这些数据写入 InfluxDB 的 telegraf 数据库。

回到 influxdb-admin,单击右上角的下拉菜单切换到 telegraf 数据库,然后输入 show measurements 查看已经存在 api 表了,然后输入:

  1. select * from api

查询结果如下:

Telegraf + InfluxDB + Grafana(上) - 图5

可以看出 api 表有以下几个字段:

  • time:InfluxDB 默认添加的时间戳。
  • 90_percentile:所有记录中从小到大 90% 那个点的值。
  • count:一次收集的日志数量,可以看出每条记录(point)的 count 值接近或等于 10,而我们的测试代码是 1s 发送一条数据,也就说明 Telegraf 默认设置是 10s 收集一次数据,默认配置也的确是这样的,见:https://github.com/samuelebistoletti/docker-statsd-influxdb-grafana/blob/master/telegraf/telegraf.conf
  • host:机器地址。
  • lower:最小的那条记录的值。
  • mean:所有记录的平均值。
  • metric_type:metric 类型。
  • stddev:所有记录的标准差。
  • upper:最大的那条记录的值。

7.1.6 创建 Grafana 图表

回到 Grafana,单击左上角 Grafana 图标的下拉菜单,单击 Dashboards 回到仪表盘页继续完成配置,单击 “New dashboard”,然后单击创建 Graph 类型的图表,就创建了一个空的图表,如下所示:

Telegraf + InfluxDB + Grafana(上) - 图6

单击当前的图表,选择 Edit,修改如下几个地方:

  1. Metrics 配置中选择 FROM -> api 表,SELECT -> field(mean) 字段。
  2. Display 配置中 “Null value” 选择 connected,将每个点连成折线。

效果如下所示:

Telegraf + InfluxDB + Grafana(上) - 图7

7.1.7 模拟真实环境

middlewares/statsd.js

  1. const StatsD = require('node-statsd')
  2. const statsdClient = new StatsD({
  3. host: 'localhost',
  4. port: 8125
  5. })
  6. module.exports = function (routerName) {
  7. return async function statsdMiddleware (ctx, next) {
  8. const start = Date.now()
  9. try {
  10. await next()
  11. const spent = Date.now() - start
  12. statsdClient.timing(`api_${routerName}`, spent)
  13. } catch (e) {
  14. statsdClient.increment(`api_${routerName}_${e.status || (ctx.status !== 404 ? ctx.status : 500)}`)
  15. throw e
  16. }
  17. }
  18. }

server.js

  1. const Bluebird = require('bluebird')
  2. const Paloma = require('paloma')
  3. const app = new Paloma()
  4. const statsd = require('./middlewares/statsd')
  5. app.route({ method: 'GET', path: '/', controller: [
  6. statsd('getHome'),
  7. async (ctx) => {
  8. // 模拟十分之一出错概率
  9. if (Math.random() < 0.1) {
  10. console.error('error')
  11. ctx.throw(400)
  12. }
  13. // 模拟 1-100 毫秒响应时间
  14. const responseTime = Math.floor(Math.random() * 100 + 1)
  15. await Bluebird.delay(responseTime)
  16. console.log(`Spent ${responseTime}ms`)
  17. ctx.status = 200
  18. }
  19. ]})
  20. app.listen(3000)

client.js

  1. const axios = require('axios')
  2. setInterval(() => {
  3. // 模拟 1-10 的 tps
  4. const tps = Math.floor(Math.random() * 10 + 1)
  5. for (let i = 0; i < tps; i++) {
  6. axios.get('http://localhost:3000')
  7. }
  8. }, 1000)

打开两个终端,分别运行:

  1. $ node server.js
  2. $ node client.js

回到 influxdb-admin,输入:

  1. show measurements

可以看到已经有 api_getHome 和 api_getHome_400 表了。回到 Grafana,在一行(row)里创建两个图表,分别为:

  • 请求量:包含了正常请求(200)和错误请求(4xx、5xx 等等)请求量的折线图。
  • 响应时间:正常请求的最低(lower)、平均(mean)、最高(upper)响应时间的折线图。

Telegraf + InfluxDB + Grafana(上) - 图8

以 “getHome 响应时间” 的图表为例,Metrics 配置截图如下:

Telegraf + InfluxDB + Grafana(上) - 图9

7.1.8 参考链接

上一节:6.5 Sentry

下一节:7.2 Telegraf + InfluxDB + Grafana(下).md)