この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
定期実行しているAWS Step Functionsステートマシンが想定時間内に正常終了したか、EventBridge SchedulerとLambdaを使って監視する方法を紹介します。
前提とするステートマシン
依存関係のあるステートマシンや日次バッチが時間内に完了したか確認したいケースを想定し、ステートマシンの実行間隔が十分に長い定期実行は想定していますが、同じステートマシンがオーバーラップして実行されることは想定していません。
具体的には、1日に1回呼び出され、処理に3時間かかるステートマシンは想定内ですが、2時間間隔で呼び出され、処理に3時間かかり、実行がオーバーラップするようなステートマシンは想定外です。
また、EXPRESS ステートマシンも対象外です。
監視方法
Step Functions には ListExecutions という API が存在し、実行履歴を最新のものから時系列順に取得できます。
ステートマシンが午前0時に呼び出され、2時間には終わっているはずであれば、午前2時にLambda関数で最新のステータスを確認し、正常終了していなければ、SNSに通知します。
以上をまとめたCloudFormationテンプレートを本文最後に記載しています。
StepFunctions:ListExecutions API について
StepFunctions:ListExecutions APIを利用すると、実行履歴を時系列順に取得できます。
最新の実行だけを取得したい場合、 maxResults=1
と個数を指定します。
各実行のステータスは以下のいずれかの値をとります。
- SUCCEEDED
- RUNNING
- FAILED
- TIMED_OUT
- ABORTED
SUCCEEDED
ステータスだけが正常終了です。
RUNNING
ステータスは遅延を意味し、残りのステータスは異常終了です。
AWS CLI からは、以下のように呼び出します。
$ aws stepfunctions list-executions \
--state-machine-arn arn:aws:states:eu-west-1:12345789012:stateMachine:XXX \
--max-items 1
Python SDKからは、以下のように呼び出します。
sfn = boto3.client('stepfunctions')
response = sfn.list_executions(
stateMachineArn = "arn:aws:states:eu-west-1:12345789012:stateMachine:XXX",
maxResults = 1
)
latest_activity = response['executions'][0]
status = latest_activity['status']
if status != 'SUCCEEDED':
# TODO:異常系処理
pass
定期呼び出し方法
EventBridge SchedulerあるいはEventBridge Ruleを利用すると、簡単に定期呼び出しできます。 後発のEventBridge Schedulerは、実行ウィンドウやタイムゾーンの指定など、より柔軟な設定が可能です。
各ステートマシンには
- ステートマシンの実行
- ステートマシン実行の監視
の2種類のスケジュール設定が必要です。
EventBridge SchedulerはたくさんのAWS APIをターゲットとして直接呼び出せます。
残念ながら、StepFunctions:ListExecutions APIは対象外だったため、Lambdaを挟んでいます。
通知
最新の実行が正常終了していなかった場合、SNS等を利用してエラー通知しましょう。
異常終了に限定すると、デッドレターキュー(DLQ)を利用すると、異常終了を速やかに検知できます。
CloudWatchメトリクスは利用しづらい
CloudWatchメトリクスには、ExecutionsSucceeded
やExecutionsFailed
やExecutionTime
のように、各ステータスに対応するメトリクスが存在します。
ステータス毎にメトリクスが異なり、処理が遅延(RUNNING
)している場合、実際に処理が終了するまで処理時間(ExecutionTime
)のデータポイントが発生しななくて監視できないことから、今回のようにSUCCEEDED
以外のすべてのステートを補足したいユースケースには向かないでしょう。
最後に
AWS Step Functionsステートマシンの実行をStepFunctions:ListExecutions APIを使って監視する方法を紹介しました。
実行スケジュールがシンプルであれば、それなりにカバーできるのではないかと思います。
それでは。
付録:CloudFormationテンプレート
今回紹介した構成のCloudFormationテンプレートです。
監視対象のステートマシンとエラー通知先のSNSトピックはすでに存在するものとします。
Lambdaに渡すステートマシンはEventBridgeのInputに利用し、Lambdaから送信するSNSトピックはLambdaの環境変数で管理しています。
このテンプレートでは
- EventBridge Scheduler
- EventBridge Rule
の2種類のスケジュールを作成しています。
好きな方を残してください。
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SnsArn:
Type: String
SfnArn:
Type: String
Resources:
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
Handler: index.lambda_handler
Code:
ZipFile: !Sub |
import boto3
import os
sfn = boto3.client('stepfunctions')
sns = boto3.client('sns')
SNS_ARN = os.environ['SNS_ARN']
def lambda_handler(event, context):
sfn_arn = event['SFN_ARN']
response = sfn.list_executions(
stateMachineArn = sfn_arn,
maxResults = 1
)
print(response)
if response['executions']:
latest_activity = response['executions'][0]
status = latest_activity['status']
if status != 'SUCCEEDED':
print("last activity didn't succeed")
sns.publish(
TargetArn=SNS_ARN,
Message='state machine {} is in the state {}'.format(sfn_arn, status)
)
else:
print("last activity succeeded")
else:
print("No acitivity found")
Runtime: python3.9
Timeout: 30
Environment:
Variables:
SNS_ARN : !Ref SnsArn
Role: !GetAtt LambdaFunctionRole.Arn
LambdaFunctionRole:
Type: AWS::IAM::Role
Properties:
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: function-nsn-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Resource: !Ref SnsArn
Effect: Allow
Action:
- sns:Publish
- Resource: "*"
Effect: Allow
Action:
- states:ListExecutions
EventSchedule:
Type: AWS::Scheduler::Schedule
Properties:
GroupName: default
ScheduleExpression: cron(0 0 * * ? *)
ScheduleExpressionTimezone: Japan
FlexibleTimeWindow:
Mode: "OFF"
State: ENABLED
Target:
Arn: !GetAtt LambdaFunction.Arn
Input: !Sub '{"SFN_ARN": "${SfnArn}"}'
RoleArn: !GetAtt EventScheduleRole.Arn
EventScheduleRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- scheduler.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
- PolicyName: CallStepFunctions
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- lambda:InvokeLambda
Resource:
- !GetAtt LambdaFunction.Arn
EventBridgeRule:
Type: AWS::Events::Rule
Properties:
EventBusName: default
ScheduleExpression: cron(0 15 * * ? *)
State: ENABLED
Targets:
- Arn: !GetAtt LambdaFunction.Arn
Id: LambdaFunction
Input: !Sub '{"SFN_ARN": "${SfnArn}"}'
LambdaEvent:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref LambdaFunction
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt EventBridgeRule.Arn
AWS::Scheduler::Schedule
の Mode: "OFF"
に関して、ダブルクオートを外して Mode: OFF
とすると、次のエラーが発生します。
Properties validation failed for resource EventSchedule with message: #/FlexibleTimeWindow/Mode: #: only 1 subschema matches out of 2 #/FlexibleTimeWindow/Mode: failed validation constraint for keyword [enum]
ご注意ください。