InfluxDB-FLUX查询优化、使用 InfluxDB 搭建报警系统

FLUX查询优化

使用谓词下推的查询

谓词下推常见于SQL 查询中,一个SQL 中的谓词,通常指的是 where 条件。

我们看一个最简单的 SQL 语句。它从一个名为 A 的表中查询数据,并按照 n>10 的条件对数据进行过滤。

select * from A where n > 10

你可以想象一下这条 SQL 语句在计算机中的执行流程。大体上有下面两种方式。

MMSIZE

  • 一种是将磁盘里的数据全部读到内存中,再在内存中进行过滤。这种方式我们通常说它没有内存下推

  • 另一种是在查询时,就只从磁盘取自己需要的数据到内存中,再进行下一步的操作。通常,我们说这种方式实现了谓词下推。

虽然说 FLUX 语言表面上是一个脚本语言,但在查询这件事上,它并不是老老实实一行行执行的,而是有了优化器的参与。FLUX 语言在执行时,会尽可能实现谓词下推的优化,什么样的查询可以实现谓词下推, 可以参考官网文档的优化查询一节https://docs.influxdata.com/influxdb/v2.4/query-data/optimize-queries/

MMSIZE

另外,后面我们会告诉大家如何去查看一个查询的执行计划。

避免将窗口宽度设得过小

窗口(基于时间间隔对数据进行分组)通常用于聚合和降采样数据。将窗口设长一点可以提高性能。窗口过窄会导致需要更多的算力来评估每条数据应该分配到哪个窗口,合理的窗口宽度应该根据查询的总时间宽度来决定。

避免使用“沉重”的功能

下面的这些函数对于 FLUX 来说会比较很重,这些函数会使用更多的内存和 CPU,使用这些函数时要想要是否必要。

  • map()

  • reduce()

  • join()

  • union()

  • pivot()

不过官方又说,InfluxData 一直在优化 FLUX 的性能,所以当前的列表不一定是将来的情况。

尽可能使用set()而不是 map()

如果你要给数据查一个静态常量,那么 set 比 map 要有很大的性能优势。map 是我们上面说的沉重操作。在后面的示例,我们会比较两种操作的差距。

平衡数据的时间范围和数据精度

想要保证查询的性能良好,应该平衡好查询的时间范围和数据精度。如果,有一个measurement 的数据每秒入库一条,你一次请求 6 个月的数据,那么一个序列就能包含1550 万点数据。如果序列数再多一些,那么数据很可能会变成数十亿点。Flux 必须将这些数据拉到内存再返回给用户。所以一方面做好谓词下推尽量减少对内存的使用。另外,如果必须要查询很长时间范围的数据,那应该创建一个定时任务来对数据进行降采样,然后将查询目标从原始数据改为降采样数据。

使用FLUX 性能分析工具查看查询性能

执行 FLUX 查询时,你可以导入一个名为 profiler 的包,然后添加一个 option 选项以查看当前 FLUX 语句的执行计划。比如:

option profiler.enabledProfilers = ["query", "operator"]

这里的 query 和 operator 是查询计划的两个选项,query 表示你要查看整个执行脚本的的执行情况,operator 表示你要查看一个 FLUX 查询各个算子的执行情况。

(1)query(查询)

query 提供有关整个 Flux 脚本执行的统计信息。启用后,结果将多出一个表,其中包含以下信息:

  • TotalDuration:查询总持续时间(以纳秒为单位)

  • CompileDuration:编译查询脚本所花费的时间(以纳秒为单位)

  • QueueDuration:排队所花费的时间(以纳秒为单位)

  • RequeueDration:重新排队花费的时间(以纳秒为单位)

  • PlanDuration:计划查询所花费的时间(以纳秒为单位)

  • ExecuteDuration:执行查询所花费的时间(以纳秒为单位)

  • Concurrency:并发,分配给处理查询的 goroutines。

  • MaxAllocated:查询分配的最大字节数(内存)

  • TotalAllocated:查询时分配的总字节数(包括释放然后再次使用的内存)

  • RuntimeErrors:查询执行期间返回的错误消息

  • flux/query-plan:flux 查询计划

  • influxdb/scanned-values:数据库扫描磁盘的数据条数

  • influxdb/scanned-buytes:数据库扫描磁盘的字节数

(2)operator(算子)

有关一个查询脚本中每个操作的统计信息。在存储层中执行的操作将作为单个操作返回。启用此配置后,返回的结果将多出一个表,并包含以下内容

  • Type:操作类型

  • Label:标签

  • Count:执行这个操作的总次数

  • MinDuration:操作被执行多次中,最快的一次花费的时间(以纳秒为单位)

  • MaxDuration:操作被执行多次中,最慢的一次花费的时间(以纳秒为单位)

  • DurationSum:当前操作完成的总持续时间(以纳秒为单位)。

  • MeanDuration:操作被执行多次的平均持续时间(以纳秒为单位)。

示例:使用 profile 优化查询

(1)编写查询

首先,打开 DataExplorer。写下如下代码:

from(bucket: "test_init")
 |> range(start: -1h)
 |> filter(fn: (r) => r["_measurement"] == "go_goroutines")
 |> map(fn: (r) => ({r with hello:"world"}))

这段代码,会从 test_init 存储桶查询一个名为 go_goroutines 的 measurement,这个测量下没有 tag,所以我们只有一个序列。

map 函数帮我们在 filter 后的数据上加了一个常量列,列名是 hello,值是字符串 world。

(2)执行查询

现在,SUBMIT 一下上面的代码,然后点击View Raw Data。数据应该如下所示。

MMSIZE

可以看到有一个常量列。

(3)修改代码查看性能指标和执行计划

现在,我们对代码做出修,以方便我们观察执行计划。

import "profiler"
option profiler.enabledProfilers = ["query","operator"]
from(bucket: "test_init")
 |> range(start: -1h)
 |> filter(fn: (r) => r["_measurement"] == "go_goroutines")
 |> map(fn: (r) => ({r with hello:"world"}))

代码解释:

  • option profiler.enabledProfilers,这其实是个开关选项,当后面的列表出现“query” 时,就显示整个查询的性能和执行计划,当然出现"operator"时,会具体显示每个算子的性能指标。

  • import “profiler”,引包,enabledProfilers 是 profiler 中的开关,不引没法用。

现在,再次点击 SUBMIT,同样还是观察原始数据。一上来展示的还是我们查询出来的数据,需要切换到页尾才能看到性能指标和执行计划。

MMSIZE

MSIZE

现在,我们看到两张表,有一个_measurement 为 profiler/query,这是我们整个查询的性能指标和执行计划。还有一个_measurement 为 profiler/operator 的,这是我们每个算子的性能指标。里面包括某个算子运行了多长时间等信息。

(4)如何判断谓词下推

按照官方文档的说法,如果实现了谓词下推,那么多个 operator 会合并成一个。我们可以看到现在操作列表里,有一个叫做 merged_ReadRange4_filter2 的算子操作,后面紧跟的是我们的map 操作。这说明 from -> range -> filter 被合并了。它们是一步操作。

(5)查看查询性能

查询性能有很多指标,但是我们现在只关注两个指标,一个是 MaxAllocated。它表示的是我们为了完成查询,总共使用过的内存(包含释放后又申请的内存)。

MMSIZE

现在这个指标的数值是 52736,也就是说为了完成这个查询,我们前后用了大概 50kb的内存。

另一个是TotalDuration,表示执行这个操作的总时间,现在是 17365972 纳秒。

MMSIZE

(6)在 map 后增加 AggregateWindow

现在,我们在 map 后面加上一个 AggregateWindow 函数。整体代码如下:

import "profiler"
option profiler.enabledProfilers = ["query","operator"]
from(bucket: "test_init")
 |> range(start: -1h)
 |> filter(fn: (r) => r["_measurement"] == "go_goroutines")
 |> map(fn: (r) => ({r with hello:"world"}))
 |> aggregateWindow(column: "_value", every: 1h,fn:mean)

(7)查看查询性能

首先关注我们的 operator 表,可以看到,操作数从之前的 2 个变成了 3 个。map 的后面,多了一个聚合窗口操作。

MSIZE

查询的 MaxAllocated 依然是 52736,没有变。

因为之前需要返回几百条数据,现在开窗聚合,只需要返回两条数据,所以查询的持续时间有所缩短。

(8)将 AggregateWindow 移至 map 前

现在,我们将AggregateWindow 移到 map 之前 filter 之后。修改后的代码整体如下:

import "profiler"
option profiler.enabledProfilers = ["query","operator"]
from(bucket: "test_init")
 |> range(start: -1h)
 |> filter(fn: (r) => r["_measurement"] == "go_goroutines")
 |> aggregateWindow(column: "_value", every: 1h,fn:mean)
 |> map(fn: (r) => ({r with hello:"world"}))

(9)查看查询性能

首先还是关注 operator 表,这张表里之前是 3 个操作,现在变成了两个。map 之前, 有一个 ReadWindowAggregateByTime 操作。也就是说,我们的 aggreagteWindow 操作实现了谓词下推。

MSIZE

当读磁盘的操作完成后,内存中只会存在聚合后的两条数据。现在我们关注查询性能指标。

可以看到 MaxAllocated 变成了 864,之前这一指标的数值还是 52736。之前要消耗50KB,现在却 1KB 都不到。

查询的持续时间也有进一步缩短。

(10)将 map 改为 set

最后值得说一下,我们的 map 操作数据的原理是对数据集中的数据一行一行处理。此处,我们用它实现了添加常量的功能。其实还有一个同样能完成此类任务的算子叫 set,它操作数据的逻辑是直接操作整个数据集。

数据量越大,这两个算子的性能差距就越明显。

此处,我们将 aggregateWindow 算子去掉,并将 map 改成 set。与第一次查看性能时的代码做比较,当时我们还没有做聚合操作。

改完的代码整体如下:

import "profiler"
option profiler.enabledProfilers = ["query","operator"]

from(bucket: "test_init")
 |> range(start: -1h)
 |> filter(fn: (r) => r["_measurement"] == "go_goroutines")
 |> set(key: "hello", value: "world")

(11)查看查询性能

运行之后,查看查询性能。可以看到用 Set 版的 MaxAllocated 是 51712,这跟 map 版的 52736 几乎没啥区别。

但是,我们看一下 TotalDuration 这个指标 4612562。之前的 map 版在这个指标上可是17365972。

这说明 set 操作要比 map 要快。

使用 InfluxDB 搭建报警系统

什么是监控

监控其实每隔一段时间对数据计算一下。比如,我有一个一氧化碳浓度传感器, 每 1分钟我就算一下这 1 分钟内室内一氧化碳浓度的平均值。将这个结果跟一个写死的标准值做比较,如果超过了就报警。这就是监控的基本逻辑。

所以,InfluxDB 中的监控其实也是一个 FLUX 脚本写的定时任务。只不过,不管是在HTTP API 还是在Web UI 上,InfluxDB 都把它和定时任务分离区别对待了。

认识检查、报警终端和报警规则

在 Web UI 的左侧工具栏中,点击 Alerts 按钮,会打开一个报警的配置页面。上方的选项栏中显示着 CHECKS ( 检查 )、 NOTIFICATION ENDPOINTS ( 报警终端) 和NOTIFICATION RULES(报警规则)分别对应着 InfluxDB 进行报警所需要的 3 个组件。

MSIZE

三个组件的功能分别如下:

  • CHECKS(检查):它其实也是一种定时任务,我们可以称之为检查任务。检查任务会从目标存储桶中读取部分数据然后进行阈值检查,并最终出 4 类信号。CRIT(严重)、WARN(警戒)、INFO(信息)和 OK(良好)。

    MSIZE

  • NOTIFICATION ENDPOINTS(报警终端):是一个向指定地址发送报警信号的组件。

  • NOTIFICATION RULES(报警规则):它可以指定哪些 Check 出问题了发送微信报警,哪些Check 出问题了可以发邮件通知。它相当于 Check 与报警终端之间的路由。

示例:模拟对一氧化碳浓度的报警(※)

(1)需求

假设我们现在有一个可以采集一氧化碳浓度的传感器,这个传感器通过物联网网络每隔一段时间就向我们部署在服务器上的 InfluxDB 插入一条数据,格式如下:

co,code=01 value=0.001 1664851126000

现在,我们希望使用 InfluxDB 能够完成下述的报警功能。

  • 当 CO 浓度大于 0.04 的时候发出CRIT(严重)级别的通知信号。

  • 当 CO 浓度介于 0.04 和 0.01 之间的时候发出 WARN(警戒)级别的通知信号。

  • 当 CO 浓度低于 0.01 的时候发出OK(良好)级别的通知信号。

最终,当 CO 浓度超标时,我们希望相关的工作人员能够收到一通电话,以便对事故做出快速响应。

(2)辅助工具

为了方便验证报警终端的效果,写了一个很简单的仅支持 POST 请求的 HTTP 服务。

直接使用下面的命令即可开启一个监听本地 8080 端口的 POST HTTP 服务。

./simpleHttpPostServer-linux-x64

这个命令执行后会阻塞终端,当它接收到 POST 请求后,会自动将请求体中的内容打印到终端。如果 0.0.0.0 不是你想绑定的 host 或者 8080 端口已经被占用。你可以使用下面的两个参数来修改。

例如:

./simpleHttpPostServer-linux-x64 -h localhost -p 8080

具体可以参考项目地址: https://github.com/realdengziqi/simpleHttpPostServer

(3)创建一个新的存储桶

为了避免我们将本示例的数据同之前的示例搞混,此处我们先创建一个新的名为example_alert 的存储桶。

(4)准备数据模板

在本示例中,我们会自己手动一条条地向 InfluxDB 中插入数据,所以可以打开一个文本编辑器(本教程使用 vs code)先编写一个 InfluxDB 行协议的数据模板,后面可以直接复制数据,稍微改一下数值然后接着插入。

数据模板如下:

co,code=01 value=0.001

(5)事先插入一两条数据

这一步操作是为了后面进行创建检查的操作时,查询构造器中有东西可选。所以为了顺利创建检查,这一步的操作不可以省略。

此处,我们在 Web UI 导入行协议数据的窗口上,分两次各导入一条数据。如图所示:

MSIZE

数据如下: 第一次

co,code=01 value=0.0015

第二次

co,code=01 value=0.0025

(6)创建检查(CHECK)

在左侧的工具栏中点击 Alerts 按钮。默认情况下会进入 CHECKS 的页面,如图所示:

MSIZE

将鼠标悬停在右上角 CREATE 按钮,会弹出一个下拉菜单,其中包括两个按钮:

  • THRESHOLD CHECK(阈值检查):这类检查任务主要是去判断数据有没有超出某种阈值限定。

  • Deadman Check(死人检查):这类检查任务是去判断某个序列下多长时间没有写入新的数据了。你也可以设定一个值,比如一旦超过 30s 某个序列还没有数据入库,就发出一个警戒信号。

此处,我们选择Threshold Check,创建一个阈值检查。

MMSIZE

后面会弹出一个对话窗口,其布局和 Data Explorer 非常像,但是功能上会有一些差异。

MSIZE

  • 最上方有一个Name this Check,点击一下可以给当前创建的Check 命名。

  • 左上角有一个选项卡,默认是选中了 DEFINE QUERY(定义查询),也就是上图所示的页面效果。

  • 页面的下方是一个查询构造器,需要注意,此处我们无法切换到脚本编辑器,也就是在此处,我们只能使用查询构造器来实现查询。

  • 最右边还有一个清单。上面说到为了创建一个阈值检查,你必须选择:

    • 一个字段

    • 一个聚合函数(也就是开窗后的聚合函数)

    • 一个或更多的值域。

现在,我们需要构造查询。如下图所示。

MSIZE

  1. 在存储桶处选择 example_alert

  2. _measurement 处选择 co

  3. 注意,虽然我们当前 co 这个 measurement 下只有一个序列,但是还是必须将 _field=value 添加到过滤条件中,否则右上方检查项 One Field 不会通过。

  4. 最后将聚合逻辑从默认的mean 改为 max。

  5. 点击 submit,可以预览数据的查询效果。

  6. 点击左上方的 CONFIGURE CHECK 按钮。这会让我们进入一个新的页面,在这个页面中,我们可以对阈值进行配置。

首先要注意,只有页面的下半部分发生了改变。

MSIZE

  • 最左侧的卡片对应的是查询和调度的进一步配置。这里我们将 Schedule Every 设为15s 这样,每隔 15 秒就会调用一次检查。

  • 中间的 STATUS MESSAGE TEMPLATE 是状态消息模板。这里支持使用 Shell 风格的取值语法。${ }。这里 r 的含义在后面会进行详细的讲解。此处保持默认的模板不做任何修改。

  • 最右侧的 THRESHOLDS 对应的是值域的设定。此处包含 4 种类型的值域,对应着一个检查能够发出的 4 中状态信息。它们分别是:

    • CRIT(critical 的前 4 个字母)表示严重紧急。

    • WARN(warning)表示警告、警戒

    • Info(Information)表示普通信息,提醒

    • ok 表示状态良好

此时,点击右下角的 CRIT 按钮,会弹出一个小的设置窗口,如下图所示:

这里的意思就是,当值大于多少的时候将检查的状态设置为 CRIT。此处 When value 右边的 is above 就是大于的意思,可以看到这还是一个下拉菜单。我们可以点击一下。会发现它还有更多可选的选项,包括 is below(小于)、is inside range(在什么范围之内)等。

0.00125 是 Web UI 根据我们当前的查询结果自动帮我们填充的。这里,根据我们的需求,co 的浓度值大于 0.04 的时候才将状态置为CRIT。效果如下。

同理,设置 Warn 和ok,Info 就不设置了。

最后点击右上角的对号,保存Check。

现在,我们回到了最初的 CHECKS 页面,可以看到,下方的列表里就有一个我们刚才配置的 Check。

MSIZE

(7)测试 Check

现在,可以回到上传数据的页面,尝试插入两条数据测试一下检查的运行效果。

MSIZE

插入的数据如下:

co,code=01 value=0.025

这时一氧化碳的浓度为 0.025,介于 0.01 到 0.03 之间,这个时候我们刚才创建的CHECK 应该发出 WARN 级别的信号。

现在,我们可以在左侧的工具栏里,点击 Alert History。

MSIZE

可以看到,现在我们的状态记录里面就躺着一条级别为 WARN 的通知。这里,右面的MESSAGE 显示Check:CO_Alert is : warn 就是我们的消息模板生成的消息。

(8)修改消息模板

当前,我们的消息模板提示的消息还不够精确,我们希望进行报警的时候能够把当前的一氧化碳浓度的值也输出出来。

这个时候可以看一下官方文档关于模板的说法,可以发现,官方文档指出,我们是可以通过 r.字段名的方式去访问到数据具体值的。

MSIZE

这样的话我们就可以重新修改消息模板,最终的消息模板如下图所示:

MSIZE

注意,模板中的 r.code 和 r.value。通过这个操作,我们可以直接提取数据中的设备编号和当前的一氧化碳浓度值。

(9)验证消息模板

接下来,我们再次插入一条数据。

co,code=01 value=0.0146

0.0146 在 0.01 和 0.03 之间,我们之前创建的 CHECK 应该再发出一个 WARN 级别的信号。

现在,还是在左侧的工具栏点击 Alter History,查看检查汇报的状态记录。我们发现新的状态记录里面的 MESSAGE 有所改变,这次在信息中我们可以看到设备编号和当时的一氧化碳浓度了。

MSIZE

(10)创建报警终端(NOTIFICATION ENDPOINT)

仅有状态记录还不够,我们还需要将信息发送到外部系统,比如给开发人员发送邮件, 或者拨打电话。那么这个负责向外面发送消息的组件就是报警终端。

  • 首先点击左侧工作栏中的 Alerts 按钮, 进入页面后,在上方的工具栏选择NOTIFICATION ENDPOINTS 选项卡。

    MSIZE

  • 点击右上角的CREATE 按钮,会弹出一个如下图所示的对话窗。

    MSIZE

左上角的 Destination 有个下拉菜单,这个其实是报警终端的类型,可以看到此处为我们提供了 3 种终端,HTTP、Slack 和 Pagerduty。Slack 和 Pagerduty 是海外开发团队常用的通讯软件,此处我们选择HTTP。

MMSIZE

  • 选择 HTTP 后,可以看到窗口中的配置项会发生变化。所谓 HTTP 终端,其实就是向一个目标地址发送POST 请求。

  • 我们暂时不向睿象云对接,而是想办法先去观察一下 HTTP 终端发出数据的数据结构。

    运行 ./simpleHttpPostServer-linux-x64。执行后,程序会监听 0.0.0.0:8080 地址。当它收到 POST 请求时,会在终端打印收到的数据。

  • 现在,我们可以将 InfluxDB 中的 HTTP 终端地址设为 http://host1:8080。如下图:

    MMSIZE

  • 最后点击右下角的CREATE NOTIFICATION ENDPOINT,创建终端。

(11)创建报警规则(NOTIFICATION RULES)

报警规则起到报警信息和终端之间的路由作用。报警规则可以指定哪些 Check 的何种级别的信息发送给哪个终端。

注意!创建报警规则的前提是已经创建了至少一个报警终端,否则 Web UI 上的创建报警规则按钮会变成灰色,也就是无法创建报警规则。

首先在左侧工具栏点击 Alerts 按钮,然后在上方的选项卡点击 NOTIFICATION。接着点击CREATE 按钮。

MMSIZE

现在可以看到一个设置报警规则的弹窗。如下图所示:

MMSIZE

最上方可以设置调度时间,看上去就像是一个定时任务。中间 Conditions 的意思是条件。比如现在默认的条件就是当 InfluxDB 中有 CHECK 的状态为 CRIT 时,就使用http_endpoint 发送报警信息。需要注意中间的Conditions 还有一个按钮叫做tag Filter,也就是按照标签过滤。

为了能够更快的看到报警效果,我们将调度的时间设为 15 秒。名字可以自己随便起。效果如下图所示。

MSIZE

在中间的 Conditions 区域,点击一下 Tag Filter 按钮,然后添加一个标签过滤条件为_check_name == CO_Alert。其中 CO_Alert 是我们之前创建的检查的名称。后面我们会讲到检查和通知规则的工作原理。此处先这样设置,设置后的效果如下图。

MSIZE

窗口最下方的 Message 区域,因为我们目前只有一个名为 http_endpoint 的终端, 所以这里UI 自动帮我们选择的 http_enpoint,保持现状就好。

MMSIZE

最后点击最下方的CREATE NOTIFICATION RULE 按钮,创建规则。

(12)测试报警信号发送效果

现在,我们要测试一下在 InfluxDB 里已经搭建好的报警链路。只需插入一条模拟的一氧化碳浓度的数据,让它的值大于 0.04 即可。

插入的数据如下:

co,code=01 value=0.05

如图所示:

MSIZE

接下来,在左侧工具栏点击 Alert History,来到报警历史页面,等待大概 15 秒。

MSIZE

正常情况下,在检查状态历史记录里应该会出现一个级别为 crit 的状态信息。

点击上方的 NOTIFICATIONS 按钮,这时应该出现一个通知记录,这个列表里面是 InfluxDB 向外发送通知的记录。可以看到这条记录的最右边有一个绿色的对号,这说明我们的消息已经成功通过http_endpoint 发送出去了。

MSIZE

回到之前开启 simpleHttpPostServer 的终端,看一下里面的内容。

如图所示,我们成功收到一个 POST 请求,并将它请求体中的数据打印在了控制台。将这条 json 数据格式化后的效果如下:

MSIZE

可以看到,其中包含了数据的时间,报警的消息,报警的级别,事发时的一氧化碳浓度值等等。如果能够在终端看到最终的 json,说明 InfluxDB 的报警配置已经完成并且能够正常工作。

(13)检查和报警规则的工作原理

之前我们设置报警规则的时候发现,配置报警规则是需要设置调度的时间间隔的,感觉上有些奇怪,为什么一个规则还需要每隔一段时间去执行一次呢?这要先从检查的工作原理开始讲起。

InfluxDB 安装好后,会有一个由 InfluxDB 自动创建的名为_monitoring 的存储桶。我们可以用 DataExplorer 去查询一下其中的内容。

查询结果如下:

MSIZE

可以看到,查询的结果中,包含了 Check 任务生成的报警信息。比如有一个字段名为_check_name,值为 CO_Alert,这就是我们之前设置的检查名称。

也就是我们定时执行的 Check,其实是定时从 example_alert 里面查询数据出来,然后对其进行阈值检查,最终,将检查后的状态信息写入到 _monitoring 存储桶中。效果如下图所示:

MSIZE

其实后面的通知策略也是一个定时任务,它从_monitoring 查询最近一段时间的数据, 并根据你设置的条件对数据进行过滤, 最终如果有符合要求的数据, 就使用我们的http_endpoint 将数据转成 json 格式发送出去。最终整个流程如下图所示。

MSIZE

这也就是Check、Notification rule 和 Notification endpoint 在一起工作的原理。

示例:集成睿象云(报警系统的 Saas 方案)

(1)什么是睿象云

睿象云是一个告警平台,它提供五花八门的报警方式。你可以充值,选择电话报警, 按照说明进行一下配置,之后你会得到一个 API 接口。以后,当你的系统需要报警时,只需在代码里想这个 API 发送一个 http 请求,睿象云就会按照你配置的电话号码,拨打电话, 语音提醒程序员该加班了。

(2)注册睿象云

官网地址:https://www.aiops.com/

(3)创建自己的报警 API

(4)在睿象云上创建报警 API

  1. 首先进入睿象云的首页,点击左侧的智能告警平台按钮,进入智能告警平台的工作页面。

    MSIZE

  2. 如下图所示,点击上方选项卡的集成按钮,进入集成的配置页面

    MSIZE

  3. 这个时候,左侧有一个监控工具列表,可以看到睿象云可以和很多监控工具进行 集成,但是这个列表里面并没有 InfluxDB,这个时候还有一种万能的集成方案,REST API。

    REST API 会向外提供一个 URL,只要你的监控工具能够以 API 要求的数据格式向睿象云发送 POST 请求,那就可以同睿象云集成。

    MSIZE

  4. 这时,我们会进入一个配置页面。首先需要设置一个应用名称。再点击下方的蓝色按钮,保存并获取应用key。

    MSIZE

  5. 这个时候页面上会出现一行红字,这个就是 Appkey。注意不要把这个 key 泄漏出去哦。

至此,我们的报警API 就算是配置完了。

(5)创建分派策略

现在外部可以通过接口向睿象云发送报警信息了,但是睿象云平台如何将报警信息发送给具体的个人呢。

将报警信息转发给具体的人,这个过程叫做分派。

回到报警平台的主页,点击上方的配置按钮,然后再点击下方的右边的新建分派按钮。

MSIZE

此时会进入一个新的配置页面,可以按照下图的说明进行操作。注意,如果此处设置分派人的时候啥都不显示,说明你的账户目前还没有绑定邮箱,这个时候请自行绑定邮箱,再进行后面的操作。

MSIZE

配置好后,点击保存。

现在,我们的TICK_TEST API 一旦接收到报警信息,就会通知到具体的人了。

(6)InfluxDB 尝试与睿象云进行对接

现在,我们可以尝试与睿象云进行对接了,现在看来,只要让 InfluxDB 发出的通知数据符合睿象云 API 的要求就好了。我们可以回看一下刚才在睿象云创建的 API 的说明文档(在集成-\右侧应用列表-\找到你自己创建的REST API 应用-\点击编辑-\页面下方可见)

如下图所示,这里说明了我们应该发送什么格式的数据。

MSIZE

(7)报警终端的不足

现在我们回到 Web UI 的Alerts 页面,并点击到http_endpoint 的编辑页面。会发现,没有地方可以修改发送数据的格式。不错,InfluxDB 的报警终端就是没法去设置发送的数据格式的。所以到这一步,我们前功尽弃了。但是还有一个方案,我们会直接接触检查与报警的底层。

示例:Notebook 与报警的底层(※)

在之前,我们说过使用 Notebook 也可以创建报警任务,但是之后就再也没有接触过Notebook。这里我们直接借助Notebook,走进报警的底层。

(1)使用 Notebook 创建报警任务

首先,在左侧点击 Notebooks 按钮,来到 Notebooks 的配置页面。然后,点击Set an Alert 模板,创建一个新的 notebook。

MSIZE

(2)进入 Notebook 后,会发现第一个 Cell 是一个查询构造器。此处,我们将存储桶设为 example_alert,_measurement 设为 co,_field 设为 value。效果如下图所示:

MSIZE

(3)点击上方的 RUN 按钮,查看执行效果。

MSIZE

MSIZE

可以看到下方的两个 cell,一个 cell 是将查询出来的数据原样展示了出来,另一个是将数据绘制成了折线图。

(4)最后,下面还有一个 cell,左上角有这个 cell 的名称,New Alert。也就是说明这个 cell 是用来配置报警的。

MSIZE

上方有两块,一个是用来设置报警条件的,另一个是用来设置调度间隔的。此处,我们还是按照需求,将报警阈值设为 0.04。细心的同学可能发现,我们此处的报警阈值只能设置一种,少了crit、warn、info 和 ok,这个问题我们后面会提到。

MSIZE

(5)再看到这个 cell 的底部,这是报警终端的配置。此处,我们还是选择 http 终端。并将目标URL 设置为 http://host1:8080。

MSIZE

(6)上面的操作都完成后,点击右下方的 EXPORT ALERT TASK 按钮。

我们会惊喜地发现 Notebook 直接为我们生成了一个很长的 FLUX 脚本。如下图所示。现在,建议大家先将脚本复制出来,粘贴到 Data Explorer 里。稍后,我们自己研读这段脚本。

MSIZE

(2)脚本解读

脚本如下:

import "strings"
import "regexp"
import "influxdata/influxdb/monitor"
import "influxdata/influxdb/schema"
import "influxdata/influxdb/secrets"
import "experimental"
import "http"
import "json"

option task = {name: "Notebook Task for local_8dc08939-f5df-447e-8e53-41532537902f", every: 10m, offset: 0s}
option v = {timeRangeStart: -24h, timeRangeStop: now()}
check = {_check_id: "local_8dc08939-f5df-447e-8e53-41532537902f", _check_name: "Notebook Generated Check", _type: "custom", tags: {}}
notification = {_notification_rule_id: "local_8dc08939-f5df-447e-8e53-41532537902f", _notification_rule_name: "Notebook Generated Rule", _notification_endpoint_id: "local_8dc08939-f5df-447e-8e53-41532537902f", _notification_endpoint_name: "Notebook Generated Endpoint"}
task_data = from(bucket: "example_alert") |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
 |> filter(fn: (r) => r["_measurement"] == "co")
 |> filter(fn: (r) => r["_field"] == "value")
trigger = (r) => r["value"] > 0.04
messageFn = (r) => "${strings.title(v: r._type)} for ${r._source_measurement} triggered at ${time(v: r._source_timestamp)}!"
task_data
 	|> schema["fieldsAsCols"]()
 	|> set(key: "_notebook_link", value: "http://host1:8086/orgs/d2377c7832daa87c/notebooks/0a0bc4b03a6ba000")
 	|> monitor["check"](data: check, messageFn: messageFn, crit: trigger)
 	|> monitor["notify"](
		data: notification,
 		endpoint: http["endpoint"](url: "http://host1:8080")(
 			mapFn: (r) => {
 				body = {r with _version: 1}
            	return {headers: {
   
   "Content-Type": "application/json"}, data: json["encode"](v: body)}
 			},
		 ),
 )

接下来,我们就按照从前到后的顺序为大家讲解这段代码。

  • 导包:最上方的 import 代码我们直接跳过,不讲解了。

  • option task:option task 其实就是对定时任务的设置,这一行代码其实也表名了,notebook 帮我们生成的报警脚本本质上是一个 InfluxDB 的定时任务。

  • option v:第一行代码 option v,声明了一个 record 类型的变量,里面有两个键值对,其实分别表名了查询时间范围的开端和末端。这里显示-24h,其实是因为我们直接在 notebook 里面操作的时候,右上角的时间范围设置了-24h。后面我们会把它改成-15s。

  • checknotification 两个变量:这两行代码分别声明了一个 record,它其实是用来给后面的 monitor 函数做参数用的,因为_moitoring 存储桶中需要_check_id 和_check_name 和_type 等字段,所以 notebook 自动生成的时候自动帮我们安排好了。

  • 查询数据:上图中的代码完成了对 example_alert 存储桶的查询。并且查询的表流被赋值给了一个名为 task_data 的变量。

    task_data = from(bucket: "example_alert") |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
     |> filter(fn: (r) => r["_measurement"] == "co")
     |> filter(fn: (r) => r["_field"] == "value")
    
  • 声明阈值函数:此处,有一个名为 trigger 的函数,可见它的主体逻辑就是一个谓词表达式。在这里声明一个函数其实是因为后面有个 monitor 函数需要传入一个谓词函数。另外,可以看到这个函数的逻辑就是用来判断一氧化碳浓度是否超过 0.04 的。

    trigger = (r) => r["value"] > 0.04
    
  • 消息模板:这也是一个函数,不过它直接返回一个字符串,这里面的内容其实是消息模板。

    messageFn = (r) => "${strings.title(v: r._type)} for ${r._source_measurement} triggered at ${time(v: r._source_timestamp)}!"
    
  • 报警逻辑:接下来这一大段都是报警的逻辑。

    task_data
     	|> schema["fieldsAsCols"]()
     	|> set(key: "_notebook_link", value: "http://host1:8086/orgs/d2377c7832daa87c/notebooks/0a0bc4b03a6ba000")
     	|> monitor["check"](data: check, messageFn: messageFn, crit: trigger)
     	|> monitor["notify"](
    		data: notification,
     		endpoint: http["endpoint"](url: "http://host1:8080")(
     			mapFn: (r) => {
     				body = {r with _version: 1}
                	return {headers: {
         
         "Content-Type": "application/json"}, data: json["encode"](v: body)}
     			},
    		 ),
     )
    

    1)首先 schema[“fieldAsCols”]函数起到了转换数据结构的作用。效果如下图所示:

    MSIZE

    2)set 函数为表流添加了一个常量字段

    3)monitor[“check”]函数起到了检查状态的作用

    需要注意,data 参数传进来的 check 变量其实就是_check_id,_check_name 这些变量。messageFn 是消息模板,crit 在我们之前的 CHECK 里是一个报警级别,但是这里变成了函数的形参,传进来的函数值是 trigger 是我们之前提到过得谓词函数。

    另外注意, 虽然 notebook 帮我们生成的脚本里面值传递了 crit 参数,但是monitor[“check”]函数其实还有其他可传的参数。如下图所示:

    MSIZE

    可以看到,还有 info、ok、warn 参数。所以其实我们还是可以手动修改脚本把这些值域范围给补上的。

    4)monitor[“notify”]函数是用来向外发送数据的,可以看到里面声明了一个 http 终端。最后,十分需要注意的是有一个名为 body 的局部变量。这个其实就是我们发送 POST 请求的请求体。r 是我们表流里面的数据,所以说,对接不上睿象云的症结就在这里。

    我们只需要将 body 修改成符合睿象云 api 要求的格式就可以了。

    5)为什么我们不直接用 if else 的逻辑来完成大于小于检查然后直接向外发送请求, 而是用两个专门的 monitor 函数来完成功能呢?主要是因为 monitor 函数会在我们的 Alter history 里留下痕迹。也就是 monitior[“check”]和 monitor[“notification”]函数会向_monitoring 存储桶里写检查和通知记录,这是非常重要的。

(3)修改脚本以集成睿象云

最后,我们把 body 这个局部变量做一下修改,让它符合睿象云 API 要求的格式就好了。

MSIZE

修改好的代码如上图所示。

(4)创建定时任务

点击左侧Tasks 按钮,在点击右上方的CREATE TASK 按钮,

MSIZE

将方才我们修改好的脚本粘贴到编辑区域,将 option task 一行代码中的信息写到左侧的设置表单中,并删除原先的 option task 代码。

MSIZE

最后点击右上角的 Save 按钮,创建这个定时任务。

(5)测试报警效果

现在,我们上传一条 value 大于 0.04 的数据,测试一下对接的效果。

MSIZE

数据如下:

co,code=01 value=0.08

等待一段时间,可以看到,我们收到了一通电话,这个电话里面就向我们说明了一氧化碳浓度的值超过 0.04 了。

示例:改进报警系统

(1)当前的报警架构

MMSIZE

你可以将睿象云视作一个高可用的报警服务,也就是不论如何睿象云 24 小时都能无故障访问。那么结合睿象云,我们的 InfluxDB 可以设置一个检查数据合理性的定时任务,每隔一段时间就把最近的数据抽出来算一下。如果数据不合适,就发送一个报警信号给睿象云,然后由睿象云向我们具体的技术人员发起通知。

(2)更值得信任的架构

上一节的架构有一个问题,如果一晚上过去了,我的 InfluxDB 出现异常宕机了。InfluxDB 宕机了自然不会向睿象云发送报警信息,所以一夜过去了,你睡了一个好觉,但那真是一个平安夜吗?

所以,如果睿象云能够知道 InfluxDB 还有没有活着就好了。最好是睿象云能够每隔一段时间检查一下我的 InfluxDB 还在不在、能不能用。这种行为,我们称为业务可用性检查。

MMSIZE

在这张图中,橘黄色的箭头就是睿象云对 InfluxDB 的检查。

(3)接下来示例中的架构

使用睿象云进行报警,其实是向睿象云购买软件服务,这套软件在睿象云的服务器上, 而不是自己企业的服务器上。这种方式我们称为 SaaS,软件即服务。这种情况下,想让睿象云能够反过来访问我们的 InfluxDB 服务,那就得让 InfluxDB 服务暴露在公网。这个时候 InfluxDB 要么本身就在公网,要么利用内网穿透。因为这里的演示环境是内网,所以需要搭建内网穿透。

MMSIZE

这样,不管内网穿透崩了,还是 InfluxDB 崩了都会触发报警。

(4)搭建内网穿透

这里采用花生壳提供的内网穿透工具来实现内网穿透。一个新的花生壳账号,有免费的内网穿透额度,而且免费提供的 1M 大小的带宽

1)安装花生壳内网穿透客户端

访问官网下载页面:https://hsk.oray.com/download

MSIZE

注意选择和自己的系统匹配的安装包,我们演示的是使用的 CentOS,所以此处我选择CentOS Linux(x86_64)

使用下面的命令安装 deb 包。

sudo rpm -ivh ./phddns_5.2.0_amd64.rpm

安装完成后,会自动开启一个名为 Phtunnel 的服务,并且你会拥有一个可以控制这项服务的命令行工具,叫做 phddns。所有相关的信息,都显示在安装好后的打印出来的提示信息里了。

MMSIZE

2)激活 SN

正常情况下,安装好后,phddns 会自动运行。可以使用 phddns status 命令查看程序的运行状态。

phddns status

如果显示ONLINE,那就是正常运行。

注意这里的 SN 码,使我们的设备标识码。

另外,这里显示我们有一个远程的管理地址,是 http://b.oray.com

在浏览器里访问这个地址。会进入一个登录页面,如下图所示:

MMSIZE

现在,切换到 SN 登录,可以看到这里需要输入我们的设备 SN 码。刚才安装的时候也提示过我们,初始密码是 admin。现在输入 SN 码和密码,点击登录。

这里需要注册一个贝瑞账号,进行激活。

MMSIZE

3)配置内网穿透

激活成功后,看到管理页面,先点击左侧工具栏的内网穿透按钮,进入内网穿透的管理面板,点击新增映射。

MSIZE

按照图中顺序先后操作,注意内网主机是指你刚才安装内网穿透所在的主机。试用版最高只能有 1Mbps 带宽,换算上行和下行速度应当在 128kb/s

MMSIZE

点击确定后,会回到内网穿透的管理页面。

如果能看到下图所示的卡片,那就说明内网穿透已经配置成功。

MMSIZE

以后我们访问 https://1674b87n99.oicp.vip/就相当是在访问本地的 127.0.0.1:8086 了。

(5)配置业务可用性检测

1)创建监控任务

首先回到睿象云的主页,在左侧点击图中标出的业务可用性监测平台。

MMSIZE

来到监控任务的主页后,点击图中标出的绿色按钮(创建监控)

MMSIZE

首先完成监控设置,此处我们要把监控地址设成刚才我们配置过内网穿透的地址。地址使用/health,对这个地址发起 Get 请求,正常情况下会返回一个 json 格式的数据,它会告诉我们 InfluxDB 目前是否健康。另外,如果请求成功的话状态码应该为 200。最后,对这个接口进行get 请求,并不需要 token 加持。

MMSIZE

响应部分设置如下图所示,解释一下,这里的意思就是如果接口 2 秒内完成响应那说明速度比较满意,如果在 2~5 秒之间说明比较慢,如果大于 5 秒说明非常缓慢。

MMSIZE

点击右上角的结果验证,设置响应码为 200,也就是说响应码为 200 是我们期望的正常状态。

MMSIZE

监控频率设置使用 15 分钟,其实免费版最快只能每隔 15 分钟访问一次,充值后可以获得更高的访问频率

MMSIZE

运营商与监控区域,是指你要使用哪个省份、哪个运营商的网络对你的接口发起访问,因为有的时候一个接口,可能移动的网络可以访问,联通的网就访问不通。最后我们只选择一台主机。如下图所示。

MMSIZE

上述操作都完成后,点击右上角的保存按钮。

2)配置报警规则

回到监控列表后,可以看到页面上已经有一个监控项了。

MSIZE

现在点击左侧的告警按钮,我们来配置告警的通道。

可以看到,我们面对着 3 个概念:报警规则、报警策略、报警行为。和我们的InfluxDB 一样,这里的报警规则对应着检查任务,它将数据翻译为警告、严重和正常三种信号。报警行为相当于报警终端,你可以选择发送邮件还是拨打电话。报警策略就是用来连接报警规则和报警行为的,它相当于 InfluxDB 中的报警规则。

MMSIZE

首先我们来配置报警规则首先,点击左侧的报警规则按钮,然后点击+号

MMSIZE

给规则命名、并在规则类型上选择API 监控。

MMSIZE

点击上方选项卡的报警对象,可以看到这里左边是已经创建的 API 监控类型的监控任务,选中左边的 InfluxDB 健康状况,再点击中间的>按钮,将它加到已选列表里来,再点击下一步。

MMSIZE

可以看到,这里要设置严重条件,这相当于我们在 InfluxDB 里面设置 CRIT 的阈值,此处我们设置为,如果过去 15 分钟的可用率不是 100%,那么就认为已经发生严重错误。

MMSIZE

如图所示,再次点击右下角的下一步。

这一步叫做警告条件,相当于 InfluxDB 中的 warn。此处我们直接点击蓝色的按钮,从严重条件复制相同条件,然后再右下角点击保存。

MMSIZE

最后,一切正常的话,我们的报警规则列表中就会多出一条,如下图所示。

MSIZE

3)配置报警行为

点击左侧的报警行为按钮,然后点击+号,创建一个新的报警行为。

MMSIZE

在弹出的窗口上先选好行为类型,此处我们选择 Webhook。可以看到这里需要一个 URL,它也相当于我们要向外发送一个请求。所以这个 URL 指定谁,其实还是应该对接到睿象云来做处理。

MMSIZE

回到智能告警平台的集成页面,在集成工具中找到睿象云。点一下创建。

MMSIZE

先命好名,然后直接点击下方的保存并获取应用 key。

MMSIZE

可以看到配置说明上的 url 自动补全了一个随机字符串,这个就是 key。现在将它复制一下。

回到创建报警行为的窗口中,填写 URL,再点击测试,如果测试结果显示为connect success,那就说明我们的配置是正确的。点击右下角的保存。

MMSIZE

4)修改分派策略

现在我们的告警平台中,已经创建了两个应用了。但是直接我们之前创建过一个分派策略,它会将 REST API 收到的报警通知全部转发给某个用户。但是我们刚才创建的Webhook 还尚未包含在这个分派策略中。

所以此处,找到我们之前设置的分派策略,点击右边的编辑按钮。

MSIZE

MSIZE

进入编辑页面后,点击图中指出的添加按钮。

可以看到,此处我们就会展示出来两个应用,也就是这两个接口收到的报警通知都会转发给我们指定的用户。

MSIZE

最后点击保存,分派策略就修改成功了。

5)创建报警策略

  1. 回到之间的监控平台,告警管理页面。首先点击左侧的报警策略按钮,再点击+ 号,创建一个报警策略。

MMSIZE

  1. 触发策略的配置如下图所示。

    MSIZE

  2. 点击上方的触发行为设置按钮,然后点击右侧的添加按钮。

MSIZE

  1. 可以看到有一个可以进行选择的选项卡,这是我们之前创建的报警行为,鼠标点一下将它选中,然后点击右下角的选择按钮。

    MSIZE

  2. 如果报警行为成功添加,就点击右下角的保存按钮。至此我们的报警策略就搭建成功了。

    MSIZE

猜你喜欢

转载自blog.csdn.net/qq_44766883/article/details/131586277