LambdaとCloudWatch Logsを使ったジョブの遅延監視を考えてみる
目次
こんにちは。スカイアーチHRソリューションズのきむです。
LambdaとCloudWatch Logsを使ったジョブの遅延監視についてご紹介させていただきます。
今回作成する構成は以下のようになります。
今回の監視対象となるのはEventBridegeにより日時で実行されるジョブ実行用Lambdaです。
遅延監視用Lambdaを使ってジョブ実行用LambdaのCloudWatch Logsからジョブの実行開始を示すメッセージを探し、対象のログが見つからない場合はジョブの実行遅延とし、SNSで通知を飛ばします。
今回は遅延監視の機能を確認したいため、EventBridegeについては割愛し赤枠の中のリソースを作成していきます。
コードの作成
今回の実行環境はPython3.9です。
ジョブ実行用Lambda
今回はテスト用なのでログだけを出力させる簡易的なコードにしています。
def lambda_handler(event, context):
print("Hello World Job Start")
print("Hello World")
print("Hello World Job End")
return {"statusCode": 200}
遅延監視用Lambda
import json
import os
from datetime import datetime
import boto3
import dateutil
logs = boto3.client("logs")
sns = boto3.client("sns")
# タイムゾーンの設定
UTC = dateutil.tz.gettz("UTC")
JST = dateutil.tz.gettz("Asia/Tokyo")
# 変数設定
start_hour = HH
start_minute = MM
end_hour = HH
end_minute = MM
log_group_name = os.environ.get("log_group_name")
filter_pattern = "Hello World Job Start"
topic_arn = os.environ.get("topic_arn")
def lambda_handler(event, context):
# 監視時刻の設定
today = datetime.now(JST)
start = datetime(
today.year,
today.month,
today.day,
start_hour,
start_minute,
0,
0,
JST,
)
end = datetime(
today.year,
today.month,
today.day,
end_hour,
end_minute,
0,
0,
JST,
)
# UNIXタイムスタンプに変換
start_timestamp = int(start.timestamp() * 1000)
end_timestamp = int(end.timestamp() * 1000)
log_stream_name = [log_stream["logStreams"][0]["logStreamName"]]
log_event = logs.filter_log_events(
logGroupName=log_group_name,
logStreamNames=log_stream_name,
startTime=start_timestamp,
endTime=end_timestamp,
filterPattern=filter_pattern,
)
# 対象のログが存在する場合は正常終了させる。
if log_event["events"]:
return {"statusCode": 200}
# 対象のログが存在しない場合はSNSによる通知を行う。
else:
sns_body = {
"default": f'Time: {datetime.now(JST).strftime("%Y-%m-%d %H:%M:%S")} \n'
f"LogGroup: {log_group_name} \n"
"Messages: ジョブが実行されていません \n"
}
publish = sns.publish(
TopicArn=topic_arn,
Message=json.dumps(sns_body, ensure_ascii=False),
Subject="ログ遅延監視",
MessageStructure="json",
)
return
以下の変数には監視時刻としたいと時間と分をそれぞれ指定してください。
start_hour = HH
start_minute = MM
end_hour = HH
end_minute = MM
SAMテンプレートの作成
SAMを使ってAWSリソースを作成していきます。
今回作成するリソースは以下の通りです。
- ジョブ実行用LambdaのIAMロール
- 遅延監視用LambdaのIAMロール
- ジョブ実行用LambdaのCloudWatchロググループ
- 通知用SNS
- ジョブ実行用Lambda(HelloWorldJobLambda)
- 遅延監視用Lambda(DelayMonitorLambda)
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Parameters:
IamRoleName1:
Type: String
Default: JobLambdaRole
IamRoleName2:
Type: String
Default: DelayMonitorLambdaRole
IamPolicyName1:
Type: String
Default: DelayMonitorLambdaPolicy
LambdaFunctionName1:
Type: String
Default: HelloWorldJobLambda
LambdaFunctionName2:
Type: String
Default: DelayMonitorLambda
SNSTopicName1:
Type: String
Default: DelayMonitorTopic
Resources:
IamRole1:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref IamRoleName1
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
IamRole2:
Type: AWS::IAM::Role
Properties:
RoleName: !Ref IamRoleName2
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
IamPolicy2:
Type: AWS::IAM::Policy
Properties:
PolicyName: !Ref IamPolicyName1
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "logs:*"
Resource: "*"
- Effect: "Allow"
Action: "sns:*"
Resource: "*"
Roles:
- !Ref IamRole2
LogGroup1:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${LambdaFunctionName1}"
RetentionInDays: 3
SNSTopic1:
Type: AWS::SNS::Topic
Properties:
Subscription:
- Endpoint: "通知を飛ばしたいEメールアドレス"
Protocol: email
TopicName: !Ref SNSTopicName1
Lambda1:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Ref LambdaFunctionName1
Description: "ジョブ実行用"
CodeUri: ./functions/job/
Handler: app.lambda_handler
Runtime: python3.9
MemorySize: 256
Role: !GetAtt IamRole1.Arn
Timeout: 10
Architectures:
- x86_64
Lambda2:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Ref LambdaFunctionName2
Description: "遅延監視用"
CodeUri: ./functions/monitor/
Handler: app.lambda_handler
Runtime: python3.9
MemorySize: 256
Role: !GetAtt IamRole2.Arn
Timeout: 300
Architectures:
- x86_64
Environment:
Variables:
log_group_name: !Ref LogGroup1
topic_arn: !GetAtt SNSTopic1.TopicArn
ジョブ実行用Lambdaにはマネージドポリシーである「AWSLambdaBasicExecutionRole」をアタッチしたロールを使用しています。
遅延監視用LambdaにはCloudWatch LogsやSNSの操作が必要になるので、両リソースのフルアクセス権限を付与しています。
遅延監視用Lambdaのコード内でロググループ名とSNSトピックのArnをOS環境変数から取得するようにしているのでSAMテンプレート内で設定をしています。
テスト実行
それではテストを行っていきます。
まずはHelloWorldJobLambdaを実行しログを出力させます。
21:00:53に監視対象のログが出力されました。
次にDelayMonitorLambdaを実行します。
ログイベントの検索期間を21:00-21:05としました。
対象期間内にログが出力されているため正常に終了しました。
次はログイベントの検索期間を21:01-21:05とし、SNS通知が飛んでくるかを確認します。
対象期間内に監視対象のログが存在しない場合は、戻り値なしとしているためレスポンスがnullとなっています。
通知先に設定したメールの受信ボックスを確認してみましょう。
遅延監視を知らせるSNS通知も届きました。
おわりに
お疲れさまでした。
メトリクスフィルターやサブスクリプションフィルターで特定のログが出力された場合の監視は行えますが、
ログが出力されていない場合の監視について頭を悩ませていました。
今回はAWSのリソースだけで完結する監視の仕組みを紹介させていただきました。
この記事が少しでも皆様のご参考になれば幸いです。