服务器监控工具挺多的,我也用过好多个,体验最好的是Nixstats,然而后来它修改了免费版的策略,只支持一个服务器,导致我不得不迁移,目前在用的是HetrixTools,功能比较全,价格也很良心,值得推荐。
HetrixTools支持Email、SMS、Telegram、PushBullet等通知渠道,可以说是非常全面了,然而这些几乎都是国外的服务。呃我知道这个需求很扭曲,但是如果你的某些设备不太方便访问这些服务,或者你某个服务的受众在墙内,那么搭建一个墙内的通知渠道就有必要了。由于缺乏基础知识,本菜鸡研究了很久才搞定这一套,这里记录一下。代码嘛我就随便写写,你们凑合看,主要看方法就好。
实现逻辑是:HetrixTools报警 → 自建webhook服务 → 触发运行脚本 → 使用API推送到微信
首先,你需要自己搭建一个webhook服务,比如说官方文档给了一个php的示例,然而这个示例只有解析,照搬是没有效果的。我从来没有写过php,东拼西凑糊了一个(<>括起来的内容自行替换,下同):
<?php
// 出于安全考虑限制了来源IP,一个是HetrixTools的,一个是本机调试用,你也可以不限IP用token,但是最好别裸奔
$valid_ip = array('144.217.15.240','127.0.0.1');
$client_ip = $_SERVER['REMOTE_ADDR'];
if (!in_array($client_ip, $valid_ip)) die('IP does not match!');
// 测试用的,看php运行是否正常,没问题就可以注释掉了
//echo("hello\n");
// 获取JSON
$json = file_get_contents('php://input');
// 解析JSON,示例中有很多项,但是uptime和resource usage中共同的项只有这几个,其实我的脚本中只有timestamp有用
$array = json_decode($json,true);
$monitor_id = $array['monitor_id'];
$monitor_name = $array['monitor_name'];
$timestamp = $array['timestamp'];
// 转换timestamp,$time读起来方便,$timestring是命名文件用的
date_default_timezone_set('Asia/Shanghai');
$time = date('Y-m-d H:i:s', $timestamp);
$timestring = date('YmdHis', $timestamp);
// 写入文件,$base是储存目录(斜杠结尾),uptime.json是最新状态,带时间戳的是归档用的
$base = '<PATH>';
$archive = $base . $timestring . '.json';
$uptime = $base . 'uptime.json';
$content = $json;
file_put_contents($archive,$content);
file_put_contents($uptime,$content);
// 运行脚本
exec('/usr/bin/bash <SCRIPT>');
?>
在HetrixTools的Contact List里填入这个php脚本的地址,之后每次报警,就会把一个JSON喂给这个php,然后php把内容写入文件,交给Shell脚本处理。
为了避免重复通知(根据你的设置,HetrixTools可能会反复发送警报,但是对于客户来说持续的事件通知一次就够了),我建了一个status.txt,格式是:
server1: online
server2: online
...
server1、server2等是服务器的名字,和HetrixTools中一一对应,冒号后面online/offline是在线状态,只有当状态改变时,才推送通知。
Shell脚本如下(需要安装jq,用来解析JSON):
#!/bin/bash
# 目录
basedir='<PATH>'
# 服务器名
server=`cat "$basedir/uptime.json" | jq -r '.monitor_name'`
# 服务器状态
original_status=`grep "$server" "$basedir/status.txt" | awk -F": " '{print $2}'`
current_status=`cat "$basedir/uptime.json" | jq -r '.monitor_status'`
# 服务器离线/恢复的提示内容,如果多行用\n隔开,直接换行也不是不行,但是发送的时候会报一个wrong json format的warning
# 如果通知类型是resource usage,是没有minitor_status项的,为了避免误报加了一个判定
if [[ $current_status != 'null' && $current_status != $original_status ]]; then
if [[ $current_status == 'offline' ]]; then
sed -i "s/$server: online/$server: offline/" "$basedir/status.txt"
message="Server down!"
elif [[ $current_status == 'online' ]]; then
sed -i "s/$server: offline/$server: online/" "$basedir/status.txt"
message="Server up!"
fi
fi
# <LIST>是监控的服务器名,写成字符串,中间空格连接就行,不在里面的服务器只会修改status.txt,不会发送通知
server_list="<LIST>"
if [[ ! "$server_list" =~ "$server" ]]; then
exit 0
fi
# API信息
corpid="<CORPID>"
party="<PARTYID>"
agentid="<AGENTID>"
corpsecret="<SECRET>"
# 生成token
token_json=`curl "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$corpid&corpsecret=$corpsecret"`
access_token=`echo $token_json | jq -r '.access_token'`
# 推送文字消息
body=`cat << EOF
{
"toparty" : "$party",
"msgtype" : "text",
"agentid" : "$agentid",
"text" : {
"content" : "$message"
},
"safe":0,
"enable_id_trans": 0,
"enable_duplicate_check": 0,
"duplicate_check_interval": 1800
}
EOF
`
curl https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=$access_token \
-H 'Content-Type: application/json' \
-d "$body"
这里发消息用的是企业微信的API,可以发到微信,搭建方法请自行搜索,也可以换成钉钉、Server酱等。这里有个坑就是exec()运行的路径是php文件所在的目录,而不是Shell脚本所在的目录,我调试的时候没注意,出现了终端执行正常,webhook执行不正常的情况- -所以如果你的这些文件在别的地方(为了安全最好放在别的地方,或者写伪静态屏蔽掉),Shell脚本里一定要用绝对路径。另一个坑是php用户要有相关文件和目录的权限。resource usage我没想到好的办法处理,因为没有警报解除的通知,暂时搁置了。
文章评论