前置き
自宅サーバー使ってますか?
最近もう流行らないかな、と思ったらRaspberry Piの流行で機器も電気代も安価に組めるようになったりして、地味に持っている人も多いんじゃないかな、と思ってます。
自宅サーバーはいつの間にか死にがちですよね。
死んだら教えてくれよ!って思うんですが、死人には口がないので教えてくれない。
リッチな環境なら複数台で相互監視すればいいですが、複数台も持ってねぇよ、って人。
Google Apps Script(GAS)に監視させる、という手があります。
他にもいろんなクラウドサービスはありますが、GASはGoogleアカウントを持ってればだいたい誰でも使えるので敷居が低いです。
概要
さて監視と言っても、GASに自作サーバーをポーリングさせるのは得策ではありません。自宅サーバーを外部から見える位置に公開しないといけなくなるので。
そうじゃなくて、自宅サーバー側からGASに定期的に信号を送ってやって、信号が途絶えたら死んだ、という方式を取ります。いわゆるハートビートってやつですね。
できあがり
GAS側
GASは”doPost”という関数を作ることで、POSTを受け取るWeb APIを置くことができます。
「公開」から「ウェブアプリケーションとして導入」を選ぶと公開できます。
これは通常認証を必要としますが、どうせURLなんかバレやしないので、認証不要になるよう”Anyone, even anonymous”で公開してやりましょう。
function doPost(e) {
// POSTデータ確認
if (e == null || e.postData == null || e.postData.contents == null) {
Logger.log("POST ERROR");
return ContentService.createTextOutput("NG").setMimeType(ContentService.MimeType.TEXT);
}
// JSONパース
var requestJSON = e.postData.contents;
var requestObj = JSON.parse(requestJSON);
// リクエスト解析
if (requestObj.triggerDateText == null) {
Logger.log("JSON ERROR");
return ContentService.createTextOutput("NG").setMimeType(ContentService.MimeType.TEXT);
}
// トリガーを設定
var triggerDate = new Date(requestObj.triggerDateText);
const triggers = ScriptApp.getProjectTriggers();
for(const trigger of triggers){
if(trigger.getHandlerFunction() == "checkTimer"){
ScriptApp.deleteTrigger(trigger);
}
}
ScriptApp.newTrigger('sendAlertMail').timeBased().at(triggerDate).create();
return ContentService.createTextOutput("OK").setMimeType(ContentService.MimeType.TEXT);
}
function sendAlertMail() {
// アラートメール送信
GmailApp.sendEmail('myAddress@gmail.com', 'サーバ死んだよ');
}
自宅サーバー側
Linuxを想定してシェルコマンドです。好きな言語で書いてください。
URLは自分のGASで公開したAPIのものに書き換えて。
このコマンドをcronなどで5分おきに実行し続けます。10分間止まるとトリガーが発動してメールが飛びます。
$ curl -L -X POST -d "{\"triggerDateText\":$(date "+%s000" -d "10 minutes")}" -sS "https://script.google.com/macros/s/AKfycbXxxxxxxxxxxxxxxxxxxx/exec"
解説
ほぼ書いてある通りです。
GASはスクリプトを定期実行するためにトリガーという仕組みがあるんですが、これは任意の日時を指定して発動できます。
また、スクリプト自身からトリガーを設定できます。
そのため、「POSTを受け取ってトリガーを再設定するスクリプト」を作ることで、「この時刻までに次の信号が来なかったらメールを出す」という機能を実現できます。
処理の流れは以下のような感じ。
- クライアントがn分おきに”メールを出す時刻”をGASにPOSTし続ける
- GASはクライアントから時刻を受け取って(doPost関数)、既存のトリガーを消して新しいトリガーに更新し続ける。
- クライアントからの通信が途絶えるとトリガーが発動して、自分のメールアドレス向けにアラートが飛ぶ(sendAlertMail関数)
送信する側もAPIにPOSTさえ出来れば良いのでcurlコマンドで十分ですし、任意の言語で書けば良いです。ただ、リダイレクトを適切に処理する必要があるので気をつけてください。
また、GAS側はNGだのOKだのを返すように見えますが、どうも上手く動かないようなので気をつけて。
蛇足1 ログを取る
スクリプト的には以上ですが、もっと色々ログを取りたいならスプレッドシートと組み合わせるという手があります。
そもそもスプレッドシート上のスクリプトにしてしまうことができますし、上のコードもJSONでやりとりするように書いているので、JSONに情報を増やせば好きなものを記録できます。
蛇足2 サーバーをタイマ起動させて監視
実は自分は上記の使い方をしてません。
録画鯖を番組開始前にタイマ起動するように仕込んでいて、タイマ起動に失敗したらアラートを上げるようにしています。常時起動させると電気代がアレなので。
このタイマ起動にはLinuxのwakealermというのを使っていて、/sys/class/rtc/rtc0/wakealarmにUNIXTIMEを入れてシャットダウンすれば自動で上がってきます。
GASのトリガーは「起動時間の10分後」に設定していて、10分経って上がってこなかったらアラートが上がるようにしています。
色々使いみちがあるよ、という話でした。