🐠

CloudWatch Syntheticsで外形監視!!!

2024/11/23に公開

はじめに

「外形監視」って知ってますか?
私はSAPを勉強している時に聞いたような、聞いていないような、そんな感じです。外形監視の機能を提供してくれるAWSサービスは「Amazon CloudWatch Synthetics」(cwsyn)ですか、これも同様であまり覚えていません。
最近、外形監視を導入する機会があったのでまとめたいと思います。CloudFormationテンプレートも作りました。( -`ω-)✧

Amazon CloudWatch Synthetics

  • 外形監視とは?
    • Webサイトを利用するユーザと同じネットワーク(インターネット)からアクセスして、ユーザ視点でシステムが正常に稼働しているかを監視する手法である。
  • 内部監視とは?
    • 外形監視がユーザ視点からの監視であるのに対し、内部監視はシステム視点からの監視を指す。
    • リソース監視(CPU/メモリ/ストレージ)、ログ監視、ネットワーク監視(PING)、ポート監視等が該当する。
  • Canary
    • cwsynで外形監視を実施するにはcwsynでCanaryというリソースを作成する。
    • Canaryの実態はLambda関数であり、Webサイト/Webアプリケーションへアクセスし実行結果をS3バケットやCloudWatch Logs, X-Rayに保存する。
    • 関数コードはブループリントが容易されているため、ノーコーディングで始められる(Node.js or Python)。
    • Webページのスクリーンショットを取得することも可能で、S3バケットに保存される。

アーキテクチャ

各リソースの役割とアーキテクチャは下記の通りである。

  • Lambda: 外形監視の実行主体
  • Lambdaレイヤー:Seleniumとブループリント(アーキ図では省略)
  • S3: モニタリング結果の保存場所
  • CloudWatch Logs: Lambdaのログを保管
  • IAM: Lambdaの実行ロール

テンプレート

  • TargetUrlに監視対象のURLを指定します。
  • 関数コードはブループリント(ハートビートのモニタリング)を使用します。
AWSTemplateFormatVersion: 2010-09-09
Description: ""

Parameters:
PjName:
    Type: String
Env:
    Type: String
TargetUrl:
    Type: String

Resources:
CloudWatchSyntheticsCanary:
    Type: AWS::Synthetics::Canary
    Properties:
    ArtifactS3Location: !Sub s3://${S3BucketCwsynResult}
    Code:
        Handler: pageLoadBlueprint.handler
        Script: !Sub
        - |
        from selenium.webdriver.common.by import By
        from aws_synthetics.selenium import synthetics_webdriver as syn_webdriver
        from aws_synthetics.common import synthetics_logger as logger

        def main():
            url = "${TargetUrl}"

            # Set screenshot option
            takeScreenshot = True

            browser = syn_webdriver.Chrome() # Chromeを操作するドライバー
            browser.get(url) # URLを開く

            if takeScreenshot:
                browser.save_screenshot("loaded.png") # スクリーンショットを取得してファイルに保存

            response_code = syn_webdriver.get_http_response(url) # URLにアクセスしてステータスコードを取得
            if not response_code or response_code < 200 or response_code > 299:
                raise Exception("Failed to load page!")
            logger.info("Canary successfully executed.")

        def handler(event, context):
            # user defined log statements using synthetics_logger
            logger.info("Selenium Python heartbeat canary.")
            return main()
        - {
            TargetUrl: !Ref TargetUrl
        }
    ExecutionRoleArn: !GetAtt CwsynExecutionRole.Arn
    FailureRetentionPeriod: 31
    Name: !Sub ${PjName}-cwsyn-${Env}
    RuntimeVersion: syn-python-selenium-4.1
    Schedule:
        DurationInSeconds: 0
        Expression: rate(5 minutes)
    StartCanaryAfterCreation: true
    SuccessRetentionPeriod: 31
    Tags:
        - Key: Name
        Value: !Sub ${PjName}-cwsyn-${Env}

S3BucketCwsynResult:
    Type: AWS::S3::Bucket
    Properties:
    BucketEncryption:
        ServerSideEncryptionConfiguration:
        - BucketKeyEnabled: true
            ServerSideEncryptionByDefault:
            SSEAlgorithm: AES256
    BucketName: !Sub ${PjName}-s3-bucket-cwsyn-result-${Env}
    PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
    Tags:
        - Value: Name
        Key: !Sub ${PjName}-s3-bucket-cwsyn-result-${Env}

CwsynExecutionRole:
    Type: AWS::IAM::Role
    Properties:
    AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
            Principal:
            Service:
                - lambda.amazonaws.com
            Action:
            - sts:AssumeRole
    Path: /service-role/
    RoleName: !Sub ${PjName}-iamrole-cwsyn-execution-${Env}
    Tags:
        - Key: Name
        Value: !Sub ${PjName}-iamrole-cwsyn-execution-${Env}

CwsynExecutionPolicy:
    Type: AWS::IAM::Policy
    Properties:
    PolicyName: !Sub ${PjName}-iampolicy-cwsyn-execution-${Env}
    Roles:
        - !Ref CwsynExecutionRole
    PolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
            Action:
            - s3:PutObject
            - s3:GetObject
            Resource:
            - !Sub ${S3BucketCwsynResult.Arn}/*
        - Effect: Allow
            Action:
            - s3:GetBucketLocation
            Resource:
            - !Sub ${S3BucketCwsynResult.Arn}
        - Effect: Allow
            Action:
            - logs:CreateLogStream
            - logs:PutLogEvents
            - logs:CreateLogGroup
            Resource:
            - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/cwsyn-*
        - Effect: Allow
            Action:
            - s3:ListAllMyBuckets
            - xray:PutTraceSegments
            Resource:
            - '*'
        - Effect: Allow
            Resource: '*'
            Action: cloudwatch:PutMetricData
            Condition:
            StringEquals:
                cloudwatch:namespace: CloudWatchSynthetics

Canaryの実行結果

  • 「Synthetics Canaryの概要」を確認→5分ごとに実行され、すべて「成功」しています。
  • Canaryを確認→スクリーンショットが正常に取得されています。

疑問点

  • そもそも「Selenium」とは?

    • WEBブラウザを操作するためのPython/Node.jsのライブラリ
    • 標準インストールされていないため別途インストールが必要
    • Lambdaで使う場合はレイヤーとして配置
    • 最新バージョンは4.26.1→PyPI Selenium
  • 2つのレイヤーの役割は?

    • arn:aws:lambda:ap-northeast-1:172291836251:layer:Synthetics_Selenium:41
      • 本レイヤーがPythonの外部ライブラリであるSelenium本体
      • PyPIで提供されているものと異なり、AWSによりカスタマイズされている
      • 下記サイトにARNの記載あり
    • arn:aws:lambda:ap-northeast-1:${AccountId}:layer:cwsyn-zenn-cwsyn-dev-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:2
      • ブループリントのPythonプログラム(ローカルにダウンロードして中身を確認)。
      • 独自のモニタリングを行いたい場合はここに実装する。
  • Canary Lambdaに渡されるeventの中身は?

    • 気になったのでログ出力させました。
      {
          "canaryName": "zenn-cwsyn-dev",
          "s3BaseFilePath": "${バケット名}",
          "customerCanaryHandlerName": "pageLoadBlueprint.handler",
          "customerCanaryCodeLocation": "arn:aws:lambda:${AWS::Region}:XXXXXXXXXXXX:layer:cwsyn-zenn-cwsyn-dev-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:1",
          "invocationTime": 1732352615487,
          "runtimeVersion": "syn-python-selenium-4.1",
          "activeTracing": false,
          "canaryRunId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
          "artifactS3Location": {
              "s3Bucket": "${バケット名}",
              "s3Key": "canary/${AWS::Region}/zenn-cwsyn-dev/yyyy/mm/dd/hh/mm-ss-sss"
          },
          "canaryRunStartTime": 1732352624558
      }
      
  • AWSのリソースを監視対象にする際、Canary LambdaをVPCLambdaにすることも可能だが...
    今回はAWS Cloud外部にホストされているサービスを監視対象にしました。AWSに構築したサービスを監視対象とする場合はどんな構成になるでしょうか。
    オーソドックスな構成として、プライベートサブネットにWebサーバ、前段のパブリックサブネットにALBを配置するとします。ユーザ→ALB→Webサーバとなるので、ALBに割り当てられたURLを監視対象とすれば、ユーザ視点のモニタリングができそうです。
    一方、プライベートサブネットにCanary Lambda(VPCLambda)を配置し、VPCLambda→Webサーバ、もしくはVPCLambda→ALBという構成も可能です。ユーザとWebサーバの間には大抵WAF、ALB、SGなどが配置されるため、これではネットワークの設定不備・障害を発見できない恐れがあります。
    あくまでも、外形監視はユーザ視点であるかが大切なポイントです。※VPCLambdaにする必要はない。

さいごに

今回はCloudWatch Syntheticsで外形監視を行いました。
URLにアクセス後、IDとパスワードを入力しログインする、といった処理も実装次第で可能らしいです。作り込みはまたの機会に。( 。。•ᴗ• )੭⁾⁾
今後Google Cloudの資格に挑戦する予定なので、それに関連した記事も書いていこうと思います。

参考文献

Discussion