AWS CDKによる【CloudWatch・SNS】の構築

皆様こんにちは。
AWS CDKを利用してマルチAZ3層アーキテクチャ構築をしていきます。
この記事ではCloudWatch・SNSの作成を行います。

目次はこちら

1.Amazon CloudWatchとは

Amazon CloudWatchは、AWS(Amazon Web Services)上の監視および管理サービスです。CloudWatchは、AWSリソースやアプリケーションのパフォーマンスと運用データを収集し、追跡、分析、可視化するためのツールを提供します。

主な機能

1. モニタリングとメトリクス収集

  • CloudWatchは、EC2、RDS、DynamoDB、ECS、LambdaなどのAWSリソースからメトリクスを自動的に収集します。これにはCPU使用率、ディスクI/O、ネットワークトラフィックなどが含まれます。
  • カスタムメトリクスもサポートしており、アプリケーションやオンプレミスリソースからメトリクスを送信することができます。

2. ログ管理

  • CloudWatch Logsを使用して、AWSリソースやオンプレミスリソースからログを収集し、保存、分析できます。
  • リアルタイムでログストリームを監視し、特定のイベントやエラーが発生した際にアラートを設定できます。

3. アラーム

  • メトリクスに対する閾値を設定し、その閾値を超えた場合にアラートをトリガーするアラームを作成できます。
  • アラームは、SNSを使用して通知を送信したり、オートスケーリングをトリガーしたり、Lambda関数を実行したりすることができます。

4. ダッシュボード

  • CloudWatchダッシュボードを使用して、複数のメトリクスを一元的に表示し、リソースのパフォーマンスや運用状況を可視化できます。

利点

  • リアルタイムモニタリング: AWSリソースとアプリケーションのパフォーマンスをリアルタイムで監視できます。
  • スケーラビリティ: AWS環境全体のメトリクスとログをスケーラブルに収集および分析できます。
  • カスタマイズ性: カスタムメトリクスとログを使用して、特定のビジネスニーズに合わせた監視とアラームを設定できます。
  • 統合: 他のAWSサービス(SNS、Lambda、EC2 Auto Scalingなど)と統合して、アラームやイベントに対する自動化されたアクションを実行できます。

詳細は公式ドキュメントを参照ください。

2.Amazon SNSとは

Amazon Simple Notification Service (SNS) は、AWSのフルマネージドメッセージングサービスです。SNSを使用すると、通知を簡単に作成し、配信することができます。メッセージを複数のエンドポイントに送信するためのパブリッシュ/サブスクライブモデルを提供します。

主な機能

1. メッセージの配信

  • SNSは、メッセージを複数のプロトコルで配信することができます。これには、メール、SMS、HTTP/HTTPSエンドポイント、AWS Lambda、SQS(Simple Queue Service)などが含まれます。

2. トピックの作成と管理

  • SNSでは、トピックを作成して、メッセージを送信するためのチャネルとして利用します。サブスクリプションを作成し、トピックに対してサブスクライブしたエンドポイントにメッセージを配信します。

3. メッセージのフィルタリング

  • メッセージにタグや属性を付けて、サブスクライバーが受け取るメッセージをフィルタリングすることができます。これにより、特定の条件に合致するメッセージのみを配信することができます。

4. メッセージのフォーマットと変換

  • SNSでは、メッセージのフォーマットや内容を変換するための機能も提供しています。これにより、異なるエンドポイントに合わせてメッセージを調整できます。

5. 監視とアラート

  • SNSは、メッセージの送信状況やエンドポイントの状態を監視するためのメトリクスとアラームを提供しています。CloudWatchと統合し、通知やアラートを設定することができます。

利点

  • スケーラビリティ: 大量のメッセージを迅速に配信でき、スケーラブルなアーキテクチャを提供します。
  • 簡単なセットアップ: トピックとサブスクリプションを簡単に設定でき、直感的なUIやAPIで管理できます。
  • 多様な配信オプション: メール、SMS、HTTP/HTTPS、Lambda、SQSなど、さまざまな配信方法をサポートしています。
  • コスト効率: 使用した分だけ支払う従量課金制で、コストを抑えつつ効率的にメッセージングを行えます。

詳細は公式ドキュメントを参照ください。

3.目的

  • EC2のメトリクス収集を行う
  • CPU、メモリ、ディスク、それぞれに80%、90%の閾値を設定しアラームを作成する
  • 閾値を超えた場合、SNSでメール発報を行う

4.構成図

5.CloudWatch Agentの設定

CloudWatch Agentは、EC2インスタンスやオンプレミスサーバーからシステムメトリクスやアプリケーションログを収集し、AWS CloudWatchに送信するためのエージェントです。

EC2構築時にユーザーデータでインストール済みのため設定のみこちらで行います。

5-1.前提条件

1. CloudWatch AgentをEC2にインストールする

  • EC2構築時にユーザーデータでインストールしています。

2. IAMロール付与

※詳細はリンクにてご確認ください

5-2.セットアップウィザードにてCloudWatch Agentの設定

今回はEC2上ででセットアップウィザードを使用して行います。

1. 下記コマンドでウィザードを起動する

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard

2.セットアップ内容を入力
おおまかに以下の設定にしていきます。

  • メモリとディスクを追加収集する設定と基本的に不要なものは省くような設定にしています。
    ※CPUはデフォルトで収集

  • SSMパラメータストアに保存し、2号機のEC2でも流用できるようにしています。

以下のようなフォーマットで質問に答える形式で入力していきます。
私の設定例を含めて載せておきます。

================================================================
= Welcome to the Amazon CloudWatch Agent Configuration Manager =
=                                                              =
= CloudWatch Agent allows you to collect metrics and logs from =
= your host and send them to CloudWatch. Additional CloudWatch =
= charges may apply.                                           =
================================================================
On which OS are you planning to use the agent?
1. linux
2. windows
3. darwin
default choice: [1]:

1を選択

Trying to fetch the default region based on ec2 metadata...
I! imds retry client will retry 1 timesAre you using EC2 or On-Premises hosts?
1. EC2
2. On-Premises
default choice: [1]:

1を選択

Which user are you planning to run the agent?
1. cwagent
2. root
3. others
default choice: [1]:

1を選択

Do you want to turn on StatsD daemon?
1. yes
2. no
default choice: [1]:

1を選択

Which port do you want StatsD daemon to listen to?
default choice: [8125]

What is the collect interval for StatsD daemon?
1. 10s
2. 30s
3. 60s
default choice: [1]:

1を選択

What is the aggregation interval for metrics collected by StatsD daemon?
1. Do not aggregate
2. 10s
3. 30s
4. 60s
default choice: [4]:

4を選択

Do you want to monitor metrics from CollectD? WARNING: CollectD must be installed or the Agent will fail to start
1. yes
2. no
default choice: [1]:

2を選択

Do you want to monitor any host metrics? e.g. CPU, memory, etc.
1. yes
2. no
default choice: [1]:

1を選択

Do you want to monitor cpu metrics per core?
1. yes
2. no
default choice: [1]:

2を選択

Do you want to add ec2 dimensions (ImageId, InstanceId, InstanceType, AutoScalingGroupName) into all of your metrics if the info is available?
1. yes
2. no
default choice: [1]:

1を選択

Do you want to aggregate ec2 dimensions (InstanceId)?
1. yes
2. no
default choice: [1]:

1を選択

Would you like to collect your metrics at high resolution (sub-minute resolution)? This enables sub-minute resolution for all metrics, but you can customize for specific metrics in the output json file.
1. 1s
2. 10s
3. 30s
4. 60s
default choice: [4]:

4を選択

Which default metrics config do you want?
1. Basic
2. Standard
3. Advanced
4. None
default choice: [1]:

1を選択

Current config as follows:
{
        "agent": {
                "metrics_collection_interval": 60,
                "run_as_user": "cwagent"
        },
        "metrics": {
                "aggregation_dimensions": [
                        [
                                "InstanceId"
                        ]
                ],
                "append_dimensions": {
                        "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
                        "ImageId": "${aws:ImageId}",
                        "InstanceId": "${aws:InstanceId}",
                        "InstanceType": "${aws:InstanceType}"
                },
                "metrics_collected": {
                        "disk": {
                                "measurement": [
                                        "used_percent"
                                ],
                                "metrics_collection_interval": 60,
                                "resources": [
                                        "*"
                                ]
                        },
                        "mem": {
                                "measurement": [
                                        "mem_used_percent"
                                ],
                                "metrics_collection_interval": 60
                        },
                        "statsd": {
                                "metrics_aggregation_interval": 60,
                                "metrics_collection_interval": 10,
                                "service_address": ":8125"
                        }
                }
        }
}
Are you satisfied with the above config? Note: it can be manually customized after the wizard completes to add additional items.
1. yes
2. no
default choice: [1]:

1を選択

Do you have any existing CloudWatch Log Agent (http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html) configuration file to import for migration?
1. yes
2. no
default choice: [2]:

2を選択

Do you want to monitor any log files?
1. yes
2. no
default choice: [1]:

2を選択

Do you want the CloudWatch agent to also retrieve X-ray traces?
1. yes
2. no
default choice: [1]:

2を選択

Existing config JSON identified and copied to:  /opt/aws/amazon-cloudwatch-agent/etc/backup-configs
Saved config file to /opt/aws/amazon-cloudwatch-agent/bin/config.json successfully.
Current config as follows:
{
        "agent": {
                "metrics_collection_interval": 60,
                "run_as_user": "cwagent"
        },
        "metrics": {
                "aggregation_dimensions": [
                        [
                                "InstanceId"
                        ]
                ],
                "append_dimensions": {
                        "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
                        "ImageId": "${aws:ImageId}",
                        "InstanceId": "${aws:InstanceId}",
                        "InstanceType": "${aws:InstanceType}"
                },
                "metrics_collected": {
                        "disk": {
                                "measurement": [
                                        "used_percent"
                                ],
                                "metrics_collection_interval": 60,
                                "resources": [
                                        "*"
                                ]
                        },
                        "mem": {
                                "measurement": [
                                        "mem_used_percent"
                                ],
                                "metrics_collection_interval": 60
                        },
                        "statsd": {
                                "metrics_aggregation_interval": 60,
                                "metrics_collection_interval": 10,
                                "service_address": ":8125"
                        }
                }
        }
}
Please check the above content of the config.
The config file is also located at /opt/aws/amazon-cloudwatch-agent/bin/config.json.
Edit it manually if needed.
Do you want to store the config in the SSM parameter store?
1. yes
2. no
default choice: [1]:

1を選択

What parameter store name do you want to use to store your config? (Use 'AmazonCloudWatch-' prefix if you use our managed AWS policy)
default choice: [AmazonCloudWatch-linux]

デフォルトを選択

Trying to fetch the default region based on ec2 metadata...
I! imds retry client will retry 1 timesWhich region do you want to store the config in the parameter store?
default choice: [ap-northeast-2]

デフォルトを選択

Which AWS credential should be used to send json config to parameter store?
1. ASIAX247IP3FOAA6C5YT(From SDK)
2. Other
default choice: [1]:

1を選択

Successfully put config to parameter store AmazonCloudWatch-linux.
Program exits now.

3.CloudWatchエージェントの起動
SSMパラメータストアに保存した設定で起動します。
コマンドは下記です。
※2号機以降は下記コマンドのみでウィザードでの再設定は不要です。

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:AmazonCloudWatch-linux -s

4.メトリクスの作成を確認
マネジメントコンソールでメトリクスが作成されたことを確認します。
インスタンスIDで検索し、以下のように収集できていました。

6.SNSトピック作成

メール発報のためにSNSトピックを作成していきます。
下記2点行います。

  • トピックに対してメールエンドポイントのサブスクリプションを追加
  • 連携予定のサービスを許可するSNS トピックポリシーの設定

6-1."sns.Topic"を使用してSNSトピックを作成

論理ID(テンプレート内で一意)の指定をしたのち、詳細のプロパティを指定しています。

プロパティは下記

使用するプロパティ 設定値 説明
topic_name ["kitaya-alarm"] 作成するSNSトピックの名前を指定します。

6-2. "Tags.of()"メソッドを使用してNameタグをつける

書式は下記です。
Tags.of("リソース名").add("キー", "値")

6-3. メールエンドポイントのサブスクリプションを追加

  • ".add_subscription":SNSトピックに新しいサブスクリプションを追加するメソッド
  • "subscriptions.EmailSubscription":メールアドレスを使用したサブスクリプションを作成するためのクラス

下記のような書式になります。

"対象のトピック".add_subscription(subscriptions.EmailSubscription("指定したいメールアドレス"))

6-4. SNSトピックポリシーの設定

  • ".add_to_resource_policy":SNS トピックに対する特定のアクションを許可するポリシーを追加するメソッド
  • "iam.PolicyStatement":ポリシーの基本要素を定義するためのクラス

下記のような書式になります。

"対象のトピック".add_to_resource_policy(iam.PolicyStatement("下記プロパティ"))

使用するプロパティ 設定値 説明
effect iam.Effect.ALLOW ポリシーの効果。ここではアクセスを許可する設定。
actions ["sns:Publish"] 許可するアクション。ここではSNSのPublishアクションを許可。
resources [self.topic.topic_arn] ポリシーが適用されるリソースのARN。ここではSNSトピックのARN。
principals [iam.ServicePrincipal("cloudwatch.amazonaws.com")] アクセスを許可するプリンシパル。ここではcloudwatch.amazonaws.comサービス。

ソースコード

        # SNS トピックの作成
        self.topic = sns.Topic(self, "KitayaTopic",
            topic_name="kitaya-alarm"
        )
        Tags.of(self.topic).add("Name", "kitaya-alarm")

        # メールエンドポイントのサブスクリプション
        self.topic.add_subscription(subscriptions.EmailSubscription("kitaya.r@cp-info.co.jp"))

        # SNS トピックポリシーの設定
        self.topic.add_to_resource_policy(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,  # Effectを指定
                actions=["sns:Publish"],  # 許可するアクション
                resources=[self.topic.topic_arn],  # SNS トピック ARN
                principals=[iam.ServicePrincipal("cloudwatch.amazonaws.com")]  # 許可するサービス
            )
        )

        self.topic.add_to_resource_policy(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,  # Effectを指定
                actions=["sns:Publish"],  # 許可するアクション
                resources=[self.topic.topic_arn],  # SNS トピック ARN
                principals=[iam.ServicePrincipal("events.amazonaws.com")]  # 許可するサービス
            )
        )

7.アラーム作成

各EC2、監視リソース、閾値ごとに引数を設定しました。
その引数を各リソースの関数に渡してアラームの作成を行っています。

7-1.引数の設定

下記のような書式で引数を設定します。

"関数名"("引数1", "引数2", "引数3"・・・・ )

(例)self.create_disk_alarm(instance_01, sns_topic, 80, "kitaya-ec2-01-Disk-80", "/", "nvme0n1p1", "xfs")

  • 関数"create_disk_alarm"に下記の引数を渡しています。
引数名 説明
instance_01 アラームを設定するEC2インスタンス。
80 アラームのしきい値。
"kitaya-ec2-01-Disk-80" アラームの名前。
"/" ディスクのマウントポイント。
"nvme0n1p1" デバイス名。
"xfs" ファイルシステムのタイプ。

ソースコード

        # CPU使用率のアラーム (instance_01)
        self.create_cpu_alarm(instance_01, sns_topic, 80, "kitaya-ec2-01-CPU-80")
        self.create_cpu_alarm(instance_01, sns_topic, 90, "kitaya-ec2-01-CPU-90")

        # CPU使用率のアラーム (instance_02)
        self.create_cpu_alarm(instance_02, sns_topic, 80, "kitaya-ec2-02-CPU-80")
        self.create_cpu_alarm(instance_02, sns_topic, 90, "kitaya-ec2-02-CPU-90")

        # メモリ使用率のアラーム (instance_01)
        self.create_memory_alarm(instance_01, sns_topic, 80, "kitaya-ec2-01-Memory-80")
        self.create_memory_alarm(instance_01, sns_topic, 90, "kitaya-ec2-01-Memory-90")

        # メモリ使用率のアラーム (instance_02)
        self.create_memory_alarm(instance_02, sns_topic, 80, "kitaya-ec2-02-Memory-80")
        self.create_memory_alarm(instance_02, sns_topic, 90, "kitaya-ec2-02-Memory-90")

        # ルートディスク使用量のアラーム (instance_01)
        self.create_disk_alarm(instance_01, sns_topic, 80, "kitaya-ec2-01-Disk-80", "/", "nvme0n1p1", "xfs")
        self.create_disk_alarm(instance_01, sns_topic, 90, "kitaya-ec2-01-Disk-90", "/", "nvme0n1p1", "xfs")

        # ルートディスク使用量のアラーム (instance_02)
        self.create_disk_alarm(instance_02, sns_topic, 80, "kitaya-ec2-02-Disk-80", "/", "nvme0n1p1", "xfs")
        self.create_disk_alarm(instance_02, sns_topic, 90, "kitaya-ec2-02-Disk-90", "/", "nvme0n1p1", "xfs")

7-2.関数の作成

def関数の作成

以下の書式で受け取る引数を設定します。

def "関数名"("受け取る引数1", "受け取る引数2","受け取る引数3"・・・・)

(例)def create_disk_alarm(self, instance, sns_topic, threshold: int, id_suffix: str, filesystem: str, device: str, fstype: str):

  • 関数"create_disk_alarm"に下記のように受け取る引数を設定しています。
引数名 説明
self メソッドが所属するクラスのインスタンスを指します。
instance アラームを設定する対象のEC2インスタンスオブジェクトです。EC2インスタンスのメタデータを取得するために使います。
sns_topic アラームが発動したときに通知を送信するSNSトピックオブジェクトです。アラーム通知の受信先を指定します。
threshold アラームを発生させるためのディスク使用量のしきい値です。
id_suffix アラームの名前に付加されるサフィックスです。これにより、アラームが一意になります。
filesystem 監視対象のファイルシステムのマウントポイントです。
device 監視対象のデバイス名です。
fstype ファイルシステムのタイプです。

"cloudwatch.CfnAlarm"を使用してアラーム作成の処理を設定します。

論理ID(テンプレート内で一意)の指定をしたのち、詳細のプロパティを指定しています。

プロパティは下記

使用するプロパティ 設定値 説明
alarm_name {id_suffix} アラームの名前です。
alarm_description ソースコード参照 アラームの説明です。
comparison_operator GreaterThanOrEqualToThreshold アラームの比較演算子です。しきい値以上の値でアラームを発動させる設定です。
evaluation_periods 3 アラームの評価期間の数です。データポイントを評価する期間の数を指定します。
metric_name ソースコード参照 監視するメトリクスの名前です。
namespace ソースコード参照 メトリクスが属する名前空間です。
period 300 メトリクスの収集間隔(秒)です。
statistic Average メトリクスの統計情報のタイプです。平均値を計算してアラームを発動させる設定です。
threshold threshold アラームが発動するしきい値です。ディスク使用量がこの値を超えた場合にアラームが発動します。
alarm_actions [sns_topic.topic_arn] アラームが発動したときに実行するアクションです。SNSトピックへの通知を設定します。
dimensions [] メトリクスに関連付けられるディメンションを設定します。

"cloudwatch.CfnAlarm.DimensionProperty"でディメンションを設定します。

ディメンション(Dimension)とは、CloudWatch メトリクスのデータをさらに詳細に分類するためのキーと値のペアです。

プロパティは下記です。

使用するプロパティ 設定値 説明
name ソースコード参照 ディメンション名の指定
value ソースコード参照 ディメンションの値

ソースコード

    def create_cpu_alarm(self, instance, sns_topic, threshold: int, id_suffix: str):
        cloudwatch.CfnAlarm(self, f"{id_suffix}-Alarm",
            alarm_name=f"{id_suffix}",
            alarm_description=f"Alarm for CPUUtilization at {threshold}% for {instance.ref}",
            comparison_operator="GreaterThanOrEqualToThreshold",
            evaluation_periods=3,
            metric_name="CPUUtilization",
            namespace="AWS/EC2",
            period=300,
            statistic="Average",
            threshold=threshold,
            alarm_actions=[sns_topic.topic_arn],
            dimensions=[
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="InstanceId",
                    value=instance.ref
                )
            ]
        )

    def create_memory_alarm(self, instance, sns_topic, threshold: int, id_suffix: str):
        cloudwatch.CfnAlarm(self, f"{id_suffix}-Alarm",
            alarm_name=f"{id_suffix}",
            alarm_description=f"Alarm for mem_used_percent at {threshold}% for {instance.ref}",
            comparison_operator="GreaterThanOrEqualToThreshold",
            evaluation_periods=3,
            metric_name="mem_used_percent",
            namespace="CWAgent",
            period=300,
            statistic="Average",
            threshold=threshold,
            alarm_actions=[sns_topic.topic_arn],
            dimensions=[
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="InstanceId",
                    value=instance.ref
                ),
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="ImageId",
                    value=instance.image_id
                ),
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="InstanceType",
                    value=instance.instance_type
                )
            ]
        )

    def create_disk_alarm(self, instance, sns_topic, threshold: int, id_suffix: str, filesystem: str, device: str, fstype: str):
        cloudwatch.CfnAlarm(self, f"{id_suffix}-Alarm",
            alarm_name=f"{id_suffix}",
            alarm_description=f"Alarm for disk_used_percent at {threshold}% for {instance.ref}",
            comparison_operator="GreaterThanOrEqualToThreshold",
            evaluation_periods=3,
            metric_name="disk_used_percent",
            namespace="CWAgent",
            period=300,
            statistic="Average",
            threshold=threshold,
            alarm_actions=[sns_topic.topic_arn],
            dimensions=[
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="path",
                    value=filesystem
                ),
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="InstanceId",
                    value=instance.ref
                ),
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="ImageId",
                    value=instance.image_id
                ),
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="InstanceType",
                    value=instance.instance_type
                ),
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="device",
                    value="nvme0n1p1"
                ),
                cloudwatch.CfnAlarm.DimensionProperty(
                    name="fstype",
                    value="xfs"
                )
            ]
        )

8.検証

CPU、メモリ、ディスクでそれぞれ閾値に達した際にメール発報が行われるか検証していきます。
stressコマンドでEC2に負荷をかけて検証します。
下記コマンドで先にインストールを行います。

yum install stress

8-1.CPU使用率の検証方法

下記コマンドでCPUに負荷をかけます。

stress --cpu 2 --timeout 2000

8-2.メモリ使用率の検証方法

1. 下記コマンドでメモリの総量と使用量を確認します。

free -h

2. 確認した値と照らし合わせて計算しメモリ使用率に負荷をかけます。

※総量-使用量+負荷をかける値>閾値

stress -m 1 --vm-bytes 538M --vm-hang 0 -v

8-3.ディスク使用率の検証方法

下記コマンドでダミーファイルを作成し、ディスク使用率をあげます。

dd if=/dev/zero of=ファイル名 bs=1M count=5800

8-4.メトリクスの確認とメール発砲の確認

下記のようにメトリクスの変動とメール発報の確認ができました。
※メモリ以外も同様に確認できました


8.感想

Cloud watchエージェントの設定、アラームの作成、テストのすべてで割と時間がかかった。
細かい設定まで学ぶいい機会になった。

Last modified: 2024-08-14

Author