Amazon CloudWatch Synthetics を試してみた

2019年11月25日に Amazon CloudWatch Synthetics のプレビューが公開されました。

[Introducing Amazon CloudWatch Synthetics \- Now in Preview](https://aws.amazon.com/jp/about-aws/whats-new/2019/11/introducing-amazon-cloudwatch-synthetics-preview/)

一言で言えば外形監視機能ですが、単に Web サービスの死活監視をするだけでなく、

- ページ内の要素を監視する
- レスポンスタイムを監視する
- スクリーンショットを撮る
- リンク切れを検出する

などなど、監視項目を柔軟に設定できるようです。

こういう機能欲しいな〜〜〜と思っていたところだったので、テンション爆上がりで早速試してみました。

## 監視項目を作成する
現時点では US East (N. Virginia), US East (Ohio), EU (Ireland) の3リージョンでしか使えないとのことですが、どのリージョンから監視をしても大して問題はないと思うので US East (N. Virginia) リージョンで使ってみることにします。

今回は会社のコーポレートサイトの死活監視をやろうと思います。

CloudWatch Synthetics では監視項目を Canary という単位で管理するようなので、まずは Canary を作成していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/1b09e82a-5105-bdc0-2fdd-2ad512a19000.png)

`blueprint` から `Heartbeat monitoring` を選んでエンドポイントの URL を指定すると、その下の Canary Builder に監視内容を設定するコードが生成されます。

``` javascript
var synthetics = require('Synthetics');
const log = require('SyntheticsLogger');

const pageLoadBlueprint = async function () {

    // INSERT URL here
    const URL = "https://churadata.okinawa/";

    let page = await synthetics.getPage();
    const response = await page.goto(URL, {waitUntil: ['load', 'networkidle0'], timeout: 30000});
    await synthetics.takeScreenshot('loaded', 'loaded');
    let pageTitle = await page.title();
    log.info('Page title: ' + pageTitle);
    if (response.status() !== 200) {
        throw "Failed to load page!";
    }
    // 追記してみた
    if (pageTitle !== 'ちゅらデータ株式会社') {
        throw "Failed to load page!";
    }
};

exports.handler = async () => {
    return await pageLoadBlueprint();
};
```

まんま AWS Lambda の JavaScript コードですね。
<del>ランタイムを Python に変えれないのかな</del>

blueprint そのままの状態だと「ページにアクセスしてスクリーンショットを撮ってステータスコードが 200 以外だったら例外を投げる」という流れです。つまりは Lambda Function を記述して、何か異常があれば例外を投げればいいということです。わかりやすい。

`synthetics` は「クローラ + HTML パーサ」なモジュールのようなので、これを使えば「ページ内の特定の要素に期待したコンテンツが含まれているか」というような監視項目も簡単に記述できそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/94029aa6-75b4-1230-850d-dcfb6c44794d.png)

監視コードを記述した後は、監視する頻度・監視データの保持期間・Lambda Function に割り当てる Role などの設定をします。Threshold については後述します。

## 監視データを見てみる
監視項目を作成した後にステータスを見てみると、Lambda Function の実行結果が見れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/a048dc01-a474-a747-7a66-339b4e0155a0.png)

Canary を作成した時点で自動で S3 バケットが作成されて、監視ログやスクリーンショットなどはそこに保存されています。

スクリーンショットをよく見ると日本語が文字化けしていますね。(フィードバックを送った)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/cf9efe3c-06e4-3937-3837-1335275266ce.png)

`HAR File` タブでは、各アセットのレスポンスタイムなどの詳細を見ることができます。

## 異常時のアラートを設定する
`Canary Thretholds` を有効にして各 Canary の設定でも `Thresholds` を有効にすると、CloudWatch Alarm が自動で作成されて異常時にアラートをあげることができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/085fc763-c84f-2b78-20ac-edbe97e49cb6.png)

作成された CloudWatch Alarm 側で閾値は変更できるようですが、デフォルト値は変更できないんですかね？ (まだプレビューなので未実装なのかもしれない)

CloudWatch Alarm まで作成できればあとは Amazon SNS と連携してメールを送ったり Slack にポストしたりと自由自在に通知先を設定できます。このへんは特に新しい話でもないので割愛します。

## 料金
<del>現時点では [Amazon CloudWatch の料金ページ](https://aws.amazon.com/jp/cloudwatch/pricing/) を見ても料金が書いてなかったのでわかりませんでした。</del>
言語設定を英語にすれば料金が確認できました。

- Lambda Function 実行にかかる費用
- S3 バケットにデータを保存する費用
- CloudWatch Alarm にかかる費用
- CloudWatch Synthetics にかかる費用
    - 1回の Canary 実行あたり $0.0012
    - 1時間に1回実行する設定だと約100円/月

## 所感
新サービスと言いつつも背後に AWS Lambda + Amazon S3 + Amazon CloudWatch Alarms がいるサービスなので、そのへんを触ったことがある身としては学習コスト低めで使えてとてもわかりやすいです。それでいてとても柔軟な設定ができるのでかなり使えそうという印象。

CloudFormation や Terraform で Canary を自動で設定できるようになれば、詳細な監視項目を記述した上でそれをアプリケーションと同じリポジトリで管理できて便利なのでは？などと思いました。