CloudWatch Alarmでアラーム発生時にStep Functionsステートマシンを起動する
こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、CloudWatch Alarmでアラーム発生時にStep Functionsステートマシンを起動する方法を確認してみました。
ステートマシンはどこから起動できるか
まず、なるべく簡潔な構成にするという前提で、アラーム発生時にステートマシンをどのようにして起動できるかを確認してみます。
CloudWatch Alarmの場合
まずCloudWatch Alarmからステートマシンを直接起動できるか。
CloudWatch Alarmのアクションの設定を見ると、ターゲットとしてSNS Topicしか指定できないようです。
ドキュメントを見る限りでもアラームアクションへのSNS Topic以外のサービスの指定については記述がありません。
よってCloudWatch Alarmからはステートマシンの直接起動はできないようです。
SNS Topicの場合
続いてCloudWatch Alarmのアクションの唯一の発行先となるSNS Topicからは起動できるのか。
Topicのサブスクリプションの設定を確認すると、サブスクライブするエンドポイントのプロトコルは以下が指定可能となっています。ステートマシンは一覧にありません。
- HTTP/HTTPS
- Email/Email-JSON
- Amazon Kinesis Data Firehose
- Amazon SQS
- AWS Lambda
- プラットフォームアプリケーションエンドポイント
- SMS
このうち聞き慣れないものとしてプラットフォームアプリケーションエンドポイント
がありますが、こちらはモバイルアプリケーションのエンドポイントに通知を送信する際に使用するものとのことです。
なのでステートマシンのArnを指定しようとしてもエラーとなります。
考えられる構成
よって現時点で考えられる、アラーム発生時にステートマシンを起動させる最も簡単な構成は以下となりそうです。(※)
※2022/5 追記:Amazon EventBridgeを使用した構成であればさらに簡略化できました。記事末尾に詳細記載しています。
実装
上記構成を実装してみます。
CDKコード
import * as cdk from '@aws-cdk/core'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as cloudwatchActions from '@aws-cdk/aws-cloudwatch-actions'; import * as sns from '@aws-cdk/aws-sns'; import * as lambda from '@aws-cdk/aws-lambda'; import * as lambdaNodejs from '@aws-cdk/aws-lambda-nodejs'; import { SnsEventSource } from '@aws-cdk/aws-lambda-event-sources'; import * as iam from '@aws-cdk/aws-iam'; export class AwsCdkAppStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); //SNS Topic const cloudwatchAlarmNotifyTopic = new sns.Topic( this, 'cloudwatchAlarmNotifyTopic' ); //CloudWatch Alarm const sampleAlarm = new cloudwatch.Alarm(this, 'sampleAlarm', { alarmName: 'sampleAlarm', metric: new cloudwatch.Metric({ namespace: 'StateMachinePublish', metricName: 'temperature', }), evaluationPeriods: 1, threshold: 5, statistic: cloudwatch.Statistic.MAXIMUM, comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD, period: cdk.Duration.minutes(3), }); //Alarmアクション追加 sampleAlarm.addAlarmAction( new cloudwatchActions.SnsAction(cloudwatchAlarmNotifyTopic) ); //ステートマシン const stateMachine = new sfn.StateMachine(this, 'sampleStateMachine', { stateMachineName: 'sampleStateMachine', definition: new sfn.Pass(this, 'pass', {}), }); //Lambda関数 const startStateMachineFunc = new lambdaNodejs.NodejsFunction( this, 'startStateMachineFunc', { functionName: 'startStateMachineFunc', entry: 'src/lambda/startStateMachineHandler.ts', handler: 'handler', runtime: lambda.Runtime.NODEJS_14_X, environment: { STATE_MACHINE_ARN: stateMachine.stateMachineArn, }, } ); //ステートマシン起動権限付与 startStateMachineFunc.addToRolePolicy( new iam.PolicyStatement({ actions: ['states:StartExecution'], resources: [stateMachine.stateMachineArn], }) ); //Lambdaイベントソース追加 startStateMachineFunc.addEventSource( new SnsEventSource(cloudwatchAlarmNotifyTopic) ); } }
Lambdaコード
import * as AWS from 'aws-sdk'; const STATE_MACHINE_ARN = process.env.STATE_MACHINE_ARN!; const sfn = new AWS.StepFunctions(); export const handler = async () => { await sfn .startExecution({ stateMachineArn: STATE_MACHINE_ARN, }) .promise(); };
動作
監視対象のメトリクスがしきい値を下回り、CloudWatch Alarmがアラーム状態となりました。
するとLambda実行を介してステートマシンがちゃんと起動しました。
まとめ
- CloudWatch Alarmのアラーム発生時にステートマシンを起動したい場合、現在取りうる最も簡潔な構成は、
CloudWatch Alarm -> SNS topic -> Lambda関数 -> ステートマシン
となる。 - せめてSNS topicからステートマシンを直接起動できるようになると嬉しい。
2022/5 追記:Amazon EventBridgeを使用した方が良い場合もある
Amazon EventBridgeのRuleを使用するとさらに構成を簡略化することができました。
ただし注意点として、EventBridge Ruleからステートマシンを起動する場合は任意の実行名を指定できません。指定したい場合は本記事で紹介した方法の方が良いかと思います。
参考
- amazon web services - How to call a step funtion from Node.js Lambda function? - Stack Overflow
- AWS SNSからLambda連携をCDKで
- Amazon CloudWatch SyntheticsによるURL監視のアラームをメール通知してみた(AWS CDK) | DevelopersIO
以上