ついに本サイトもめでたく外形監視を行うようにしたので雑に手順を残しておきます。

下記の続きです。

動機 & やりかた


動機

明日(4/18)の日中にサーバメンテナンスでサイトがダウンするので 私は見届けたい…この目で確かに 見届けることができませんでした…(後述)

やりかた

外形監視は次のような感じにします。

  • AWS Lambdaで定期的にGETリクエストを投げる
  • ダウンしていた場合はAWS LambdaからAWS SNSのメッセージを発行する
  • メールが届く!!

という感じです。というわけで雑に手順を書いていきます。

Lambdaの環境について


現時点のAWS Lambdaで利用できる最新のNode.js v8.10.0でやってます。

コードを書く


もういきなり雑に完成したコードから書くとこんな感じになりました。

1
function getRequest() {
2
  const https = require('https');
3
4
  return new Promise((resolve, reject) => {
5
    https.get("https://yourdomain.example.com", (response) => {
6
      const { statusCode } = response;
7
      if (parseInt(statusCode) === 200) {
8
        resolve("Operational");
9
      } else {
10
        reject("Down!! " + statusCode);
11
      }
12
    }).on("error", (error) => {
13
      reject("Down!!\n\n" + error);
14
    });
15
  });
16
}
17
18
async function send(message) {
19
  const aws = require('aws-sdk');
20
  aws.config.update({region: 'ap-northeast-1'}); //リージョン指定します
21
  
22
  const params = {
23
    Message: message.toString(),
24
    TopicArn: 'arn:aws:sns:ap-northeast-1:12345678901234567890abcdefg' //SNSのトピックARNを指定
25
  };
26
27
  const publishTextPromise = new aws.SNS({apiVersion: '2010-03-31'}).publish(params).promise();
28
  publishTextPromise.then((data) => {
29
      console.log("Sent to SNS MessageID is " + data.MessageId);
30
    }).catch((err) => {
31
      console.error(err, err.stack);
32
    });
33
}
34
35
async function handle() {
36
  getRequest().then((s) => {
37
    console.log(s);
38
  }).catch((e) => {
39
    console.log(e);
40
    send(e);
41
  });
42
}
43
44
// AWS Lambda実行するエントリーポイント的なの
45
exports.handler = async (event, context, callback) => {
46
  callback(null, handle());
47
};

Node.jsのhttps.getで対象のURLにGETリクエスト投げてステータスコードでダウンしてるかどうか判定してます。

コードに記載している通り、ステータスコードが200以外だとダウンしているとみなすこととしました。3xxとか4xxを除外したのはトップが200以外で返ってくることは…まあ、ないので…。たぶん…。

ちょっと引っかかったのはGETリクエストから全部Promiseで返しまくってるので、AWS Lambda実行するエントリーポイント的なの(最後の匿名関数)でcallbackに実行する関数を放り込んでやらなければならなかったとこです。動いてるのでたぶんそういう理解であってると思います。

Lambdaの設定


定期実行

定期実行のやり方は前回書いた通りです。ただ、今回は15分に1回の実行にしました。どうせすぐ気づいたところで仕事中なのでどうしようもできないですし。まあ、ホビーサイトですしお寿司。

ロールの権限

実行ロールに対してSNSへのメッセージ発行権限を与えてやらないとSNSメッセージ発行時にエラーが発生します。

実行ロールへの権限付与は上図の最下部「IAMコンソールでxzyのロールを表示します」のとこからIAMに遷移できるのでそちらで設定すればよいです。

タイムアウト設定(追記)

え~、7秒に設定していたのですが、実際にサイトがダウンした際には7秒だと足りませんでした。サイトがダウンした時間帯のCloudWatchLogには下記のように制限時間をオーバーした旨のメッセージが表示されてました。もっと引き上げる必要がありました。

1
Task timed out after 7.01 seconds

実行してみる


存在しないドメイン入れてみたり200以外のステータスコードが返ってくるようなリクエストを投げてみたりしてメールが来るのを確認できました。これで明日も動くはず…。

CloudWatchのログも15分おきに出力されてます。

ちなみに、Outlookはやはりスパムフィルターレベルがイマイチでホワイトリストに入れないと迷惑メール扱いになります。MSさんもうちょっと頑張って欲しい…

感想


これが噂の「サーバレス」とかいうやつらしいですよ。たぶん。「思ったより大したことやってないんだなぁ」という感じです。もちろん、こんなホビーサイトじゃなくてプロダクションで使うならもっといろいろと制約があるとは思うのですが…。

これで明日(4/18)の日中にメールが届くはずです。なんとか間に合いました。ワクワクしますね。JavaScriptが若干怪しいにほいがするのですが、まあ、私JavaScript良く知らないのでこんなもんなんじゃないかと思います。

追記


既に記載のとおり、タイムアウト設定していた時間をオーバーしてしまい、メールは届きませんでした。…残念。CloudWatch Eventで特定の値が含まれていたらメールとかにした方がいいかもしれないですね。