サイトアイコン 協栄情報ブログ

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

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

目次はこちら

1.Amazon CloudWatchとは

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

主な機能

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

2. ログ管理

3. アラーム

4. ダッシュボード

利点

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

2.Amazon SNSとは

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

主な機能

1. メッセージの配信

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

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

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

5. 監視とアラート

利点

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

3.目的

4.構成図

5.CloudWatch Agentの設定

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

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

5-1.前提条件

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

2. IAMロール付与

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

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

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

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

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

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

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

================================================================
= 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点行います。

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

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

プロパティは下記

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

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

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

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

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

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

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

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

"対象のトピック".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")

引数名 説明
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):

引数名 説明
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エージェントの設定、アラームの作成、テストのすべてで割と時間がかかった。
細かい設定まで学ぶいい機会になった。

モバイルバージョンを終了