じゅころぐAR

ARのブログ

Crystal Signal Piで光る通知システムを作る

ARもAIも関係なく、AWSとラズパイの話。

AWSの監視したいけど、メール見落としたら意味ないよね。
ラズパイとかでパトランプ回してる人、いそうじゃない?

という訳で、ググってみた。

www.infiniteloop.co.jp

www.youtube.com

そうそう、こういうやつ・・・なっ!?
なにこれ!カッコいい!!
Amazonで売ってるの? 5,000円?

ポチりました。

お急ぎ便にしてないけど、2日で届いた。

組み立て

基板のはんだ付けが終わっている通常版を買ったので、ラズパイ本体(別売り)にネジ止めするだけです。

組み立ては5分くらいで終わりました。

f:id:jyuko49:20181119013514j:plain

試しにラズパイを電源につないでみると、クリスタルが緑色に!

思った以上にカッコいい!!
なんとなく部屋を暗くしてしまう・・・。

Crystal Signal Piの設定

公式のマニュアル(PDF)の手順の通りに実行しました。

設定が終わったら、ブラウザからラズパイのIPアドレスにアクセスして、Web UIを表示します。

f:id:jyuko49:20181119012112p:plain

アラート実行を試すと、色がRGB値で自由に変更できて点滅のパターンも指定できます。
API経由で叩く場合は、GETパラメータで渡す感じです。
http://192.168.0.8/ctrl/?color=200,0,248&mode=1&repeat=0&period=1000&json=1

リモートで叩けるようにする

AWSやSlackから通知を受けるには、グローバルでアクセス可能なホスト名かIPアドレスが必要になります。

方法は色々ありますが、今回はこちらの記事を参考にルーターの設定とNo-IPで実現しました。
raspberry.mirukome.com

ラズパイのサーバプロセスは既に立ち上がっているので、Apacheのインストールは不要です。

  • No-IPクライアントをラズパイにインストール
  • ラズパイのプライベートIP(192.168.0.XXX)を固定
  • ルーターでラズパイへのポート:80を開放

を行えば、No-IPで作成したホスト名→ルーターグローバルIP→ラズパイのプライベートIPでルーティングされます。

No-IPのインストールにはサインアップが必要で、こちらから作成しています。
無償のホスト名は何もしないと30日で期限切れになってしまうため、定期的にconfirmが必要です。嫌なら有償版に、ってことですね。

疎通テストはWi-Fi接続を切ったiPhoneからNo-IPで作成したホスト名へのリクエストで実施できます。インターネット(4G回線)越しでも確かにつながりました。

AWSからの通知を設定する

AWSで障害が起きた際の通知を想定して、設定&テストしてみました。

f:id:jyuko49:20181120220545p:plain

  • CloudWatchでアラームを作成してSNSに通知
  • LambdaファンクションでSNSトピックをサブスクライブ
  • LambdaファンクションからラズパイにGETでアラート送信

詳細は割愛しますが、CloudWatchのアラームをSlack通知する手順とほぼ同じです。ググるとたくさん出てきます。
Lambdaファンクションを変更して、AWSからラズパイにアラートを投げるようにしました。

'use strict';

const AWS = require('aws-sdk');
const url = require('url');
const http = require('http');

const hookUrl = 'No-IPで作ったホスト名';

function sendAlert(state, callback) {
   
    const options = url.parse(hookUrl);
    if (state === 'ALARM') {
        options.path = '/ctrl/?color=255,0,0&mode=1&repeat=20&period=500&json=1';
    } else if (state === 'OK'){
        options.path = '/ctrl/?color=0,255,0&mode=0&repeat=1&period=500&json=1';
    } else {
        options.path = '/ctrl/?ack=1';
    }
    
    
    const alertReq = http.get(options, (res) => {
        const chunks = [];
        res.setEncoding('utf8');
        res.on('data', (chunk) => chunks.push(chunk));
        res.on('end', () => {
            if (callback) {
                callback({
                    body: chunks.join(''),
                    statusCode: res.statusCode,
                    statusMessage: res.statusMessage,
                });
            }
        });
        return res;
    });

    alertReq.end();
}

function processEvent(event, callback) {
    const message = JSON.parse(event.Records[0].Sns.Message);
    const newState = message.NewStateValue;

    sendAlert(newState, (response) => {
        if (response.statusCode < 400) {
            console.info('Send Alert is successfully');
            callback(null);
        } else if (response.statusCode < 500) {
            console.error(`Error send Alert: ${response.statusCode} - ${response.statusMessage}`);
            callback(null);
        } else {
            // Let Lambda retry
            callback(`Server error when send Alert: ${response.statusCode} - ${response.statusMessage}`);
        }
    });
}


exports.handler = (event, context, callback) => {
    if (hookUrl) {
        processEvent(event, callback);
    } else {
        callback('Hook URL has not been set.');
    }
};
  • 通知された状態がアラームなら、クリスタルが赤く点滅
  • 通知された状態がOKなら、クリスタルを緑に点灯

通知をテストする

監視対象にしているEC2のApacheを停止して応答不可にします。
LBのTarget Groupがヘルスチェックに失敗して、CloudWatchがアラートを上げる仕組み。

まとめ

当初の目的だったアラートに気付きやすくという点では、目に入るところに置いておけば、ほぼ間違いなく気付きます。
少なくとも、メールやSlackよりは見落としにくいです。

あえて欠点を挙げるなら、アラート時じゃなくても光らせたくなってしまう点でしょうか。
頻繁に光らせたいなら、Slack通知をWebhookさせた方が楽しそうです。