Try to monitor alive or not by NodeJS
目次
経緯
家のネットワーク機器が増えてきたので、簡単な監視をしたい。
やりたいこと
家のありとあらゆるIPを持つ機械を監視したい。
できればNodeJSで。
構築
てことで、やってみる。
graph TD
a((start))-->|monitoring| c{is dead?}
c-->|dead| d{has timeout counter exceeded threshold? }
d-->|exceeded| g{has message already sent?}
d-->|not yet| j[counter up]
j-->a
c-->|alive| f[counter reset]
f-->h{has message already sent?}
g-->|yep| a
g-->|nop| i[send message]
h-->|yep| i
h-->|nop| a
i-->a
こんな感じで、違反のしきい値をもたせておいて、違反した回数がしきい値を超えればメッセージを送信する。
途中で生きていることを確認できたら、またカウンタをリセットしてまた再開する。
しきい値オーバーのとき正常に戻ったとき、それぞれ1回ずつ通知することとして通知先はLINEにしてみた。
結構シンプルだったのと手軽にできるのがよかったので、本当はGoogleChatとかでも良かったのかもしれない。
$ cd /srv
$ sudo mkdir serverstat && sudo chown ME:ME serverstat # ME: your name
$ cd serverstat
$ npm init -y
$ sed -i -e 's/"main":.*/"main": "monitor.js"/' package.json
$ npm i fs axios form-data ping --save
$
今回はaxiosというHTTPクライアントを使ってLINE Notification APIを叩いている。
//filename: monitor.js
/**
*
* simple alive monitoring
*
*/
const fs = require('fs');
const axios = require('axios');
const formdata = require('form-data');
const ping = require('ping');
const cred = require('./credentials');
const monitor = require('./servers');
/**
* required variables for credentials
* file: credentials.js
* imagedir: the directory where required image files exist
* token: Token for LINE Notification API
* url: Access URI for LINE Notification API
*/
/**
* for monitored nodes
* file: servers.js
*
* servers = [
* {
* host:'192.168.1.1', //host address [editable]
* threshold: 1, //counter triggered if 'count' exceeded the value [editable]
* count: 0, //icmp req-timed-out counter
* status: false, //current status (true:alive|false:dead)
* needtosend: false, //boolean trigger for sending message
* hasdead: false //whether the host has already dead (true:already dead|false:still alive)
* },
* {
* ...
* }, ...
* ]
*/
let servers=monitor.servers;
let timer1=monitor.interval; //interval of ping execution
let imageArray=['alive.jpg','dead.jpg']; //0:alive, 1:dead
let sendMessage=async (msg, imagefile)=>{
let message='テストメッセージ';
let imageFile=imageArray[0];
if(msg) message=msg;
if(imagefile) imageFile=imagefile;
let form=new formdata();
form.append('imageFile', fs.createReadStream(cred.imagedir + '/' + imageFile));
form.append('message', message);
let headers = {
'Authorization': 'Bearer ' + cred.token,
...form.getHeaders(),
}
try {
const resp = await axios.post(cred.url, form, {headers});
console.log(resp.data);
} catch (error) {
console.error(error);
}
}
let serverStat=()=>{
servers.forEach((server)=>{
//validation
ping.sys.probe(server.host, async (isAlive)=>{
let msg='';
let imageindex=0;
//dead or alive
if(!isAlive){
//is dead
server.count++;
server.status=false;
if(!server.hasdead&&server.count>=server.threshold){
msg='host:[' + server.host + '] is dead as a reason of request timeout'
server.needtosend=true;
server.hasdead=true;
imageindex++;
}else{
msg='';
}
}else{
//is alive
server.status=true;
if(server.hasdead&&server.count>=server.threshold){
msg='host:[' + server.host + '] is alive';
server.needtosend=true;
server.hasdead=false;
imageindex=imageindex*0;
}
server.count=server.count*0;
}
//sending message
console.log(server);
if(server.needtosend&&msg!==''){
await sendMessage(msg, imageArray[imageindex]);
server.needtosend=false;
}
});
});
}
let main=()=>{
let tid=setInterval(serverStat, timer1);
}
main();
こんな感じにしてみた。
このファイルと同じディレクトリにcredentials.jsを作って以下を参考に更新する。
//filename: credentials.js
exports.token='XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; //LINE notification API
exports.url='https://notify-api.line.me/api/notify'; //LIVE Notification API URI
exports.imagedir='/tmp';
LINE側の設定は適宜行っていただきたい。
あとは監視する機器の設定
//filename: servers.js
//modify below
let monitored_servers = [
{
host:'192.168.1.1', //host address [editable]
threshold: 1, //counter triggered if 'count' exceeded the value [editable]
},
{
host:'192.168.1.2',
threshold: 1,
},
{
host:'8.8.4.4',
threshold: 1,
}
];
let timer=5000; //interval for alive monitoring
//modify above
let props={ count: 0, status: false, needtosend: false, hasdead: false };
let servers=[];
monitored_servers.forEach(async (srv, i)=>{
servers[i]={
host: srv.host,
threshold: srv.threshold,
...props };
});
exports.servers=servers;
exports.interval=timer;
serversとtimerを適宜変更してほしい。
serversはhostとthresholdのみ変更できる。
timerはsetIntervalにそのまま渡しているのでmsecで設定する。
それではテストしてみる。
$ pwd
/srv/serverstat
$ node .
{ host: '192.168.1.1',
threshold: 1,
count: 0,
status: true,
needtosend: false,
hasdead: false }
{ host: '192.168.1.2',
count: 0,
threshold: 1,
status: true,
needtosend: false,
hasdead: false }
.
.
.
$
このように出ていたら、起動には問題はなさそう。
実際に監視しているノードを再起動なりをしてみて、ちゃんとreq timed outしたときにLINEに通知が来るか試してみる。
で、あとは起動。
$ forever start monitor.js
$ forever logs 3 # 3: id
.
.
.
.
.
data: monitor.js:12221 - { host: '192.168.1.1',
data: monitor.js:12221 - threshold: 1,
data: monitor.js:12221 - count: 0,
data: monitor.js:12221 - status: true,
data: monitor.js:12221 - needtosend: false,
data: monitor.js:12221 - hasdead: false }
$
大丈夫そうだ。
最後にファイル一覧。
$ pwd
/srv/serverstat
$ ls -F
alive.jpg credentials.js dead.jpg monitor.js node_modules/ package-lock.json package.json servers.js
$
User Experience
画面の見た目はこんな感じ。
