こんにちは。SCSKのふくちーぬです。
Amazon CloudWatch AlarmのターゲットにAmazon Simple Notification Service (Amazon SNS) だけではなく、AWS Lambdaを指定できるようになったことを知っていますでしょうか。
今回は、Amazon CloudWatch Alarm のターゲットに AWS Lambda をターゲットとしたメトリクス監視をご紹介します。またリソースの構築には、AWS CloudFormation を利用します。
CloudWatch AlarmのターゲットにLambdaがサポートされた件
以前までは、CloudWatch Alarmはアラームアクションとして、SNSやAutoacalingの増減やEC2インスタンスの起動/停止、Systems Manager Incident Manager でインシデントの起票が可能でした。
カスタムアクションを実施する際には、一度SNSトピックに送信した後にLambdaでサブスクライブする必要がありました。
2023/12/22に発表されたアップデートにより、直接Lambdaをターゲットとすることが可能になりました。
使いどころ
今回のアップデートによりSNSを経由することなく、カスタムアクションを容易に実施することができます。SNSを挟む必要がなくなったため、その分のリソースを構築する必要がなくなったことは大きなメリットとなります。CloudWatch Alarmのアラート内容をLambdaにて加工し、その後にSNSへ送信する処理をLambda内で完結して実行できます。
SNSを挟むパターンとしては、他の複数のサービス(Lambda・SQS・E-Mail等)へ送信する要件があったり、リトライ処理を入れ込むこと等が対象となってくると思います。
検証
実際に、CloudWatch AlarmのターゲットにLambdaを指定してアラート内容の出力結果を確認します。
アーキテクチャー
- LambdaのConcurrenExecutions(同時実行数)メトリクスの監視をする
- アラーム状態の場合、Lambdaをターゲットとして起動する
- Lambdaの処理では、アラート内容を専用のロググループに出力する
CloudFormationテンプレート
下記のリソースを作成するCloudFormationテンプレートです。
- CloudWatch アラーム
- Lambda
- Lambda リソースポリシー
- ロググループ
- IAMロール
以下のテンプレートをデプロイしてください。
AWSTemplateFormatVersion: 2010-09-09 Description: To trigger Lambda with a CloudWatch Alarm Parameters: ResourceName: Type: String Resources: # ------------------------------------------------------------# # CloudWatch Alarm # ------------------------------------------------------------# CloudWatchAlarm: Type: 'AWS::CloudWatch::Alarm' Properties: Namespace : AWS/Lambda AlarmName : !Sub ${ResourceName}-Lambda-ConcurrentExecutions-Alarm AlarmActions : - !GetAtt Function.Arn MetricName : ConcurrentExecutions ComparisonOperator : GreaterThanThreshold # 以上を表す EvaluationPeriods : 5 DatapointsToAlarm: 1 Period : 60 Statistic : Maximum Threshold : 100 # ------------------------------------------------------------# # Lambda # ------------------------------------------------------------# Function: Type: AWS::Lambda::Function Properties: FunctionName: !Sub ${ResourceName}-lambda-function Role: !GetAtt LambdaRole.Arn Runtime: python3.12 Handler: index.lambda_handler Environment: Variables: LOGGROUPNAME: Alarm-Lambda-ConcurrentExecutions Code: ZipFile: !Sub | import boto3 import os import time import datetime import json s3_client = boto3.client('s3') cwlogs_client = boto3.client('logs') def lambda_handler(event, context): message = event['alarmData'] LOG_GROUP_NAME = os.environ['LOGGROUPNAME'] localtime = datetime.datetime.now() + datetime.timedelta(hours=9) LOG_STREAM_NAME = localtime.strftime("%Y%m%d%H%M") try: cwlogs_client.create_log_stream( logGroupName = LOG_GROUP_NAME, logStreamName = LOG_STREAM_NAME, ) response = cwlogs_client.put_log_events( logGroupName = LOG_GROUP_NAME, logStreamName = LOG_STREAM_NAME, logEvents = [ { 'timestamp': int(round(time.time() * 1000 )), 'message' : json.dumps(message) } ] ) except cwlogs_client.exceptions.ResourceAlreadyExistsException: pass except Exception as e: print("error",e) raise return response LambdaInvokePermission: Type: 'AWS::Lambda::Permission' Properties: FunctionName: !GetAtt Function.Arn Action: 'lambda:InvokeFunction' Principal: lambda.alarms.cloudwatch.amazonaws.com SourceAccount: !Ref 'AWS::AccountId' SourceArn: !GetAtt CloudWatchAlarm.Arn # ------------------------------------------------------------# # Log Group # ------------------------------------------------------------# FunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/lambda/${Function}" CloudWatchAlarmLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: Alarm-Lambda-ConcurrentExecutions # ------------------------------------------------------------# # IAM Role # ------------------------------------------------------------# LambdaRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${ResourceName}-lambda-role AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: - lambda.amazonaws.com ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
アラームの発砲テスト
任意の端末(Cloud9やCloudShell等)から、以下のコマンドを実行してアラートを発砲させます。<ResourceName>には、スタックデプロイ時に入力したパラメータを挿入します。
aws cloudwatch set-alarm-state --alarm-name <ResourceName>-Lambda-ConcurrentExecutions-Alarm --state-value ALARM --state-reason "alarm test"
アラート内容が正常に出力されていることが確認できました。
容易にCloudWatch AlarmとLambdaを連携することができましたね。
最後に
いかがだったでしょうか。CloudWatch AlarmのターゲットにLambdaを指定して、カスタムアクションを実行しました。
既存の構成でSNSを利用している場合は、SNSを省くことができるかどうか検討をすることをお勧めします。
本記事が皆様のお役にたてば幸いです。
ではサウナラ~🔥