AWS CloudWatch Synthetics Monitoring を CloudFormation で設定する

参考) Using Synthetic Monitoring - Amazon CloudWatch Documentation

リソース

管理コンソールから CloudWatch Synthetics の Canary を作成すると、以下のリソースが展開される。 これを参考にして CloudFormation で各リソースを作成する。

CloudFormation テンプレート

Parameters

以下をパラメタ化した。

  • CanaryName : Canary の名前。配下にいろいろリソースを作る都合上からか、Canary の名前は [a-z\-_]{,21} と厳しい制約になっている。
  • BucketName : 結果の保存先とするバケット名
  • TargetURL : 監視対象の URL

参考) Parameters - AWS CloudFormation

Parameters:
  CanaryName:
    Description: Synthetics Canary name to create
    Type: String
  BucketName:
    Description: Bucket name to save Synthetics results
    Type: String
  TargetURL:
    Description: Target URL to monitor
    Type: String

Service Role

自動で作られる Role を参考に作成した。必要な permissions は Canary リソースに指定する ExecutionRoleArn の説明に書かれている。

  • S3 バケットに結果を書き込むための s3:PutObject と s3::GetBucketLocation、あと s3:ListAllMyBuckets
  • Lambda から実行ログを出力するための logs:CreateLogStream, logs:PutLogEvents, logs:CreateLogGroup
  • CloudWatch Metrics に送信するための cloudwatch:PutMetricData

参考) AWS::IAM::Role - AWS CloudFormation

Resources:
  SyntheticsIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "SyntheticsPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetBucketLocation
                Resource:
                  - !Sub '${SyntheticsResultsBucket.Arn}/*'
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - logs:CreateLogGroup
                Resource:
                  - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/cwsyn-${CanaryName}-*'
              - Effect: Allow
                Action:
                  - s3:ListAllMyBuckets
                Resource: '*'
              - Effect: Allow
                Resource: '*'
                Action: cloudwatch:PutMetricData
                Condition:
                  StringEquals:
                    cloudwatch:namespace: CloudWatchSynthetics

S3 Bucket

参考) AWS::S3::Bucket - AWS CloudFormation

  SyntheticsResultsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

Synthetics Canary

参考) AWS::Synthetics::Canary - AWS CloudFormation

上で生成した IAM Role、保存先バケット名を指定して作る。 Canary を生成すると Lambda 関数も自動的に生成される。

ここで設定しているスクリプトは Blueprint として用意されている Heatbeat monitoring そのまま持ってきたもの。スクリプトは Code に直接記述するほか、S3 に保存しておいて場所を指定することができる。SAM で管理できると良いと思うのだけど...

  Canary:
    Type: AWS::Synthetics::Canary
    Properties:
      Name: !Ref CanaryName
      ExecutionRoleArn: !GetAtt SyntheticsIamRole.Arn
      Code:
        Handler: pageLoadBlueprint.handler
        Script: !Sub >
          var synthetics = require('Synthetics');
          const log = require('SyntheticsLogger');
          const pageLoadBlueprint = async function () {
            const URL = "${TargetURL}";
            let page = await synthetics.getPage();
            const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});
            //Wait for page to render.
            //Increase or decrease wait time based on endpoint being monitored.
            await page.waitFor(15000);
            await synthetics.takeScreenshot('loaded', 'loaded');
            let pageTitle = await page.title();
            log.info('Page title: ' + pageTitle);
            if (response.status() !== 200) {
              throw "Failed to load page!";
            }
          };
          exports.handler = async () => {
            return await pageLoadBlueprint();
          };
      ArtifactS3Location: !Sub 's3://${SyntheticsResultsBucket}'
      RuntimeVersion: syn-1.0
      Schedule:
        DurationInSeconds: 0
        Expression: 'rate(5 minutes)'
      RunConfig:
        TimeoutInSeconds: 60
      FailureRetentionPeriod: 31
      SuccessRetentionPeriod: 31
      StartCanaryAfterCreation: true

SNS Topic

Alarm の通知先トピック

参考) AWS::SNS::Topic - AWS CloudFormation

  AlarmTopic:
    Type: AWS::SNS::Topic

CloudWatch Alarm

Canary の実行結果は固定名の Metrics に送信されるので、それに対して Alarm を設定する。

参考) AWS::CloudWatch::Alarm - AWS CloudFormation

  CanaryAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      MetricName: 'SuccessPercent'
      Namespace: 'CloudWatchSynthetics'
      Dimensions:
        - Name: CanaryName
          Value: !Ref CanaryName
      Statistic: Average
      Period: 300
      EvaluationPeriods: 1
      Threshold: 90
      AlarmActions:
        - Ref: AlarmTopic
      OKActions:
        - Ref: AlarmTopic
      InsufficientDataActions:
        - Ref: AlarmTopic
      TreatMissingData: breaching
      ComparisonOperator: LessThanThreshold

注意点

  • Canary を生成すると Lambda が生成されるが、スタックを削除しても Lambda までは削除されないようなので、手動で削除する必要がある。