この記事は コドモン Advent Calendar 2023 6日目の記事です!
こんにちは。Webエンジニアの八木です。
コドモンテックブログ初投稿&アドベントカレンダーということもあり、趣味全開の内容にしてみました。
今回の記事では、AWS Step Functionsの挙動をAPIレベルで追ってみます!
AWS Step Functionsとは、様々なAWSサービスを呼び出すことのできる、サーバレスなワークフローサービスです。
他AWSサービスの呼び出し方法にはいくつか種類がありますが、各種方法が実際にどのように連携されているのか気になったため、CloudTrailを使って処理を追ってみました。
TL;DR
- Step Functionsが他のサービスを呼び出すには「最適化された統合」と「AWS SDK統合」の2種類がある
- 「最適化された統合」で同期呼び出しを行うときは、対象アクションを実行後にポーリングを行っている。ただしアクションの終了判定方法は不明
- 「最適化された統合」で非同期呼び出しを行うときは、対象アクションを実行するだけ
- 「AWS SDK統合」で呼び出しを行うときは、対象アクションを実行するだけ
注意事項
- 確認内容は2023/11/15時点のものです
- 今回確認した挙動はドキュメントなどによって公表されているものではないため、暗黙的に変更される可能性があります
- Step Functionsからの呼び出し先サービスやアクションによって挙動が異なる可能性があります
Step Functionsの統合種類
Step Functionsから他のサービスを呼び出す際、「最適化された統合」と「AWS SDK統合」の2種類の呼び出し方法があります。
「最適化された統合」はStep Functionsリリース時から存在する他AWSサービスの呼び出し方法で、他AWSサービスを呼び出すためのカスタマイズされたインターフェースをもっています。
後述の「AWS SDK統合」とは異なり、単なるAPI呼び出しだけでなく、実行の完了まで待機する同期実行が可能です。例えばECSのRunTask APIはタスクの実行を受け付け、実行完了は待たずに即座にレスポンスを返すAPIですが、「最適化された統合」のecs:runTaskアクションは、タスクの実行完了まで待機する、といった動作に変更することが可能です。
「AWS SDK統合」は2021年に追加された新しい呼び出し方法で、AWS SDKなどを経由したAWS APIの呼び出しと同等の機能を持っています。このアップデートのおかげで、200以上のAWSサービスをStep Functionsから呼び出し可能になりました。「最適化された統合」とは異なり、基本的にはAWS APIの呼び出しを行うだけのため、実行完了まで待機する機能は無く、必要な場合は自前のポーリング処理などを実装する必要があります。
気になること
気になった部分が「最適化された統合」の同期実行時の終了判定方法です。呼び出し時は他サービスのAPIを実行しているだけだと思われますが、終了状態の確認はどうしているのでしょうか?
おそらくStep Functions側から実行状態をポーリングしているのだと思うのですが、実際にどのAPIを使用しているのか、ポーリング間隔はどの程度なのか、CloudTrailを使って確認してみます。
また、他の呼び出し方法も同様に検証してみます。
検証
検証内容
ecs:runTaskアクションについて、
- 「最適化された統合」で同期呼び出し
- 「最適化された統合」で非同期呼び出し
- 「AWS SDK統合」で呼び出し
を行った際のAPIイベントをCloudTrailのログから確認し、挙動を追います。
なお、実行するECSタスクは、5分間Sleepする単純なタスクです。
事前準備
【1】CloudTrailの設定
実行の記録のため、まずCloudTrailを設定します。今回取得したいイベントはECSに対するイベントのため、管理イベントのみを取得するように設定します。
$ aws cloudtrail create-trail --name my-trail --s3-bucket-name aws-cloudtrail-logs { "Name": "my-trail", "S3BucketName": "aws-cloudtrail-logs", "IncludeGlobalServiceEvents": true, "IsMultiRegionTrail": false, "TrailARN": "arn:aws:cloudtrail:ap-northeast-1:123456789012:trail/my-trail", "LogFileValidationEnabled": false, "IsOrganizationTrail": false } $ aws cloudtrail start-logging --name my-trail $ aws cloudtrail get-trail-status --name my-trail { "IsLogging": true, "StartLoggingTime": "2023-11-14T00:07:07.585000+09:00", "LatestDeliveryAttemptTime": "", "LatestNotificationAttemptTime": "", "LatestNotificationAttemptSucceeded": "", "LatestDeliveryAttemptSucceeded": "", "TimeLoggingStarted": "2023-11-13T15:07:07Z", "TimeLoggingStopped": "" }
【2】CloudTrailログの検索設定
対象のイベントを絞り込めるよう、CloudTrailログの検索環境を設定します。今回はAthenaで検索できるように、テーブルを作成します。
Athenaテーブル作成クエリ(クリックすると展開されます)
CREATE EXTERNAL TABLE cloudtrail_logs_pp( eventVersion STRING, userIdentity STRUCT< type: STRING, principalId: STRING, arn: STRING, accountId: STRING, invokedBy: STRING, accessKeyId: STRING, userName: STRING, sessionContext: STRUCT< attributes: STRUCT< mfaAuthenticated: STRING, creationDate: STRING>, sessionIssuer: STRUCT< type: STRING, principalId: STRING, arn: STRING, accountId: STRING, userName: STRING>, ec2RoleDelivery:string, webIdFederationData:map<string,string> > >, eventTime STRING, eventSource STRING, eventName STRING, awsRegion STRING, sourceIpAddress STRING, userAgent STRING, errorCode STRING, errorMessage STRING, requestparameters STRING, responseelements STRING, additionaleventdata STRING, requestId STRING, eventId STRING, readOnly STRING, resources ARRAY<STRUCT< arn: STRING, accountId: STRING, type: STRING>>, eventType STRING, apiVersion STRING, recipientAccountId STRING, serviceEventDetails STRING, sharedEventID STRING, vpcendpointid STRING, tlsDetails struct< tlsVersion:string, cipherSuite:string, clientProvidedHostHeader:string> ) PARTITIONED BY ( `timestamp` string) ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe' STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://aws-cloudtrail-logs/AWSLogs/123456789012/CloudTrail/ap-northeast-1' TBLPROPERTIES ( 'projection.enabled'='true', 'projection.timestamp.format'='yyyy/MM/dd', 'projection.timestamp.interval'='1', 'projection.timestamp.interval.unit'='DAYS', 'projection.timestamp.range'='2023/11/13,NOW', 'projection.timestamp.type'='date', 'storage.location.template'='s3://aws-cloudtrail-logs/AWSLogs/123456789012/CloudTrail/ap-northeast-1/${timestamp}')
ちなみに他のログ検索方法としては、CloudTrail Lakeを使用する方法などがあります。
「最適化された統合」で同期呼び出し
さっそく、「最適化された統合」で同期呼び出しがどのように実行状態を確認しているのか、検証してみます。
ECSタスクを同期実行する、以下の定義のステートマシンを実行します。
{ "StartAt": "ECS RunTask", "States": { "ECS RunTask": { "Type": "Task", "Resource": "arn:aws:states:::ecs:runTask.sync", "Parameters": { "LaunchType": "FARGATE", "Cluster": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/my-cluster", "TaskDefinition": "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/hello-world:4", "NetworkConfiguration": { "AwsvpcConfiguration": { "Subnets": [ "subnet-xxxxxxxxxxxxxxxxxxx" ], "AssignPublicIp": "ENABLED", "SecurityGroups": [ "sg-xxxxxxxxxxxxxxxxxxx" ] } } }, "End": true } }, "TimeoutSeconds": 900 }
Step FunctionsのからECSへのAPIイベントをAthenaで確認します。
SELECT eventtime, eventsource, eventname, errorcode, errormessage, useridentity.arn FROM "default"."cloudtrail_logs_pp" where useridentity.arn like 'arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx%' and eventsource = 'ecs.amazonaws.com' and timestamp = '2023/11/15' order by eventtime limit 50;
eventtime | eventsource | eventname | errorcode | errormessage | arn |
---|---|---|---|---|---|
2023-11-15T00:28:25Z | ecs.amazonaws.com | RunTask | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | ||
2023-11-15T00:29:20Z | ecs.amazonaws.com | DescribeTasks | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | ||
2023-11-15T00:30:19Z | ecs.amazonaws.com | DescribeTasks | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | ||
2023-11-15T00:31:22Z | ecs.amazonaws.com | DescribeTasks | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | ||
2023-11-15T00:32:17Z | ecs.amazonaws.com | DescribeTasks | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | ||
2023-11-15T00:33:18Z | ecs.amazonaws.com | DescribeTasks | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx |
RunTaskを実行後、おおよそ1分ごとにDescribeTasksが実行されています。実行開始(RunTask)から5分後以降にDescribeTasksは実行されていないので、この時点では処理が完了していたのだと思われます。
1つ気になる部分は、ECSタスクの終了以降にDescribeTasksが記録されていないことです。タスクが終了するのは、早くとも開始から5分後の 2023-11-15T00:33:25Z
ですが、その時点以降にDescribeTasksイベントは記録されていません。DescribeTasksでポーリングを行っているものの、終了条件の判定には使っていないのでしょうか?
試しにStep FunctionsにアタッチしているIAM権限ロールから、DescribeTasksの権限を削除し、ポーリングが失敗する状態で実行してみます。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ecs:RunTask", "ecs:StopTask", "iam:PassRole" ], "Resource": "*" } ] }
Step Functionsの実行が失敗するだろうと思っていましたが、予想に反して正常に実行終了しました。
なお、権限は不足しているため、DescribeTasksのポーリングは失敗しています。やはりポーリング以外の部分でタスク終了を監視しているようです。
SELECT eventtime, eventsource, eventname, errorcode, errormessage, useridentity.arn FROM "default"."cloudtrail_logs_pp" where useridentity.arn like 'arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx%' and eventsource = 'ecs.amazonaws.com' and timestamp = '2023/11/15' and eventtime >= '2023-11-15T03:30:00Z' order by eventtime limit 50;
eventtime | eventsource | eventname | errorcode | errormessage | arn |
---|---|---|---|---|---|
2023-11-15T03:32:07Z | ecs.amazonaws.com | RunTask | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | ||
2023-11-15T03:33:07Z | ecs.amazonaws.com | DescribeTasks | AccessDenied | User: arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/BmhovYkXBawuygrHoBxDrrpmDaQlMwtW is not authorized to perform: ecs:DescribeTasks on resource: arn:aws:ecs:ap-northeast-1:123456789012:task/my-cluster/9f018ef6137048da851150c5c6670355 because no identity-based policy allows the ecs:DescribeTasks action arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | |
2023-11-15T03:34:03Z | ecs.amazonaws.com | DescribeTasks | AccessDenied | User: arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/rXtGsznUbTGimFIAhYLsHOtyVrgtWEVE is not authorized to perform: ecs:DescribeTasks on resource: arn:aws:ecs:ap-northeast-1:123456789012:task/my-cluster/9f018ef6137048da851150c5c6670355 because no identity-based policy allows the ecs:DescribeTasks action arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | |
2023-11-15T03:35:09Z | ecs.amazonaws.com | DescribeTasks | AccessDenied | User: arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/nuZpJSBJVXHSeZqCoNglIzstShDwGDBj is not authorized to perform: ecs:DescribeTasks on resource: arn:aws:ecs:ap-northeast-1:123456789012:task/my-cluster/9f018ef6137048da851150c5c6670355 because no identity-based policy allows the ecs:DescribeTasks action arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | |
2023-11-15T03:36:04Z | ecs.amazonaws.com | DescribeTasks | AccessDenied | User: arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/SHIGVpHVRQiWHxAyTRuGjQlexzKkTDTj is not authorized to perform: ecs:DescribeTasks on resource: arn:aws:ecs:ap-northeast-1:123456789012:task/my-cluster/9f018ef6137048da851150c5c6670355 because no identity-based policy allows the ecs:DescribeTasks action arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx | |
2023-11-15T03:37:05Z | ecs.amazonaws.com | DescribeTasks | AccessDenied | User: arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xpNxvjkUXAosJUObhhHYKxcOQJxVEaPx is not authorized to perform: ecs:DescribeTasks on resource: arn:aws:ecs:ap-northeast-1:123456789012:task/my-cluster/9f018ef6137048da851150c5c6670355 because no identity-based policy allows the ecs:DescribeTasks action arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxx |
終了判定にもつかっていないとなると、なぜポーリングしているのかもわからないですね。結局謎に包まれたままでした。。。
ちなみに私の記憶が正しければ、半年ほど前はECSタスクが終了しても、Step Functions側は状態が変わらずタイムアウトするという挙動でした。変更があったのかもしれません。
「最適化された統合」で非同期呼び出し
「最適化された統合」でのRunTaskを非同期実行でも挙動を確認してみます。
以下の定義のステートマシンを実行します。
同期実行のステートマシンから、 "Resource": "arn:aws:states:::ecs:runTask.sync"
の .sync
がなくなっています。
{ "StartAt": "ECS RunTask", "States": { "ECS RunTask": { "Type": "Task", "Resource": "arn:aws:states:::ecs:runTask", "Parameters": { "LaunchType": "FARGATE", "Cluster": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/my-cluster", "TaskDefinition": "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/hello-world:4", "NetworkConfiguration": { "AwsvpcConfiguration": { "Subnets": [ "subnet-xxxxxxxxxxxxxxxxxxx" ], "AssignPublicIp": "ENABLED", "SecurityGroups": [ "sg-xxxxxxxxxxxxxxxxxxx" ] } } }, "End": true } }, "TimeoutSeconds": 900 }
AthenaでCloudTrailログを確認したところ、以下のような結果が得られました。
SELECT eventtime, eventsource, eventname, errorcode, errormessage, useridentity.arn FROM "default"."cloudtrail_logs_pp" where useridentity.arn like 'arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-async-role-xxxxxxxxxxxx%' and eventsource = 'ecs.amazonaws.com' and timestamp = '2023/11/15' order by eventtime limit 50;
eventtime | eventsource | eventname | errorcode | errormessage | arn |
---|---|---|---|---|---|
2023-11-15T00:28:33Z | ecs.amazonaws.com | RunTask | arn:aws:sts::123456789012:assumed-role/StepFunctions-optimized-integration-async-role-xxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxx |
仕様通り、非同期実行の場合はAPIを呼び出したあとに終了を待たず、即座にステップが終了していることがわかります。特にポーリングなどは行っていません。
「AWS SDK統合」で呼び出し
最後に「AWS SDK統合」でRunTaskを呼び出す、以下の定義のステートマシンを実行します。
AWS SDK統合のため、アクションの指定を "Resource": "arn:aws:states:::aws-sdk:ecs:runTask"
に変更しています。
{ "StartAt": "ECS RunTask", "States": { "ECS RunTask": { "Type": "Task", "Resource": "arn:aws:states:::aws-sdk:ecs:runTask", "Parameters": { "LaunchType": "FARGATE", "Cluster": "arn:aws:ecs:ap-northeast-1:123456789012:cluster/my-cluster", "TaskDefinition": "arn:aws:ecs:ap-northeast-1:123456789012:task-definition/hello-world:4", "NetworkConfiguration": { "AwsvpcConfiguration": { "Subnets": [ "subnet-xxxxxxxxxxxxxxxxxxx" ], "AssignPublicIp": "ENABLED", "SecurityGroups": [ "sg-xxxxxxxxxxxxxxxxxxx" ] } } }, "End": true } }, "TimeoutSeconds": 900 }
AthenaでCloudTrailログを確認したところ、以下のような結果が得られました。
SELECT eventtime, eventsource, eventname, errorcode, errormessage, useridentity.arn FROM "default"."cloudtrail_logs_pp" where useridentity.arn like 'arn:aws:sts::123456789012:assumed-role/StepFunctions-aws-sdk-integration-role-xxxxxxxxxx%' and eventsource = 'ecs.amazonaws.com' and timestamp = '2023/11/15' order by eventtime limit 50;
eventtime | eventsource | eventname | errorcode | errormessage | arn |
---|---|---|---|---|---|
2023-11-15T00:28:40Z | ecs.amazonaws.com | RunTask | arn:aws:sts::123456789012:assumed-role/StepFunctions-aws-sdk-integration-role-xxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxx |
Step FunctionsからはRunTaskのAPI呼び出しのみが行われています。「最適化された統合」の非同期呼び出しと同じく、API呼び出しを行ったあと、実行完了を待たずステップを終了しています。
「最適化された統合」の非同期呼び出しと動作に違いがないため、サポートされているアクションの場合は、機能豊富な「最適化された統合」を選択すると良さそうです。
最後に
この記事では、CloudTrailを使ってStep Functionsの挙動を追ってみました。今回はわかりやすさを重視し、ecsに対するログのみを抽出しましたが、実はIAMなど別サービスを呼び出したりもしていました。こうして挙動を追っていくと、AWSサービスの内部が透けて見えたりとなかなか面白いです。
また、CloudTrailはセキュリティ的側面のサービスですが、AWSオタクにとってはAPIレベルでの挙動を確認できるマニアックなサービスでもあります。
みなさんも気になるサービスの挙動があれば、CloudTrailを使って追ってみてはいかがでしょうか?
以上、八木でした!