监控

使用 Prometheus 监控应用数据

Prometheus 是使用 Go 语言开发的一个监控工具和时序数据库,它的实现参考了 Borgmon。监控系统大体来说分两种模式,push 和 pull。push 模式就是应用程序主动把监控数据推送到监控服务,pull 模式就是监控服务来主动拉取应用的数据。Prometheus 采用的是 Pull 模式。

对于自己编写的应用,可以使用 prometheus 的 sdk 来自己提供 metrics,对于开源的软件,可以使用对应的 exporter .

监控系统基础原则

  • 尽量简单,不要上来就想搞个大新闻,喧宾夺主
  • 告警也尽量简单,只发需要处理的告警
  • 简单的架构就是最好的架构,业务系统都挂了,监控也不能挂。

不要想着把所有的数据都显示到监控上,太多了反倒是让人失去了重点。想象一下最容易出错的情况,以及在这种情况下你应该怎么用监控来排错。

  • 一个控制台不要有超过 5 个图。
  • 每个图上不要有超过五条线。当然堆栈图和饼形图除外。
  • 当使用提供的模板时,避免在右手边的表格里有多过 20-30 个条目。

系统的每个部分都应该有一个监控,至少让你大概知道这个系统现在的情况如何。

也不要想着把特别复杂的业务数据画到监控系统上,监控是监控,业务是业务,不能相互替代。

Prometheus 的指标类型

Prometheus 常用的有四种类型:计数器 (counter), 刻度 (gauge), 直方图 (histogram), 摘要 (summary).

计数器只增不减,用来记录一件事情发生了多少次,可以使用 rate(some_counter[interval])(具体含义后面会说到)来计算一件事情的速率。Counter 类型主要是为了 rate 而存在的,即计算速率,单纯的 Counter 计数意义不大,因为 Counter 一旦重置,总计数就没有意义了。rate 会自动处理 Counter 重置的问题,Counter 的任何减少也会被视为 Counter 重置。

Gauges 可以被设定,可以增高,可以减小。用来记录状态,比如正在进行的请求的数量,空闲内存数,温度等。对于 gauge 值,不要使用 rate. 可以使用 max_over_time 等来处理 gauge 数据。

Histogram 和 Summary 都是采样观测量,典型的比如请求的时间和相应的体积等等。他们记录观测的数量和观测的所有值,允许你计算平均值等。Histogram 实际上是一个复合值,由三部分组成,一部分是观测到的值,存在不同的 bucket 中,而 bucket 的大小则由用户指定,默认情况下是观测一个网页请求的延迟。观测的数量(也就是_count) 变量是一个 counter 类型的值,观测的和(也就是_sum) 变量也类似一个 counter, 当观测值没有负数的时候。显然响应时间和响应体积都是正的。

Histogram 类型数据最常用的函数是 histogram_quantile 了,可以用来计算 P95,P99 等数据。

如果有一个观测值叫做 http_request_duration_seconds, 那么要计算刚过去的 5 分钟内的平均时长可以这样算:

# 先不用理解,后边会讲到
rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])

Histogram 在服务端计算,Summary 在客户端计算并且不能被重新计算。如果可能的话,最好使用 Histogram, 不要使用 summary.

另外,Prometheus 支持 labels, 也就是标签,这样就可以很好地查询过滤指标,而不需要创建很多的指标了。

输出指标到 Prometheus

这里以 Python 为例。

pip install prometheus_client

Counter

from prometheus_client import Counter

# 按照 Prometheus 的最佳实践, counter 类型的数据后缀是 _total
# prometheus 客户端会智能处理 _total 后缀,在后台总是有 _total 后缀的
c = Counter("http_request_failures_total", "http 请求出错计数")
c.inc()  # 默认是 1
c.inc(2)  # 也可以指定数字

Counter 还有一个方便的属性,叫做 count_exceptions, 可以用作装饰器或者 with 语句中。

@c.count_exceptions()
def f():
    pass

with c.count_exceptions():
    pass

with c.count_exceptions(ValueError):
    pass

Gauge

from prometheus_client import Gauge

g = Gauge("cpu_usage", "CPU 使用率")
g.inc()
g.dec(10)
g.set(4.2)

Gauge 也有一些方便的辅助函数,比如说 track_inprogress 用来记录正在执行的数量。

 g.set_to_current_time()

 # Increment when entered, decrement when exited.
@g.track_inprogress()
def f():
  pass

with g.track_inprogress():
  pass

也可以给 gauge 设定一个回调函数来取值:

d = Gauge('data_objects', 'Number of objects')
my_dict = {}
d.set_function(lambda: len(my_dict))

Histogram

值得注意的是,histogram 默认定义的 buckets 大小是为了正常的网页请求设计的,也就是围绕着一秒的一些数据。如果我们需要观测一些其他的值,那么需要重新定义 buckets 的大小。

一般来说,buckets 是呈指数分布的,中间值为最常见的典型值,这样可以更好地拟合实际的分布(幂次分布)。因为 buckets 是以 label 的形式实现的,所以 buckets 最好也不要超过十个。

from prometheus_client import Histogram

h = Histogram()
h.observe(4.7)

@h.time()
def f():
  pass

with h.time():
  pass

# 默认的 buckets[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
h = Histogram(buckets=[1, 10, 100])

标签导出

如果要导出标签的话,需要使用 labels 方法

from prometheus_client import Counter
c = Counter('my_requests_total', 'HTTP Failures', ['method', 'endpoint'])
c.labels(method='get', endpoint='/').inc()
c.labels(method='post', endpoint='/submit').inc()

HTTP 服务器

前面我们提到 Prometheus 是采用的拉模型,那么从哪儿拉数据呢?需要我们的程序开启一个 http 的服务器,这样 Prometheus 才能来拉取数据。

如果实在普通的脚本里面,可以这样:

from prometheus_client import start_http_server

start_http_server(8000)

如果本身就是个 web 服务器,那么直接 mount 导一个路径就好了。不过实际上这是不可用的,因为生产中的服务器都是多进程的,而 Prometheus 的 Python 客户端不支持多进程。

from flask import Flask
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from prometheus_client import make_wsgi_app

# Create my app
app = Flask(__name__)

# Add prometheus wsgi middleware to route /metrics requests
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {
    '/metrics': make_wsgi_app()
})

很遗憾的是, Prometheus 的 Python 客户端对于多进程的支持不好.

使用 PromQL 查询指标

数据类型

在 Prometheus 中有四种数据类型,分别是:数字,字符串,直接向量 (instant vector) 和区间向量 (range vector).

数字和字符串就不用说了,重点说一下后两个向量。直接向量其实就是指标,比如说 http_request_count, 他就是一个一维的时间向量。而区间向量其实是二维的,在每一个时间点都是一个向量。

那么怎么生成区间向量呢?使用 [] 操作符。比如说 http_requests_total[5m], 表示在每个时间点,该时间点过去五分钟的时间序列,也就是二维的。那么区间向量有什么用呢?答案很简单:给 rate 函数使用。

比如说,我们常见的计算网页 qps 的函数:rate(http_requests_toal[5m]), 意思就是,在每个时间点都取前五分钟的统计数据计算访问速率,实际上这不就是求导么,而 5m 就是其中 dx 的取值。但是和微分不一样的是,dx 肯定不是越小越好,因为 Prometheus 抓取数据有间隔,所以显然不能小于抓取间隔,一般取抓取间隔的 4 倍左右,5m 就是个很好的值。采样周期 5m 如果设置的大一些,图像就会更平滑,如果小一些就会更精确。

官方建议将 Rate 计算的范围向量的时间至少设为抓取间隔的四倍。这将确保即使抓取速度缓慢,且发生了一次抓取故障,也始终可以使用两个样本。此类问题在实践中经常出现,因此保持这种弹性非常重要。例如,对于 1 分钟的抓取间隔,您可以使用 4 分钟的 Rate 计算,但是通常将其四舍五入为 5 分钟。

查询语法

使用 {} 来过滤指标, 大概相当于 SQL 中的 where 子句。除了 = 之外,还有 !==~(正则) 和 !~(不匹配)

<metric name>{<label name>=<label value>, ...}

比如:

api_http_requests_total{method="POST", handler="/messages"}

如果要查询历史数据可以使用 offset xx 来查询。比如下面这条表示比过去一个小时的 gc fraction 还要大 1.5 倍的数据。

go_memstats_gc_cpu_fraction > 1.5 * (go_memstats_gc_cpu_fraction offset 1h)

使用 by 关键字可以聚合字段:

# sum+rate 其实是求和的意思(求导再积分), 然后按照 instance 聚合
sum(rate(node_network_receive_bytes_total[5m])) by (instance)

常用函数

使用 Dashboard 展示指标

可视化界面

Prometheus 自带了在 /graph 下有一个 expression browser, 可以绘制一些简单的图形,除此之外还是建议使用 grafana.

数据采集配置

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ["localhost:9090"]

对每一个 job 都会自动生成一些指标:

up{job="<job-name>", instance="<instance-id>"}: 1 if the instance is healthy, i.e. reachable, or 0 if the scrape failed.
scrape_duration_seconds{job="<job-name>", instance="<instance-id>"}: duration of the scrape.
scrape_samples_post_metric_relabeling{job="<job-name>", instance="<instance-id>"}: the number of samples remaining after metric relabeling was applied.
scrape_samples_scraped{job="<job-name>", instance="<instance-id>"}: the number of samples the target exposed.
scrape_series_added{job="<job-name>", instance="<instance-id>"}: the approximate number of new series in this scrape.

其中的 up 指标可以用来监控目标服务是否正常运行

报警

Prometheus 使用 AlertManager 做告警。
可以使用 predict_linear 等函数基于预测的做一些报警。

如何选择监控指标

首先问自己一个问题:当我的程序出了问题的时候,我需要哪些数据来 debug 呢?

Google SRE Book 中提出了四个黄金原则:延迟、流量、错误数、饱和度(需要排队无法提供服务的时间)。实际使用中对于资源可以使用 USE 指标,对于在线服务可以使用 RED 指标。

  • USE 指标:Utilization、Saturation、Errors。如 Cadvisor 数据
  • RED 指标:Rate、Errors、Duration。如 Apiserver 性能指标

被监控服务的类型

就监控而言,服务大概可以分为三类:在线服务,离线处理 和 跑批任务。

在线服务

此类系统的关键指标在于 QPS, 错误率和延迟。正在进行中的请求的数量也有用。

在线服务系统在客户端和服务端都应该做监控。如果两遍有不同的行为,那么这个对调试是很有意义的。如果一个服务有很多客户端,也不可能让服务监控每个客户端,所以客户端肯定需要依赖自己的数据。

当你按照 query 开始或结束统计数量一定要使用一致的标准。推荐使用结束来作为标准,因为比较容易实现,而且能统计错误和延迟。

离线系统

对每一个 stage, 记录进入的 item 的数量,有多少在处理中,上次你处理某个东西的时间,多少 item 被发送出去。如果你采用的是批处理,也应该记录进出的批的数量。

更好的方法是通过系统发送一个心跳包:一些带着时间戳的 dummy item 通过整个系统。每个 stage 都输出他看到的最近的时间戳,这样你就知道一个 item 需要多长时间才能经过整个系统了。

批操作

关键指标是上次成功操作的时间。
This should generally be at least enough time for 2 full runs of the batch job. For a job that runs every 4 hours and takes an hour, 10 hours would be a reasonable threshold. If you cannot withstand a single run failing, run the job more frequently, as a single failure should not require human intervention.

对于其他的子系统而言,可以选择如下指标

第三方库

如果一个库会访问进程外的资源,比如网络硬盘等等,至少要记录下所有的访问次数,错误和延迟。

Depending on how heavy the library is, track internal errors and latency within the library itself, and any general statistics you think may be useful.

日志

As a general rule, for every line of logging code you should also have a counter that is incremented. If you find an interesting log message, you want to be able to see how often it has been happening and for how long.
一个比较通用的规则,对于每一条日志,应该有一个计数器。如果你发现了一条有有意思的信息,你肯定想知道这件事

错误

Failures should be handled similarly to logging. Every time there is a failure, a counter should be incremented. Unlike logging, the error may also bubble up to a more general error counter depending on how your code is structured.

线程池

对于所有的线程池来说,核心指标是排队的请求的数量,正在使用的线程的数量,总线程的数量,已经处理的任务的数量和处理任务花费的时间,以及任务排队花费的时间。

缓存

缓存核心指标是总的查询数,命中数,总的延迟以及缓存所对应的线上系统的查询数量,错误率,延迟。

合理使用标签

比如说,不要创建 httpresponse500total 和 httpresponse403total 这种指标,创建一个 httpresponsetotal 指标,然后使用不同的状态码作为标签。然后你就可以把整个 metric 作为一个规则和图表。

但是也不要滥用标签,前往不要用 IP 或者 email 这种信息来做标签,因为他们可能是无限的。这时候就不应该用监控系统了,可能需要一些 OLAP 的分析工具了。

总的来说,把 metrics 的秩 (cardinality) 控制在 10 以下。整个系统要控制超过 10 的 metric 的数量。绝大多数的查询不应该有标签。

为了避免秩过高的监控数据,可以添加如下的报警规则:

# 统计每个指标的时间序列数,超出 10000 的报警
count by (__name__)({__name__=~".+"}) > 10000

“坏指标”报警出来之后,就可以用 metricrelabelconfig 的 drop 操作删掉有问题的 label(比如 userId、email 这些一看就是问题户)

如果不确定的话,首先别用标签,有了真实的 use case 再添加。

参考

  1. Should I run prometheus in a Docker?
  2. Logs and metrics and graphs, oh my!
  3. developerWorks 上的入门文档
  4. https://blog.frognew.com/2017/05/prometheus-intro.html
  5. https://github.com/yolossn/Prometheus-Basics
  6. https://mp.weixin.qq.com/s/sr8AxTMZTjUoe1XYrbRgyw
  7. https://zhuanlan.zhihu.com/p/24811652
  8. https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650782456&idx=1&sn=654615ca4199514687ae8ec65444dec9
  9. https://medium.com/@valyala/promql-tutorial-for-beginners-9ab455142085
  10. https://github.com/prometheus/client_python
  11. http://www.xuyasong.com/?p=1717
  12. https://www.section.io/blog/prometheus-querying/
  13. https://github.com/danielfm/prometheus-for-developers#monitoring-uptime

influxdb 排坑记

相同点覆盖

Duplicate points

A point is uniquely identified by the measurement name, tag set, and timestamp. If you submit Line Protocol with the same measurement, tag set, and timestamp, but with a different field set, the field set becomes the union of the old field set and the new field set, where any conflicts favor the new field set.

相同的 measurement,tagset 和 timestamp 的数据会覆盖

解决方法:

  1. 提高时间精度
  2. 增加一个tag来标识不同的点
  3. 预处理数据 ✔️

每个点的 tag 数量

opentsdb 限制为每个点 8 个 tag

influxdb 限制不明确,最好也不要太多

在设计数据库是需要考虑的是,同一类型的字段作为 tag key 还是 tag value?

两种情况分别为:

time        key1 key2 key3
1500000000  true true true
1500000001  true false false
time        key   value
1500000000  _key1 true
1500000000  _key2 true
1500000000  _key3 true
1500000001  _key1 true
1500000001  _key2 false
1500000001  _key3 false

假设某个类型的字段可选值为 k 个,而一个用有 n 个这样的字段。如果作为 tag key,那么可能数据库的 series 复杂度是 O(k^n)。而作为 tag value,那么数据库的复杂度是 O(k * n)。也就是第二种的 series 复杂度大大降低。

但是对于第二种方法,效果就是打的点更多。第一种方法打点 m 个的时候,第二种需要 m * n 个。

另外考虑对于 down sampling 的友好性,虽然第二种造成点更多,但是在进行 down sampling 的时候有更好的压缩效率。

https://docs.influxdata.com/influxdb/v1.2/concepts/schemaanddata_layout/#don-t-have-too-many-series

max-values-per-tag 限制

每个 tag 可选值也有限制,不能超过10万,tag用于标识点,而不是用于存储数据,如果需要存储数据,应该使用 fields。

比如:

  • 域名:可选范围很大
  • 邮箱:可选范围几乎无限大
  • UUID

max-series-per-database 限制

tagset 的定义:每一个点的 tag key 和 tag value 的组合
series 的定义:使用同一个 measurement,retention policy,tagset 组合的点的集合

每一个数据库中的 series 的数量不能超过 100 万

以上连个限制的原因在于,influxdb 为每个 series 建立了索引并且常驻内存,如果过大

tag 只能存储字符串

一般来说,在 tag 中应该存储的是 enum 的值,而不是任意的字符串。

推荐配置

https://docs.influxdata.com/influxdb/v1.6/concepts/schemaanddata_layout/

如果想要使用 group by,推荐使用 tags
如果需要使用函数(mean,sum), 只有 fields 才能
如果需要使用数字,只有 fields 才能

硬件配置

https://docs.influxdata.com/influxdb/v1.6/guides/hardware_sizing/

Python + Influxdb + Grafana 的监控系统

influxdb

为什么我们要把监控数据存到 influxdb 呢? 存到 MySQL 或者 MongoDB 甚至 Elasticsearch 不好么?

数据模型上, 监控数据是和时间相关的, 脱离点产生的时间, 那么数据将毫无意义, 传统数据库中并没又强调这一点.

性能上, influxdb 是时间序列数据库, 这类数据库面临的问题是写入非常多, 而读取比较少. 而普通的数据库一般都是读比较多, 而写入较少, 并且要保证写入的正确性.

监控打点显然是一个面向时间序列的过程, 并且写入非常多, 而一般只有在触发告警, 排查问题的时候可能读取才比较多. 所以从性能和功能考虑上来说, 传统数据库都是不适用的.

influxdb 中常见的存储数据格式:

cpu_usage value=49 1502043216
cpu_usage value=50 1502193042
cpu_usage value=5 1502196258

influxdb 数据模型

我们以一个 measurement(测量) 作为一个表, tag-value, field-value 都是记录数据的键值对, 区别是 tag 是由索引的, 而 field 没有, timestamp 是时间戳. tag set 自然指的是一组 tag 的组合.

measurement,tag=value,tag1=value1 field=value,field1=value1 timestamp

measurement + tag set 被称为一个序列(series). 每一个 series 都可以指定不同的 retention policy.

influxdb 查询

使用类似 SQL 的语言, 执行 influx 进入shell

> CREATE DATABASE mydb
> SHOW DATABASES
> USE mydb

插入数据, 和 SQL 差别还是挺大的, 其中 cpu 是 measurement, 也就是 “表名”, 没指定时间的话, influxdb 会自己加上.

INSERT cpu,host=serverA,region=us_west value=0.64

查询数据, 注意多出来的 timestamp 一栏

> SELECT "host", "region", "value" FROM "cpu"
name: cpu
---------
time                                     host       region   value
2015-10-21T19:28:07.580664347Z  serverA   us_west    0.64

回收策略

默认情况下, influxdb 会永久保留数据, 一般来说这样是没有意义的, 我们可以设置短一点.

CREATE RETENTION POLICY <retention_policy_name> ON <database_name> DURATION <duration> REPLICATION <n> [DEFAULT]

其中 replication 只能设置为 1, 因为开源版只有 1. 可以设置成 30d, 1w

Python 客户端的编写

看到这里有人可能要问了, 不是有 python-influxdb 这个库么, 好好地客户端你为什么要封装一层呢? 答案很简单: 性能.

  1. 调用 influxdb.write_points() 是一个涉及到网络的阻塞操作, 极有可能对于程序造成性能影响.
  2. 如果我们在程序中散落着各种打点的代码, 那么就会造成没打一个点都去调用一些 IO, 不如放在一个队列里面可以每次多打几个, 减少 IO 次数, 这样对程序和 influxdb 的性能都有好处.

UDP vs http

influxdb 支持使用 UDP 和 HTTP 两种协议访问. 显然对于打点这种操纵来说, 我们不关心响应结果, 哪怕一个点没打上, 没打上就没打上吧……所以采用 UDP 就好了. 根据测试 udp 的性能在 http 的几十倍.

按照 influxdb 官网的建议, 我们需要调整 UDP buffer 的 size 到 25MB(26214400B) 为宜.

查看系统的 udp buffer 大小:

$ sysctl net.core.rmem_max
$ sysctl net.core.rmem_default

修改 /etc/sysctl.conf 文件:

net.core.rmem_max=26214400
net.core.rmem_default=26214400

但是这个设置只有到下次重启才能生效, 继续使用 sysctl 设置立即生效:

$ sysctl -w net.core.rmem_max=26214400
$ sysctl -w net.core.rmem_default=26214400

另外注意, UDP 有一个大坑, 只吃吃精度为 s 的打点, 所以在配置和客户端中都必须使用这个时间精度.

P.S. 中文互联网上的好多教程都在使用 http 打点, 误人子弟, 毁人不倦啊……

参考

  1. https://docs.influxdata.com/influxdb/v1.6/supported_protocols/udp/
  2. https://github.com/MikaelGRA/InfluxDB.Client/issues/31
  3. https://blog.codeship.com/a-deep-dive-into-influxdb/
  4. http://docs.grafana.org/features/datasources/influxdb/

influxdb+grafana+telegraf 监控系统搭建

本文基于 ubuntu 18.04

要不要用 docker?

这是一个哲学问题,用不用其实都有各自的好处。不过在这里我倾向于不用。因为 influxdb 和 grafana 都有好多的状态,而且不是都可以写到 mysql 中的,所以既然还得 mount 出来,何苦用 docker 呢?telegraf 需要采集系统的相关信息,更不适合放在 docker 里面。

  • InfluxDB,开源的时间序列数据库
  • Grafana,开源的数据可视化工具

搭建过程

influxdb && telegraf

因为这两个都是一家的产品,所以安装步骤都是一样的。按照 官网 给的安装步骤,其实很简单的。

curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/lsb-release
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

sudo apt-get update && sudo apt-get install influxdb telegraf
sudo systemctl start influxdb telegraf

我们暂时就不配置了,直接使用默认配置。可以通过 systemctl status influxdb 来查看状态

grafana

同样参考 官网 的教程。

基本概念

Grafana 从数据源中读取数据,数据源一般是时间序列数据库。their data can be mixed in a single panel.

Grafana 使用 orginazations and users to manage permissions.

A dashboard consists of rows, rows consist of panels. for each panel, there is a query editor to edit which data the panel should show.

VERSION=5.1.4
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${VERSION}_amd64.deb
apt-get install -y adduser libfontconfig
dpkg -i grafana_${VERSION}_amd64.deb

sudo systemctl daemon-reload
sudo systemctl enable grafana-server
sudo systemctl start grafana-server

然后就 ok 啦,打开 http://ip:3000 就能访问 grafana 的界面了,默认用户名和密码是 admin。如果是在阿里云等云上面,注意要在安全组里面开一下 3000 端口。

配置

配置 telegraf 的插件

配置 grafana 的 datasource

未完待续

参考资料

  1. https://blog.csdn.net/w958660278/article/details/80484486
  2. https://juejin.im/post/5b4568c851882519790c72f3
  3. https://grafana.com/dashboards/928
  4. http://docs.grafana.org/guides/gettingstarted/
  5. http://docs.grafana.org/guides/basic_concepts/

监控系统基本概念与选型

时序数据库

监控系统的基础是时序数据库

现代的监控系统一般都有如下几部分组成:

时序数据库 + 前端显示 + 报警系统 + 指标收集

一般需要实现的功能:

  • 度量数据收集和可视化
  • 收集尽可能多的性能和状态数据
  • 图形化做有意义的展示
  • 如果发现可疑问题,可以关联其他图表找到原因
  • 错误检测
  • 按需告警,触发条件越宽松则告警应该越少
  • 避免误报

从监控的层次划分的话,一般包含三层监控:

  • 基础层:主机的 CPU, 内存,网络及 IO 等
  • 中间层:应用运行的中间件层,Nginx, Tomcat, MySQL, Redis
  • 应用层:服务端及客户端性能数据,如 API 访问次数和响应时间等

现代的监控越来越关注应用层和其他层数据的整合能力,具有快速找到系统瓶颈,方便扩容或代码优化。

时序数据库的选择

监控数据往往都带有时间戳,其实就是一种典型时间序列数据,而这方面已经有很多专门的存储系统,如 opentsdb,influxdb,prometheus 等。相比 mysql 这样的传统数据库,这类系统在存储、查询上针对时间序列数据都做了特别的优化。

其中 opentsdb 基于 hadoop 生态系统,考虑到搭建的复杂度,暂时不考虑了。influxdb 和 prometheus 都是 Golang 编写的,直接一个二进制文件就可以运行。两者的区别有:

  • prometheus 对于保存长时间的数据有一些问题,influxdb 似乎没有问题
  • 另外 influxdb 可以直接写入,而 prometheus 是基于拉 (pull) 模式的,也就是说程序不能直接写入 prometheus,而是需要由 prometheus 去定期拉监控数据,太反人类了。
  • influxdb 的查询类似 SQL,而 prometheus 的查询语法更加简洁,但是有学习成本,各有千秋吧

所以选用 influxdb 了。

前端显示

唯一的标准自然是越漂亮越好,所以我们选择 grafana。

当然另一需要考虑的是编写查询界面不要过于复杂,这方面 grafana 只需要拖拽空间和勾勾选选就可以了,显然不成问题。

报警系统

grafan 自带了一些报警,但是只能根据阈值报警,显然不能满足我们的需求。我们这里选择了 bosun,是 Stack Overflow 开源的一款监控系统。通过简单的几个语句就可以编写复杂的报警条件。

指标收集

按照前面的分析,对于应用层,也就是我们自己的代码,可以随意地添加代码打点,这里不再赘述。对于系统的 metrics 的收集,可以使用 influxdb 公司钦定的 telegraf。telegraf 也有一些不同的插件,可以很好地支持 mysql、redis 等的监控数据收集。

参考

  1. Logs and Metrics and Graphs, Oh my!
  2. Prometheus 和 influxdb 对比
  3. 360 基于 Prometheus 的在线服务监控实践
  4. 虎牙直播运维负责人张观石:基于时序数据库的直播业务监控实践
  5. 监控系统选型
  6. openTSDB/Bosun 报警语法 介绍 / 学习笔记
  7. https://zhuanlan.zhihu.com/p/28127748