プログラミング

Google Apps Script(GAS)で自宅サーバーの死活監視をする

投稿日:

前置き

自宅サーバー使ってますか?
最近もう流行らないかな、と思ったら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('[email protected]', 'サーバ死んだよ');
}

自宅サーバー側

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を受け取ってトリガーを再設定するスクリプト」を作ることで、「この時刻までに次の信号が来なかったらメールを出す」という機能を実現できます。
処理の流れは以下のような感じ。

  1. クライアントがn分おきに”メールを出す時刻”をGASにPOSTし続ける
  2. GASはクライアントから時刻を受け取って(doPost関数)、既存のトリガーを消して新しいトリガーに更新し続ける。
  3. クライアントからの通信が途絶えるとトリガーが発動して、自分のメールアドレス向けにアラートが飛ぶ(sendAlertMail関数)

送信する側もAPIにPOSTさえ出来れば良いのでcurlコマンドで十分ですし、任意の言語で書けば良いです。ただ、リダイレクトを適切に処理する必要があるので気をつけてください。
また、GAS側はNGだのOKだのを返すように見えますが、どうも上手く動かないようなので気をつけて。

蛇足1 ログを取る

スクリプト的には以上ですが、もっと色々ログを取りたいならスプレッドシートと組み合わせるという手があります。
そもそもスプレッドシート上のスクリプトにしてしまうことができますし、上のコードもJSONでやりとりするように書いているので、JSONに情報を増やせば好きなものを記録できます。

蛇足2 サーバーをタイマ起動させて監視

実は自分は上記の使い方をしてません。
録画鯖を番組開始前にタイマ起動するように仕込んでいて、タイマ起動に失敗したらアラートを上げるようにしています。常時起動させると電気代がアレなので。
このタイマ起動にはLinuxのwakealermというのを使っていて、/sys/class/rtc/rtc0/wakealarmにUNIXTIMEを入れてシャットダウンすれば自動で上がってきます。
GASのトリガーは「起動時間の10分後」に設定していて、10分経って上がってこなかったらアラートが上がるようにしています。
色々使いみちがあるよ、という話でした。

-プログラミング

執筆者:

関連記事

no image

M5StickC Plusの明るさ(M5.Axp.ScreenBreath)、0~100かもしれない

M5StickC Plusで画面の明るさを調整するM5.Axp.ScreenBreath()という関数があります。 日本語リファレンスでは指定値7~12ということになっています。 また、公式ドキュメン …

GeoJSONで市町村境界をマージして都道府県境界にしたい(その2)

GeoJSONのPolygonをマージしたい第2回です。 前回、純粋な多角形の統合ではなくて、領域が被らない多角形の統合になるのでグラフ問題として解くことができるという説明をしました。 今回はどうやっ …

GeoJSONで市町村境界をマージして都道府県境界にしたい(実践編)

前々回と前回で問題を整理して、ようやく実践編です。 まず実物のリンク貼りましょう。GitHubに上げました。 今回はGo言語で書いてますが、ポイントがいくつかあります。 ちなみに言語としてGoを選択し …

no image

ランダムな日本人データを生成するツールをつくった

前置き ダミーの名簿データが作りたい、という需要は稀にあるものです。 住所とか、年齢とか、職業とか、いい感じにランダムなデータが欲しいことがありますよね。 しかし、適当にデータを作ってしまうと、「東京 …

YouTube Data APIをGoogle Apps Script(GAS)から使おう

YouTubeってAPIから色々な情報を取ることができるんですよ。 APIの情報はリファレンスにまとまってるんですが、APIキーだのOAuth2.0だの、使い始めるまでがまぁまぁ面倒なんですね。 で、 …