背景
在 grafana+ prometheus+php 监控系统实践文章当中已经实现了我们的第一个监控图表, 现在我们有了一个新需求, 需要对多个节点实现不同的监控, 以及一个汇总的监控;
按照我们之前手动创建仪表盘的方法, 每新增一个节点都需要手动去修改 prometheus 配置, 并且需要去 grafana 系统当中创建一个仪表盘, 在节点很少的时候这种方式也能满足, 但当节点数量多起来的时候, 就会增加很大一部分工作量, 并且存在每次创建的图表规则不一致的风险, 因此我们的需求是在新增节点之后让 grafana 自动创建一个仪表盘.
实现过程
操作步骤
prometheus 调用中间件
中间件收集各节点数据
验证中间件数据有效性
调试仪表盘 API 接口
编写节点变动事件处理
一, prometheus 调用中间件
1. 修改 prometheus 配置文件
修改配置文件的目的是把之前直接连接单个节点的地址更改为中间件地址, 之前单节点的 uri 为'==/api/v1/rrd/metrics==', 现在则需要将其修改为中间件地址'==/api/v1/rrd/toolSpool==', 因此新的配置文件内容如下:
- ---
- global:
- scrape_interval: 5s
- scrape_timeout: 3s
- scrape_configs:
- - job_name: 'mysql'
- scrape_interval: 5s
- static_configs:
- - targets: ['192.168.43.34:9104']
- labels:
- instance: 192.168.43.34
- - job_name: 'media'
- scrape_interval: 3s
- metrics_path: "/api/v1/rrd/toolSpool"
- static_configs:
- - targets: ['gslb.offcncloud.com:8080']
1.2 重新启动 prometheus
当修改完配置文件之后, 还需要让其配置文件生效, 所以需要将 prometheus 重新启动.
docker 重启
docker 本身就提供重启命令, 所以只需要输入如下命令就可以了, 注意后面是容器的名称.
docker restart prometheus
mac 下重启
mac 下重启比较简单, 首先终止之前的任务, 然后使用启动命令
prometheus --config.file=/tmp/prometheus.yml
二, 中间件收集各节点数据
2.1 目的
中间件的作用是将各个节点的数据进行汇总, 然后一次性返回给 prometheus, 实现这个中间件的方式有很多种, 在实现之前我们也查找了一些资料, 比如有网友用 consul-template+consul 方式来实现, 可参考下方链接; 不过我觉得这个配置好像也不简单, 而这个中间件的功能还挺简单的, 所以还是自己使用 PHP 写了一个中间件.
参考资料:
http://blog.51cto.com/xujpxm/1964878
2.2 中间件需要的效果:
拉取所有节点数据, 这是核心作用
给各节点加标示, 将来用来区分是哪个节点的
汇总并输出, 需要一个汇总的仪表盘
2.3 获取节点数据, 并添加标示
要获得各个节点的数据, name 首先获就得取到所有的节点列表, 然后通过节点的 IP 地址来拼接 URL, 最终通过 curl 请求该地址来得到节点数据;
在获得数据后, 我们还小需要给每一个节点返回的数据加上标示可以用 {} 包括起来, 因为 prometheus 支持这种格式, 伪代码如下:
- <?php
- public function getNodeListAttributesInfo(Request $req)
- {
- //1. 获取节点列表
- $nodelist = RrdToolModel::getNodeListAttributesInfo($req);
- //2. 遍历获取各节点数据
- $str = '';
- foreach ($nodelist as $name) {
- $url = "http://{$name->ip}/api/v1/rrd/metrics";
- //3. 获取数据
- $tmp = file_get_contents($url);
- //4. 在每个节点中插入 host 属性, 到时候用来做筛选单个节点
- $tmp = str_replace(''," {host=\"{$name->ip}\"} ", $tmp);
- $str .= $tmp;
- }
2.4 汇总并输出
我们的核心需求是需要看到所有节点的汇总状况, 所以在获得各个节点的数据后还需要进行累加, prometheus 中貌似并直接不支持, 所以我们得在中间件总进线累加汇总.
- // 限制需要进行汇总统计, 首先把字符串分割为数组
- $arr = explode(PHP_EOL, $str);
- $tmpArr = [];
- // 遍历数组
- foreach ($arr as $val) {
- // 把每一行再次分割
- $valArr = explode(" ", $val);
- //5. 汇总统计
- if (!empty($valArr[0]) && is_string($valArr[0]) && is_numeric($valArr[2])) {
- $tmpArr[$valArr[0]] = isset($tmpArr[$valArr[0]]) ? ($tmpArr[$valArr[0]] + $valArr[2]) : $valArr[2];
- }
- }
- //6. 汇总输出
- foreach ($tmpArr as $key => $num) {
- echo "{$key}_total $num" . PHP_EOL;
- }
- echo $str;
- }
2.5 输出最后结果
当中间件处理完成之后, 我们需要各个节点的数据, 并有在数据中需要有节点的标示, 另外还需要一个汇总的数据, 因此中间件返回数据如下:
- media_connectNum_total 0
- media_network_total 0
- media_on_push_total 2
- media_connectNum {host="192.168.43.46:8080"} 0
- media_network {host="192.168.43.46:8080"} 0
- media_on_push {host="192.168.43.46:8080"} 1
- media_connectNum {host="127.0.0.1:8080"} 0
- media_network {host="127.0.0.1:8080"} 0
- media_on_push {host="127.0.0.1:8080"} 1
三, 验证中间件数据有效性
现在我们的 prometheus 已经启动, 并且中间件也正常运行, 那么此时 prometheus 和 Grafana 应该都有相应变化, 我们可以根据这写变化来确定我们前面的处理是否成功.
3.1 prometheus 数据验证
当我们的配置文件和中间件发生变化后, 最先产生相应变化的应该是数据仓库, 所以我们可以打开打开 prometheus 的 web 界面, URL 地址( http://192.168.43.34/ :9090/graph)
在筛选中输入 media_network, 然后进行筛选, 如果能看到多个返回的记录, 则说明验证成功, 如下图所示.
3.2 Grafana 数据验证
当 prometheus 数据仓库的数据发生变化后, grafana 的仪表盘也应该会发生变化, 最明显的变化如下图所示, 标签都变成了双份, 比如之前的一份 "拥堵拉流数量" 变成了双份.
原因多个节点返回了多份数据, 而我们使用 Grafana 绘图的时候筛选项只输入了其中的 key 部分, 并没有筛选里面的属性, 因此有多少个节点就会有出来多少个项, 如果数量对上了, 说明 Grafana 也验证成功了.
3.3 设置汇总图
现在我们把之前的仪表盘, 重新编辑一下, 把之前只筛选了 key 改成筛选 key + 上属性, 设置方式如下图
设置好之后, 我们看到的将是汇总的仪表盘, 至此我们第一个的核心需求已经实现了
3.4 设置节点模板
在设置汇总图后, 我们还将要实现第二个核心需求, 自动化创建单节点的仪表盘, 我们首先需要手动先创建一个单个节点的图, 和第一篇文章的创建方法一致, 在设置筛选项的时候, 我们填写的内容要带上属性, 属性的作用可以筛选节点, 如下图:
四, 调试仪表盘 API 接口
API 官方文档 URL: http://docs.grafana.org/http_api/dashboard/
4.1 创建 API 接口
我们的目标是当新增节点时 grafana 能够自动创建相应的仪表盘, 因此需要使用到 grafana 的 API 接口, 使用之前需要先创建一个密钥用来授权, 创建的流程如下图:
添加一个 api, 在 keyname 中随便填写一个名字, 然后 role 选择 admin 权限, 点击添加按钮
当创建成功能看到 grafana 页面弹框提示, 我们需要把他先复制下来放到一个位置, 因为后面是看不见这个 key 的, 如下命令:
使用终端进行访问测试, 如果返回结果如下, 则代表这个 key 可以使用
4.2 使用 postman 调试
现在不要急着取用 PHP 进行调试, 可以先用 Postman 进行调试, 我们需要调试的并不是刚才弹框上面的 URL 地址, 而是创建一个仪表盘的地址, 在官方文档中的请求信息如下:
- POST /api/dashboards/db HTTP/1.1
- Accept: application/json
- Content-Type: application/json
- Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
- {
- "dashboard": {
- "id": null,
- "uid": null,
- "title": "Production Overview",
- "tags": [ "templated" ],
- "timezone": "browser",
- "schemaVersion": 16,
- "version": 0
- },
- "folderId": 0,
- "overwrite": false
- }
使用 postman 请求截图
- {
- "id": 23,
- "slug": "production-overview",
- "status": "success",
- "uid": "ID2FFcciz",
- "url": "/d/ID2FFcciz/production-overview",
- "version": 1
- }
当返回如上结果, 则说明已经创建成功了
4.3 导出模板
现在我们需要导出之前创建的一个节点仪表盘, 用来做模板, 导出仪表盘的配置方法比较简单,
把上面的 json 数据保存到 grafana.json 文件中, 在保存 json 文件的时候需要注意, 导出来的 json 配置并不能直接使用, 因为 prometheus 创建仪表盘的 json 格式并不是这样的, 我们需要对这份 json 内容稍微处理一下, 在其前后分别加上一些字符, 效果如下
{
"dashboard":
------- 导出 json 的内容放中间 --------
- ,
- "overwrite": false
- }
注意:, 既然他是模板文件, 里面肯定有些东西是变化的, 比如说他的 title, 和 host 属性, 因此我们得在模板里面做一个标示, 比如 title 部分我们可以用 ###title### , 方便后面的文本替换, 具体可参考我的配置文件 http://tuchuang.songboy.net/grafana/v2/grafana.json
保存之后, 也可以拿这个 json 的内容用 postman 进行验证, 使用 postman 能够正常添加后, 我们再使用 PHP 的 curl 去实现
4.4 编写 PHP 发起请求代码
现在已经确保我们的 json 数据没有问题, 所以现在使用 PHP 的 curl 来创建仪表盘, 伪代码如下:
- /**
- * 通过 curl 获取数据
- * @param $url
- * @param bool $isHearder
- * @param bool $post
- * @return mixed
- */
- function http_request($url, $isHearder = null, $post = 'GET', $data = null, $timeout = 1)
- {
- // 初始化 curl
- $ch = curl_init($url);
- // 设置 URL 地址
- curl_setopt($ch, CURLOPT_URL, $url);
- // 设置 header 信息
- if (!empty($isHearder)) {
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $isHearder);
- }
- // 如果是 post, 则把 data 的数据传递过去
- if (($post == 'POST') && $data) {
- #假如 data 为数组将其转换为 json 格式
- if (is_array($data)) {
- $data = json_encode($data);
- }
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- }
- // 如果是删除方法, 则是以 delete 请求
- if ($post == 'DELETE') {
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
- }
- // 设置超时时间, 毫秒
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, $timeout*1000);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- // 执行 CURL 时间
- $result = curl_exec($ch);
- // 如果有异常, 记录到日志当中
- $curl_errno = curl_errno($ch);
- if ($curl_errno> 0) {
- LogModel::addlog('超时');
- }
- // 关闭 URL, 返回数据
- curl_close($ch);
- return $result;
- }
五, 编写节点变动事件处理
当上面的操作都完成之后, 我们前期的基本工作已经完成了, 现在需要做得事情是创建节点的时候调用 PHP 来发起请求
5.1 新增节点触发
通过 api 来创建仪表盘的部分伪代码, prometheus 的仪表盘中有一个 uid 的 key, 这个 key 可以由我们自己控制, 必须是保证他的唯一性(如果把 json 模板中的 uid 项设置为 null,prometheus 会自动为你生成一个);
我们可以使用节点 IP 地址的 hash 值作为他的 uid, 这样我们将来在变更仪表盘的时候只要有 ip 就能得到 uid, 而无需再次存储一份, 如下面的伪代码:
- /**
- * 创建图表
- * @param $str
- * @param array $params
- * @return \Illuminate\Http\JsonResponse
- */
- public function replaceNodeInfo($ip)
- {
- // 接收节点触发事件
- $uid = md5($ip);
- // 设置 head 头, 认证信息
- $header = array(
- "Content-Type:application/json",
- 'Authorization: Bearer eyJrIjoicnhTMklodFMzaDRsUXFoUFFiZ2tSRnQ3TnI4WEVqQlEiLCJuIjoidGFuZ3Fpbmdzb25nIiwiaWQiOjF9'
- );
- // 读取 json 模板
- $jsonstr = $this->readJsonData();
- // 替换模板中需要替换的位置
- $jsonstr = str_replace('###node###', $ip, $jsonstr);
- $jsonstr = str_replace('###uid###', $uid, $jsonstr);
- // 进行 curl 请求
- http_request('http://192.168.43.34:3000/api/dashboards/db', $header, 'POST', $jsonstr);
- }
5.2 检查效果
当使用 PHP 的 curl 请求后, 我们可以在 grafana 的仪表盘管理界面看到使用 PHP 创建的图表, 当出现下图的效果则代表成功:
5.3 删除节点触发
删除节点的时候, 我们对应的仪表盘也没用了作用, 因此我们也要删除对应的仪表盘, 前面我们生产的 uid 是 ip 地址的 hash 值, 因此我们删除的时候也可以取 ip 对应的 hash 值, 通过这个 uid 来删除仪表盘, 如下伪代码:
- /**
- * 删除 node 节点视图信息
- * @param Request $req
- */
- public function delNodeViewInfo(Request $req)
- {
- // 接收参数
- $params = $req->all();
- $ip = $params['ip'];
- $uid = md5($ip);
- // 设置认证信息
- $header = array(
- "Content-Type:application/json",
- 'Authorization: Bearer eyJrIjoicnhTMklodFMzaDRsUXFoUFFiZ2tSRnQ3TnI4WEVqQlEiLCJuIjoidGFuZ3Fpbmdzb25nIiwiaWQiOjF9'
- );
- // 执行删除事件
- http_request("http://192.168.43.34:3000/api/dashboards/uid/{$uid}", $header, 'DELETE');
- }
来源: https://segmentfault.com/a/1190000015922077