※この記事は「AWSでサーバーレスなバッチ処理を作るハンズオン」の4章です。こちらの記事からスタートできます。
ここでは、作成してきたLambda関数を束ねるStep FunctionsのState Machine、およびEvent Bridgeを作成していきます。
リポジトリ
この章における変更箇所は以下のコミットで確認できます。
4章 EventBridgeでStateMachineをトリガー、デプロイ · SRsawaguchi/simple-serverless-batch@448e3e8 · GitHub
※この章でsamconfig.toml
というファイルを作成しますが、こちらは自動生成するためコミットしていません。(通常の開発ではコミットしてください。)
また、このハンズオン全体のリポジトリはこちらです。
State Machineの作成
では、State Machineを作成していきましょう。
今回はMakeReportFunction
が成功したらHtmlToPdfFunction
を呼び出すようなState Machineを作ります。
State Machineの定義を作成
まずは、State Machineの定義を追加します。
State Machineの定義はJSONを拡張したASL(Amazon States Language)で記述します。
touch statemachine/make_report.asl.json
statemachine/make_report.asl.json
には、以下のようなJSONを書き込みます。
{ "Comment": "StateMachine of Make Report Tasks", "StartAt": "Make Report Html", "States": { "Make Report Html": { "Type": "Task", "Resource": "${MakeReportFunctionArn}", "Next": "Convert Html to Pdf" }, "Convert Html to Pdf": { "Type": "Task", "Resource": "${HtmlToPdfFunctionArn}", "End": true } } }
template.yamlへの追加
State Machineのリソースと、それに紐付けるIAM Role(Lambda関数をinvokeする権限を付与)、そしてログを閲覧するためのLogGroupが必要です。
template.yaml
のResources
に以下を追加します。
MakeReportStateMachineLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: "simple-serverless-batch" RetentionInDays: 60 StatesExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - !Sub states.${AWS::Region}.amazonaws.com Action: "sts:AssumeRole" Path: "/" Policies: - PolicyName: StatesExecutionPolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "lambda:InvokeFunction" Resource: "*" - Effect: Allow Action: - "logs:CreateLogDelivery" - "logs:GetLogDelivery" - "logs:UpdateLogDelivery" - "logs:DeleteLogDelivery" - "logs:ListLogDeliveries" - "logs:PutResourcePolicy" - "logs:DescribeResourcePolicies" - "logs:DescribeLogGroups" Resource: "*" MakeReportStateMachine: Type: AWS::Serverless::StateMachine Properties: DefinitionUri: statemachine/make_report.asl.json DefinitionSubstitutions: MakeReportFunctionArn: !GetAtt MakeReportFunction.Arn HtmlToPdfFunctionArn: !GetAtt HtmlToPdfFunction.Arn Logging: Level: "ERROR" IncludeExecutionData: true Destinations: - CloudWatchLogsLogGroup: LogGroupArn: !GetAtt MakeReportStateMachineLogGroup.Arn Role: !GetAtt StatesExecutionRole.Arn
State Machineの実行
AWSでは、Step Functions ローカルを提供しています。
これを使って先ほど作成したState Machineをローカルで実行してみましょう。
docker-compose.yaml
のservices
セクションに以下の記述を追加します。
stepfn: image: amazon/aws-stepfunctions-local:latest hostname: stepfn depends_on: - dynamodb - minio ports: - "8083:8083" environment: LAMBDA_ENDPOINT: http://host.docker.internal:3001 DYNAMODB_ENDPOINT: http://dynamodb:8000
LAMBDA_ENDPOINT
はローカル上のLambdaのエンドポイントですが、これはSAMが提供します。
Step Functionsローカルは、このエンドポイントにホストマシンを経由してアクセスできます。
では、以下のコマンドでローカルにLambdaを起動します。
sam local start-lambda \ --docker-network ssb-handson \ --parameter-overrides \ DynamoDBEndpoint=http://dynamodb:8000 \ S3BucketName=handson-bucket \ S3Endpoint=http://minio:9000 \ MinioRootUser=root \ MinioRootPassword=himitsu123
この状態で、AWS CLIを使ってState Machineを作成します。
ここで、前回作成したmake_report.asl.json
を使いたいところですが、これはCloud Formationで動作することが前提になっているため使えません。
具体的には、${HtmlToPdfFunctionArn}
などの置換が必要な箇所があるからです。
ですので、やや冗長ですがローカルでテストするとき向けに置換が含まれないState Machineの定義を作成する必要があります。
make_report.asl.json
をコピーしてmake_report_local.asl.json
を作成します。
cp statemachine/make_report.asl.json statemachine/make_report_local.asl.json
make_report_local.asl.json
の中身は以下のようになります。
Resource
の内容をダミーのARNに置き換えます。
ダミーのARNはarn:aws:lambda:us-east-1:123456789012:function:
にtemplate.yaml
に記載した関数の論理名を連結させればOKです。
{ "Comment": "StateMachine of Make Report Tasks", "StartAt": "Make Report Html", "States": { "Make Report Html": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:MakeReportFunction", "Next": "Convert Html to Pdf" }, "Convert Html to Pdf": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:HtmlToPdfFunction", "End": true } } }
では実行してみましょう。
docker compose up
でStep Functionsローカルなどのコンテナが起動していることを確認してください。
続いて、AWS CLIを使ってStep FunctionsローカルにStateMachineを登録します。
aws stepfunctions \ --endpoint http://localhost:8083 \ create-state-machine \ --name "MakeReportStateMachine" \ --definition file://statemachine/make_report_local.asl.json \ --role-arn "arn:aws:iam::012345678901:role/DummyRole"
以下のコマンドでStateMachineを実行します。
--name
に$(uuidgen)
を利用していますが、これは実行ごとに重複した名前をつけるためです。
実行するたびに手動で名前を考える手間を削減する目的です。
aws stepfunctions start-execution \ --endpoint http://localhost:8083 \ --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:MakeReportStateMachine \ --name $(uuidgen)
無事に実行できればMinio上にPDFが作成されているはずです。(過去に実行した際にできたPDF等があるかもしれないので、Minioコンソールから削除しておくと良いでしょう。)
AWSにデプロイ
ここまで動作確認できたので、AWSにデプロイしましょう。
その前に、HTMLやPDFをアップロードするためにS3バケットを1つ作成しておいてください。
template.yaml
にS3バケットを記述することで、Cloud Formation上でバケットを作成できます。
しかし、今回は簡単なサンプルなので、S3バケットはtemplate.yaml
に記載せず、これとは別に作成してください。
※S3バケットをtemplate.yaml
に記載する場合、「S3バケットを作成するかどうか」の制御をする必要がある。
sam deployの実行
S3バケットを作成したら、早速デプロイしていきましょう。
sam build sam deploy --guided
これは質問に答えながらデプロイするコマンドです。
それぞれ以下のように回答しましょう。
質問 | 回答 | 備考 |
---|---|---|
Stack Name | simple-serverless-batch | |
AWS Region | ap-northeast-1 | そのままEnter |
Parameter TableName | Messages | そのままEnter |
Parameter DynamoDBEndpoint | そのままEnter | |
Parameter S3Endpoint | そのままEnter | |
Parameter S3BucketName | <作成したS3バケット名 > | |
Parameter MinioRootUser | そのままEnter | |
Parameter MinioRootPassword | そのままEnter | |
Confirm changes before deploy | y | |
Allow SAM CLI IAM role creation | Y | |
Disable rollback | N | |
Save arguments to configuration file | Y | |
SAM configuration file | samconfig.toml | そのままEnter |
SAM configuration environment | default | そのままEnter |
これにより、デプロイが開始されます。
今回はConfirm changes before deploy
をy
で回答しているため、デプロイが実行される前に作成、変更されるリソースの一覧が表示されます。
内容を確認して、問題なければy
で回答してデプロイを実行します。
デプロイ状況はAWSマネジメントコンソールのCloud Formationのページからも確認できます。
なお、先ほど質問の結果はsamconfig.toml
に保存されています。質問への回答を変更したい場合は、このファイルを編集します。
samconfig.toml
の内容を使ってのデプロイは以下のようにします。
sam deploy --config-file ./samconfig.toml
これ以降はこのコマンドを使ってデプロイしていきましょう。
なお、デプロイでErrorが発生した後、修正して再度デプロイする場合にはCloud Formationのスタックを削除する必要があります。
その場合、以下のコマンドを実行します。
aws cloudformation delete-stack \ --stack-name simple-serverless-batch
このコマンドを実行すると、このスタック内(template.yaml
に定義した)のリソースが全て削除されます。
デプロイ後に動作確認したり検証したりした後は余分に課金されるのを防ぐために早めにスタックの削除を行うことをオススメします。
DynamoDBテーブルにitemを追加
さっそく実行したいところですが、その前にDynamoDBテーブルにitemを追加しておきます。
aws dynamodb put-item \ --table-name Messages \ --item '{ "Date": {"S": "2021/11/28"}, "Message": {"S": "Hello, World"} }'
追加されたかどうか確認してみましょう。
aws dynamodb get-item \ --table-name Messages \ --key '{"Date": {"S": "2021/11/28"}}'
以下のようにitemが返ってくればOKです。
{ "Item": { "Date": { "S": "2021/11/28" }, "Message": { "S": "Hello, World" } } }
AWSマネジメントコンソールのStepFunctionsからStateMachineを実行
では、先ほど作ったStateMachineを実行します。
AWS CLIでやっても良いですが、視覚的に分かりやすいためAWSマネジメントコンソールからやります。
AWSマネジメントコンソールにアクセスし、Step Functionsのページに行きます。
そこから、MakeReportStateMachine-*
のStateMachineを見つけてアクセスし、「実行の開始」ボタンをクリックします。
今回はデータを与える必要がありませんのでその後に表示されるフォームは変更不要です。
すると、StateMachineの実行が開始されます。
全てのStateが「成功」(緑色)になることを確認してください。
あとはS3バケットに2021_11_28.pdf
および2021_11_28.html
が保存されていることを確認しましょう。
Event Bridgeを追加
前回はState Machineを手動で実行しました。ここではEvent Bridgeを使って指定した時間になったら自動でState Machineを実行する構成にします。
今回は、平日の19時に先ほど作成したState Machineを起動するようEvent Brigeを設定しましょう。
以下の2つのリソースが必要になります。
- Event Bridgeのイベント
- State Machineを実行する権限が付与されたIAM Role
template.yamlにリソースを追加
template.yaml
に以下を追加します。
CallStateMachineRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: events.amazonaws.com Action: "sts:AssumeRole" Path: "/" Policies: - PolicyName: InvokeStepFunction PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "states:StartExecution" Resource: !GetAtt MakeReportStateMachine.Arn CallStateMachine: Type: AWS::Events::Rule Properties: Name: "CallMakeReportStateMachine" Description: "Event for call MakeReportStateMachine(StepFunctions)." ScheduleExpression: cron(0 10 ? * MON-FRI *) State: ENABLED Targets: - Arn: !GetAtt MakeReportStateMachine.Arn RoleArn: !GetAtt CallStateMachineRole.Arn Id: CallStateMachine
時刻を指定してる箇所はCallStateMachine
の中にあるScheduleExpression
です。
ここはCron式で指定します。(他にもRate式が使えます。)
今回のCron式を解剖すると以下のようになります。
時
はUTCで指定することに注意してください。
JST(日本標準時)はUTCより9時間進んでいます。
そのため、19
時を指定する場合は10
と指定します。
Cron式の詳細は公式ドキュメントを参照してください。豊富なExamplesが掲載されているため、要件に近いものを参考にすると良いです。
Schedule Expressions for Rules - Amazon CloudWatch Events
この後デプロイしますが、Cron式を修正して今すぐ検証しやすい時刻に設定すると良いでしょう。
デプロイ
デプロイしましょう。
sam deploy --config-file ./samconfig.toml
あとは、先ほど設定した時刻になったらStateMachineが実行され、成功したことを確認しましょう。
AWSマネジメントコンソールでStep Functionsのページにアクセス、当該StateMachineの「実行」の欄を確認するとよいです。
リソースの削除
余計な課金が発生するため、必ずAWSリソースを削除してください。
ここまで実行して確認できたら、今回作成したAWSリソースもう不要です。
今回template.yaml
で作成したAWSリソースを全て削除します。
aws cloudformation delete-stack \ --stack-name simple-serverless-batch
なお、これを実行してもS3バケットは削除されません。
template.yaml
に記述したリソースではないからです。
今回使ったS3バケットが不要である場合は、それぞれ別途削除してください。
次のステップ
これまで簡単のために雑多に書いてきたLambda関数をリファクタリングして、ユニットテストおよびIntegrationテストを実装していきます。