Amazon EventBridge SchedulerでCloudWatchアラームを定期的に無効化・有効化するCloudFormationテンプレートを作成してみた
データアナリティクス事業本部の鈴木です。
Amazon EventBridge Schedulerを使ったCloudWatchアラームの計画的なダウンタイム設定を簡単に実装できるよう、CloudFormationのテンプレートを作ってみたのでご紹介します。
Amazon EventBridge Schedulerを使ったCloudWatchアラームの計画的な無効化・有効化について
CloudWatchアラームはAPIやAWS CLI、およびコンソールから無効化・有効化ができます。API実行はAmazon EventBridge Schedulerのユニバーサルターゲットに指定することで、定期的な実行が可能です。
Amazon EventBridge Schedulerは、2022年の11月にリリースされたEventBridgeの機能です。
このサービスについては以下のブログ記事が大変参考になります。
今回はCloudWatchアラームのEnableAlarmActions
APIおよびDisableAlarmActions
APIを呼び出し、計画的なダウンタイム設定を実現するためのEventBridge Schedulerのテンプレートを作成しました。
なお、以前にはMetric Mathを使用してダウンタイムを設定する方法をご紹介しました。Metric Mathを使用しても実装なしで計画的なダウンタイムの設定が実現できますが、EventBridge Schedulerを使えば、単に設定した時間に無効化・有効化を実行してくれるのでシンプルで分かりやすいですね。
検証した内容
以下のような構成で、EventBridge Schedulerから指定した時間にCloudWatchアラームを無効化・有効化し、無効化期間中はSNSトピック経由でメール通知がされないことを確認しました。
やってみた
1. スケジュールの準備
ここでは、作成したEventBridge Scheduler用のテンプレートをご紹介します。
Lambda関数およびCloudWatchアラームについては今回紹介したいことの本題ではないので、補足として後ほど記載します。
EventBridge Scheduler用のテンプレートは以下になります。
AWSTemplateFormatVersion: '2010-09-09' Description: EventBridge Scheduler to enable/disable CloudWatch Alarm Parameters: CloudWatchAlarmName: Type: String ScheduleEnableTime: Type: String ScheduleDisableTime: Type: String ScheduleTimezone: Type: String Default: Japan Resources: ScheduleCloudWatchAlarmEnable: Type: AWS::Scheduler::Schedule Properties: Name: !Sub 'CloudWatchAlarmEnable${CloudWatchAlarmName}' Description: Enable CloudWatch Alarm ScheduleExpression: !Ref ScheduleEnableTime ScheduleExpressionTimezone: !Ref ScheduleTimezone FlexibleTimeWindow: Mode: "OFF" State: ENABLED Target: Arn: arn:aws:scheduler:::aws-sdk:cloudwatch:enableAlarmActions Input: !Sub "{\"AlarmNames\": [\"${CloudWatchAlarmName}\"]}" RoleArn: Fn::GetAtt: - SchedulerCWAEnableDisableRole - Arn ScheduleCloudWatchAlarmDisable: Type: AWS::Scheduler::Schedule Properties: Name: !Sub 'CloudWatchAlarmDisable${CloudWatchAlarmName}' Description: Disable CloudWatch Alarm ScheduleExpression: !Ref ScheduleDisableTime ScheduleExpressionTimezone: !Ref ScheduleTimezone FlexibleTimeWindow: Mode: "OFF" State: ENABLED Target: Arn: arn:aws:scheduler:::aws-sdk:cloudwatch:disableAlarmActions Input: !Sub "{\"AlarmNames\": [\"${CloudWatchAlarmName}\"]}" RoleArn: Fn::GetAtt: - SchedulerCWAEnableDisableRole - Arn SchedulerCWAEnableDisableRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - scheduler.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: CWAEnableDisable PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - cloudwatch:enableAlarmActions - cloudwatch:disableAlarmActions Resource: - "*"
テンプレートでは以下を作成します。
- CloudWatchアラームを無効化するEventBridge Schedulerのスケジュール
- CloudWatchアラームを有効化するEventBridge Schedulerのスケジュール
- EventBridge Schedulerで使用するIAMロール
テンプレートはEventBridge Schedulerを使ってEC2を定期起動・停止するCloudFormationテンプレートを参考にしました。
スケジュールから、ユニバーサルターゲットを使ってCloudWatchのAPIを実行しました。ユニバーサルターゲットについては、ユーザーガイドの以下のページの説明を参照ください。
CloudWatchのAPIは、以下のAmazon CloudWatchのAPI Referenceを参考にしました。
CloudFormationでデプロイし、以下のようにスケジュールが作成されていることを確認しました。
ScheduleEnableTime
とScheduleDisableTime
は、以下のようにcronの設定を入力しました。
# | パラメータ | 入力した値 |
---|---|---|
1 | ScheduleDisableTime | cron(35 13 ? * 2 *) |
2 | ScheduleEnableTime | cron(45 13 ? * 2 *) |
スケジュールの指定方法は、ユーザーガイドの以下のページに説明がありました。
2. 無効化前に通知がくることを確認する
まず、無効化前にCloudWatchアラームをトリガーにメール通知が来ることを確認しました。CloudWatchアラームをOK状態からアラーム状態に遷移させてみました。
以下のようにアクションが有効になっています
であることを確認して、アラーム状態に遷移させました。
メールが送付されました。
履歴タブからもアクションが実行されたことを確認できました。
3. 無効化後に通知がこないことを確認する
無効化される時間を待ち、EventBridge Schedulerにより、CloudWatchアラームが無効化されたことを確認しました。
確かに、アクションが無効になっています
となっています。
CloudWatchアラームをOK状態からアラーム状態に遷移させてみました。
このとき、メールは送付されませんでした。確かにアクションも実行されていませんでした。
4. 再有効化後に通知がくることを確認する
有効化される時間を待ち、EventBridge Schedulerにより、CloudWatchアラームが再び有効化されたことを確認しました。
CloudWatchアラームをOK状態からアラーム状態に遷移させてみました。
メールが送付されました。
最後に
Amazon EventBridge Schedulerを使って、CloudWatchアラームの計画的なダウンタイム設定を簡単に実装できるよう、CloudFormationのテンプレートを作ってみたのでご共有しました。
ユニバーサルターゲットでのAPIの指定方法は最初はユーザーガイドを読んで使い方を理解する必要がありますが、各種サービスのAPIリファレンス記載のAPIと合わせて様々な操作が自動化できとても良いですね。
より実践的には、Amazon EventBridge Schedulerのデッドレターキュー(DLQ)を使ってみたのようにデッドレターキューの設定をしておくと、エラー発生時に原因調査が楽になります。
CloudWatchアラームの計画的なダウンタイム設定がしたい方や、EventBridge SchedulerのスケジュールのCloudFormationテンプレートのサンプルが欲しい方の参考になりましたら幸いです。
補足
使用したLambda関数について
AWS CDK v2で作成しました。
以下のようにプロジェクトを作成しました。
# CDK用のディレクトリの作成 mkdir cdk_cw_lambda # CDKプロジェクトの初期化 cd cdk_cw_lambda cdk init sample-app --language typescript # Lambda関数のスクリプト用のディレクトリの作成 mkdir lambda_function
以下のようにLambda用のスタックを作成するためのスクリプトを作成しました。
import { Duration, Stack, StackProps } from 'aws-cdk-lib'; import { aws_iam as iam, aws_lambda as lambda, } from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class CdkCwLambdaStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); const lambdaPolicy = new iam.ManagedPolicy(this, 'LambdaPolicy', { managedPolicyName: 'lambdaPolicy', description: 'Lambda execution policy', statements: [ new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEvents'], resources: ['*'], }), ], }); const lambdaRole = new iam.Role(this, 'lambdaRole', { roleName: 'lambdaRole', assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), }); lambdaRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchFullAccess')); const sampleLambda = new lambda.Function(this, 'cm-nayuts-sample-lambda', { runtime: lambda.Runtime.PYTHON_3_9, handler: 'function.lambda_handler', code: lambda.Code.fromAsset('lambda_function'), role: lambdaRole }); } }
Lambda関数で実行するPythonスクリプトは以下のようにしました。value
を変えることでCloudWachメトリクスにUnitがCountのメトリクスをPUTできます。
import os import json import boto3 cloudwatch = boto3.client("cloudwatch") def lambda_handler(event, context): """ """ value = 1 cloudwatch.put_metric_data( MetricData=[ { "MetricName": "CmSmapleMetric", "Dimensions": [ { "Name": "DimensionNAME", "Value": "SampleDimension", }, ], "Unit": "Count", "Value": value, }, ], Namespace="SampleCustomNamespace", ) return
以下のようにcdk deploy
でデプロイしました。
cdk deploy --profile プロファイル名
--profile
オプションについては、AWS CDK Toolkit (cdk command) - AWS Cloud Development Kit (AWS CDK) v2のSpecifying Region and other configurationをご確認ください。
使用したCloudWatchアラームおよびSNSトピックについて
以下のCloudFormationテンプレートで作成しました。
AWSTemplateFormatVersion: "2010-09-09" Description: CloudWatch Alarm Sample Stack Parameters: SampleDestinationEmail: Description: Destination Email for SNS Type: String Resources: # CloudWatchアラーム用のSNSトピック CloudWatchAlarmTopic: Type: AWS::SNS::Topic Properties: TopicName: CmNayutsCloudwatchAlarmTopic Subscription: - Endpoint: !Sub ${SampleDestinationEmail} Protocol: email # CloudWatchアラームの定義 LambdaFunctionErrorsAlarm: Type: AWS::CloudWatch::Alarm Properties: AlarmName: CmNayutsSampleAlarm Namespace: SampleCustomNamespace Dimensions: - Name: DimensionNAME Value: SampleDimension MetricName: CmSmapleMetric ComparisonOperator: LessThanThreshold # 閾値以上 Period: 60 # 期間[s] EvaluationPeriods: 1 # 閾値を超えた回数 Statistic: Maximum # 最大 Threshold: 1 # 閾値 TreatMissingData: notBreaching # Errorsがない場合は良好として扱う AlarmActions: - !Ref CloudWatchAlarmTopic # アラーム遷移時のアクション
テンプレートはCloudFormation で AWS Chatbot と CloudWatch Alarm を定義してSlackにアラート通知してみたを参考に作成しました。