この記事はぷりぷりあぷりけーしょんず 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のページが表示されるかを監視してみます。
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分のうちに一度でもメトリクスが確認できなかったら鳴るアラームを設定していきます。
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
});
}
}
canaryName
をsample-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コンソールでチェック
リソースが作成され成功していることが確認できます。
これでGoogle検索でQiita
と検索した時にトップにQiita
のWebページがヒットすることが監視できるようになりました。
監視できるようになったので、次回はアラームがなった時にSlackに通知されるようにCDKで設定していってみたいと思います。
ちなみに
作成したリソースを削除するときはdestroyで瞬時に消せます。
$ npx cdk destroy