こんにちは。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を省くことができるかどうか検討をすることをお勧めします。
本記事が皆様のお役にたてば幸いです。
ではサウナラ~?



