search
LoginSignup
1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

ぷりぷりあぷりけーしょんず Advent Calendar 2020 Day 1

posted at

updated at

CloudWatch SyntheticsをCDKで管理する

この記事はぷりぷりあぷりけーしょんず Advent Calendar 2020の1日目の記事です。
初っ端遅刻で失礼します。

はじめに

今年の4月にAWS CloudWatch Syntheticsの一般提供が開始されました
https://aws.amazon.com/jp/about-aws/whats-new/2020/04/amazon-cloudwatch-synthetics-generally-available/

ざっくりサービスを説明するとREST APIやWebの動作を死活監視し、CI/CDなどでは見逃しがちな特定条件で発生し得るレイテンシの大きなタイミングや想定しない挙動をチェックし、閾値を超えるとアラームを鳴らしてくれる優れものです。

今回はWebの挙動監視をし、設定からNode.jsのスクリプトまでコード管理できるようにCDKで実装をしてみました。
CDKやAWSアカウントなどの環境構築については説明を省いてつらつらと手順を書いていきます。
CDKは筆者の好みによりTypeScriptで書いていきます。

プロジェクトの用意

$ mkdir sample-cloudwatch-synthetics
$ cd sample-cloudwatch-synthetics
$ cdk init --language typescript
$ npm install @aws-cdk/aws-synthetics @aws-cdk/aws-cloudwatch 

監視用スクリプトの作成

スクリプト作成の公式ドキュメントはこちら
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_WritingCanary.html

フォルダ階層の設定

複数の .js ファイルがある場合や、スクリプトに依存関係がある場合は、それらのすべてを単一の ZIP ファイル (フォルダ構造は nodejs/node_modules/myCanaryFilename.js file and other folders and files) にバンドルできます。

上述の通り監視用のCanaryスクリプトのフォルダ構造はnodejs/node_modules/myCanaryFilename.jsとする必要があるので今回は監視対象ごとにスクリプトを管理できるように以下のようにしておきました

src
└── sample-synthetics
    └── nodejs
        └── node_modules
            └── index.js

スクリプトの作成

Canaryスクリプト用のライブラリ関数の公式はこちら。

ドキュメント内にもあるとおりCanaryスクリプトではPuppeteerのラッパーライブラリであるSyntheticsパッケージと Syntheticsログ専用のSyntheticsLoggerパッケージを使用します、がAWS上にデプロイされれば自動的に使えるのでpackage.jsonを用意する必要はありません。(ローカル実行はどうするんやという点については未調査)

今回はGoogle検索でQiitaと検索して検索結果の一番上にQiitaのページが表示されるかを監視してみます。

src/nodejs/node_modules/index.js
const synthetics = require('Synthetics');
const log = require('SyntheticsLogger');

const pageLoadBlueprint = async function () {
    try {
        // Googleのページにアクセス
        const page = await synthetics.getPage();
        await page.goto('https://www.google.com/', { waitUntil: 'domcontentloaded', timeout: 10000 });

        // 検索バーに'Qiita'と入力し検索
        await page.type('input[name="q"]', 'Qiita');
        await page.keyboard.press('Enter');
        await page.waitFor(5000);

        // 検索結果の1番上をクリック
        page.click('div#search a');
        await page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 60000 });

        // 'Qiita'のページにアクセスできていることを確認
        const pageTitle = await page.title();
        if (pageTitle === 'Qiita') {
            log.info('Success')
        } else {
            throw new Error('Not Qiita Page!!');
        }
    } catch (e) {
        console.log(e);
        throw e;
    }
};

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

CDKでリソースの作成

30分に一度syntheticsが動いて30分のうちに一度でもメトリクスが確認できなかったら鳴るアラームを設定していきます。

lib/sample-cloud-watch-synthetics-stack.ts
import * as path from 'path';
import * as cdk from '@aws-cdk/core';
import { Duration } from '@aws-cdk/core';
import { Alarm, ComparisonOperator } from '@aws-cdk/aws-cloudwatch';
import { Canary, Code, Runtime, Schedule, Test } from '@aws-cdk/aws-synthetics';
import { TreatMissingData } from '@aws-cdk/aws-cloudwatch/lib/alarm';

export class SampleCloudwatchSyntheticsStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const APP_NAME = 'SampleCloudwatchSynthetics';
    const SRC_DIR = '../src'

    const canary = new Canary(this, `${APP_NAME}-canary`, {
      canaryName: 'sample-canary',
      schedule: Schedule.rate(Duration.minutes(30)),
      runtime: Runtime.SYNTHETICS_1_0,
      test: Test.custom({
        code: Code.fromAsset(path.join(__dirname, `${SRC_DIR}/sample-synthetics`)),
        handler: 'index.handler',
      }),
    });

    new Alarm(this, `${APP_NAME}-alarm`, {
      alarmName: `${APP_NAME}-Alarm`,
      alarmDescription: 'Qiitaのページにたどり着けるか',
      metric: canary.metricSuccessPercent(),
      threshold: 1,
      comparisonOperator: ComparisonOperator.LESS_THAN_THRESHOLD,
      period: cdk.Duration.minutes(30),
      evaluationPeriods: 1,
      treatMissingData: TreatMissingData.MISSING
    });
  }
}

canaryNamesample-canaryとしていますが下のようなエラーに阻まれ仕方なくこうしました

Canary name is too large, must be between 1 and 21 characters, but is 33 (got "SampleCloudwatchSynthetics-Canary")

Canary name must be lowercase, numbers, hyphens, or underscores (got "SampleCanary")

デプロイ

$ npx cdk deploy

AWCコンソールでチェック

canary.png

リソースが作成され成功していることが確認できます。
これでGoogle検索でQiitaと検索した時にトップにQiitaのWebページがヒットすることが監視できるようになりました。
監視できるようになったので、次回はアラームがなった時にSlackに通知されるようにCDKで設定していってみたいと思います。

ちなみに

作成したリソースを削除するときはdestroyで瞬時に消せます。

$ npx cdk destroy

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1
Help us understand the problem. What are the problem?