こんにちは。AS部の兼安です。
今回の記事は、こちらの記事からの続きです。
前回は、AWS SAMを使って、Step Functionsのステートマシンを作成しました。
今回は、同じくAWS SAMを使ってステートマシンからAmazon ECS on Fargateのタスクを実行するようにしてみます。
タスクはバッチ処理的な内容であることをイメージして書いています。
- 本記事の対象者
- Amazon ECSのサービスとタスク
- 作成するステートマシンの全体像
- リソースの事前作成とパラメータの追加
- Amazon ECS用のIAMロールの追加
- Amazon ECS用のパラメータをステートマシンの定義ファイルまで渡す
- Amazon ECS on Fargateを実行するステートの追加
- コンテナのコマンドの上書き
- 最後に
今回の記事で挙がっているファイルは3つです。
この3つのファイルは下記のリンクから参照可能にしているので、併せて読んでいただけるとより理解しやすいと思います。
本記事の対象者
- AWS SAMを使って、Step Functionsのステートマシンを作成したことがある方
- Amazon ECS on Fargateを使ったことがある方
- コンテナ技術の概念を知っている方
Step Functionsの説明は前回の記事を見ていただけたら幸いです。
ECSとFargateの説明はあまりしていませんのでご了承ください。
Amazon ECSのサービスとタスク
ECSにはクラスターとタスク定義という概念があります。
クラスターを作成して、その画面をみると「サービス」と「タスク」があります。
今回はバッチ処理的なものを実行するので、「タスク」を使います。
端折った説明ですが、ECSのクラスター
に対して、タスク定義
を元にタスク
を実行するという流れです。
これでバッチ処理的な動きをさせることができます。
作成するステートマシンの全体像
前回の記事で参照したAWSの公式サンプルをベースとしてAmazon ECS on Fargateのタスクを実行するステートマシンを作成します。
下記のサイトにサンプルのソース構成も載っていますのでご覧ください。
今回作るステートマシンの全体像はこちらです。
前回のステートマシンはRecord Transaction
までだったので、最後にECSを実行するステートを追加した形ですね。
そして、ECSで例外が発生した場合に、例外処理用のステートに分岐するようにします。
例外処理の分岐先は何もしないPass
という種類のステートです。
あくまで分岐を確認するためのステートです。
リソースの事前作成とパラメータの追加
ステートマシンからECSを起動するには以下のものを事前に用意しておく必要がります。
- ECSクラスターのARN
- ECSタスク定義のARN
- ECSのコンテナ名
- ECSタスクを動かすためのVPCサブネットID
- ECSタスクに付与するVPCセキュリティグループID
- ECSタスクを実行するためのIAMロールのARN
- ECSタスクに付与するIAMロールのARN
これらはマネジメントコンソールで作ってもいいですし、CloudFormationで作成してもいいです。
今回は、AWS SAMテンプレートの外で作成し、ステートマシンを作成するAWS SAMテンプレートにパラメータとして渡すことにします。
AWS SAMテンプレートの上部Parameters
セクションを追加します。
Parameters: ClusterArn: Type: String TaskDefinitionArn: Type: String ContainerName: Type: String SubnetId: Type: String SecurityGroupId: Type: String TaskExecutionRoleArn: Type: String TaskRoleArn: Type: String
これで、外部からパラメータを受け取ることができるようになりました。
次に、サンプルコード群の中にあるsamconfig.toml
にパラメータを追加します。
# More information about the configuration file can be found here: # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html version = 0.1 [default] [default.global.parameters] stack_name = "sam-app" [default.build.parameters] cached = true parallel = true [default.validate.parameters] lint = true [default.deploy.parameters] capabilities = "CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND" confirm_changeset = true resolve_s3 = true parameter_overrides = [ "ClusterArn=xxx", "TaskDefinitionArn=xxx", "ContainerName=xxx" "SubnetId=xxx", "SecurityGroupId=xxx", "TaskExecutionRoleArn=xxx", "TaskRoleArn=xxx" ]
parameter_overrides
が追加部分です。
ここにパラメータを追加することで、sam deploy
した時に、template.yaml
にパラメータが渡ります。
xxx
の部分には、事前に作成した各種リソースの情報を入れてください。
capabilities = "CAPABILITY_IAM"
こちらの部分は以下のように変更します。
capabilities = "CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND"
AWS SAMテンプレートに後述の変更を加えた内容を処理するには、CAPABILITY_NAMED_IAM
が必要で、CAPABILITY_AUTO_EXPAND
があるとより反映しやすいからです。
この2つの詳細は下記をご覧ください。
Amazon ECS用のIAMロールの追加
ECSのステートマシンを動かすためのIAMロールを追加します。
元のtemplate.yaml
では、ロールではなく埋め込み型のポリシーを使っていますが、それだとecs:RunTask
あたりがうまく表現できないので、IAMロールを用意することにします。
#---------------------------------------- # IAM Role #---------------------------------------- StockTradingStateMachineRole: Type: AWS::IAM::Role Properties: RoleName: StockTradingStateMachineRole AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - states.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: TaskRunnerStateMachinePolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - ecs:RunTask Resource: - !Ref TaskDefinitionArn - Effect: Allow Action: - ecs:StopTask - ecs:DescribeTasks Resource: - "*" - Effect: Allow Action: - events:PutTargets - events:PutRule - events:DescribeRule Resource: - !Sub arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/StepFunctionsGetEventsForECSTaskRule - Effect: Allow Action: - iam:PassRole Resource: - !Ref TaskExecutionRoleArn - !Ref TaskRoleArn - Effect: Allow Action: - lambda:InvokeFunction Resource: - !GetAtt StockCheckerFunction.Arn - !GetAtt StockSellerFunction.Arn - !GetAtt StockBuyerFunction.Arn - Effect: Allow Action: - dynamodb:PutItem - dynamodb:UpdateItem - dynamodb:BatchWriteItem Resource: - !GetAtt TransactionTable.Arn
作成するIAMロールは、まずこちらを参考にします。
StepFunctionsGetEventsForECSTaskRule
はAmazon EventBridgeにあるAWSのマネージドのルールです。
ここに対する書き込み権限がないとステートマシンが作成できません。 1
iam:PassRole
の部分は上記参考サイトにはありませんが、私の環境ではこれがないとECSタスクが実行できなかったので追加しています。
上述のページにあるこちらに該当していたようです。
スケジュールされた Amazon ECS タスクでタスク実行ロール、タスクロール、またはタスクロールの上書きが必要な場合
そこから下はLambdaの実行権限とDynamoDBの権限で、元からあるものです。
Amazon ECS用のパラメータをステートマシンの定義ファイルまで渡す
ECSを起動するために必要なパラメータをステートマシンの定義JSONファイルに渡すよう追記。
ステートマシンの権限指定をポリシーの書き込みから、ロールの指定に変更。
そして、今回はステートマシンの名前を追加してみます。
下記の#Add
、#Modify
の部分をご覧ください。
StockTradingStateMachine: Type: AWS::Serverless::StateMachine # More info about State Machine Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-statemachine.html Properties: Name: StockTradingStateMachine # Add DefinitionUri: statemachine/stock_trader.asl.json DefinitionSubstitutions: StockCheckerFunctionArn: !GetAtt StockCheckerFunction.Arn StockSellerFunctionArn: !GetAtt StockSellerFunction.Arn StockBuyerFunctionArn: !GetAtt StockBuyerFunction.Arn DDBPutItem: !Sub arn:${AWS::Partition}:states:::dynamodb:putItem DDBTable: !Ref TransactionTable ClusterArn: !Ref ClusterArn # Add ContainerName: !Ref ContainerName # Add TaskDefinitionArn: !Ref TaskDefinitionArn # Add SubnetId: !Ref SubnetId # Add SecurityGroupId: !Ref SecurityGroupId # Add Role: !GetAtt StockTradingStateMachineRole.Arn # Modify
Amazon ECS on Fargateを実行するステートの追加
statemachine/stock_trader.asl.json
を修正します。
まず、ECSを起動する新たなステートを追加します。
そして、一つ前のステートでNext
を使って、ECSのステートに遷移するようにします。
{ 省略 "Record Transaction": { 省略 "Next": "Run ECS task" }, "Run ECS task": { "Type": "Task", "Resource": "arn:aws:states:::ecs:runTask.sync", "Parameters": { "LaunchType": "FARGATE", "Cluster": "${ClusterArn}", "TaskDefinition": "${TaskDefinitionArn}", "NetworkConfiguration": { "AwsvpcConfiguration": { "AssignPublicIp": "ENABLED", "SecurityGroups": [ "${SecurityGroupId}" ], "Subnets": [ "${SubnetId}" ] } }, "Overrides": { "ContainerOverrides": [ { "Name": "${ContainerName}" } ] } }, "Next": "Success", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "Error" } ] }, "Success": { "Type": "Pass", "Comment": "Success", "End": true }, "Error": { "Type": "Pass", "Comment": "Error", "End": true } } }
ステート:Run ECS taskに繋ぐ設定
"Record Transaction": { # 省略 "Next": "Run ECS task" },
この定義で、 ステートRecord Transaction
が終わったら、Run ECS task
に遷移するようになります。
マネジメントコンソールで見るとこのような設定です。
ステート:Run ECS taskで、コンテナをFargateで起動する設定
ステートRun ECS task
で、ECSによるコンテナをFargateで起動し、完了するまで次のステートに遷移しないようにします。
"Run ECS task": { "Type": "Task", "Resource": "arn:aws:states:::ecs:runTask.sync",
同じくマネジメントコンソールで見てみます。
ステートRun ECS task
の設定
タブを開きます。
"Resource": "arn:aws:states:::ecs:runTask.sync",
タスクが完了するまで待機
のチェックを外すとこちらの部分が下のように変化して、ECSの実行が始まるとすぐ次のステートに移るようになります。
"Resource": "arn:aws:states:::ecs:runTask",`
ステート:Run ECS taskの次の指定
"Next": "Success", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "Error" } ] },
この部分はステートRun ECS task
の次のステートを指定しています。
正常終了は、設定
タブの次の状態
で確認できます。
ステート:Run ECS taskのエラー処理
例外発生時の指定は、エラー処理
タブで確認できます。
エラー処理の分岐の設定には名前が付いています。
下記画面のように出るので、鉛筆マークをクリックすると詳細を確認できます。
以上で修正は完了です。
ステートマシンを実行すると、ステートが順次実行され、ECS on Fargateも実行されることが確認できました。
コンテナのコマンドの上書き
前項まででECSでコンテナをタスク実行できました。
しかし、実際の運用ではコンテナというより、コンテナの中で動くプログラムに何らかのパラメータが必要な時があります。
コンテナの中で動くプログラムにパラメータを渡す方法として、コマンドライン引数で渡す方法や環境変数経由で渡す方法があります。
これを実現するのが、ContainerOverride
機能です。
これを使って、コマンドライン引数や環境変数を指定することができます。
今回はコマンドライン引数で渡す方法でやってみます。
ContainerOverride
でコマンドライン引数を渡そうとするには、コンテナの元となっているDockerfile
を確認する必要があります。
ENTRYPOINT [ "python3", "example.py"] CMD ["--arg1", "1", "--arg2", "2"]
Dockerfile
にこのように書いてあるなら、このコンテナは起動すると、python3 example.py --arg1 1 --arg2 2
というコマンドが走ります。
これによりコンテナの中で、example.py
が起動し、それに2つのコマンドライン引数が渡されます。
Dockerfile
のCMD
の部分は、ステートマシン経由でECSコンテナを起動する時に書き換えることができます。
この書き換えの時に使用するのがContainerOverride
です。
statemachine/stock_trader.asl.json
を修正します。
"Overrides": { "ContainerOverrides": [ { "Name": "${ContainerName}", "Command": [ "--arg1", "100", "--arg2", "200" ] } ] }
これで、ステートマシン経由でECSコンテナを起動する時、
python3 example.py --arg1 1 --arg2 2
ではなく、
python3 example.py --arg1 100 --arg2 200
というコマンドが走るようになります。
これによりコンテナの中で動くプログラムに任意のパラメータが渡せます。
最後に
本記事は以上です。
ステートマシンからAmazon ECS on Fargateのタスク実行は、いわゆるバッチ処理をイメージしています。
サーバーレスの技術を駆使すると、常時起動するサーバーがないので、バッチ処理を実行する場所がありません。
このことは、オンプレミスのシステムをクラウド化、さらにサーバーレス化する際の課題の一つです。
AWS Step FunctionsとAmazon ECSの組み合わせは、これを解決する方法の一つとなると思います。
サーバーワークスはDevOpsの支援をしています。お気軽にお問い合わせください。
- 実はStepFunctionsgateventsForECSTaskRuleがなくとも初回だけはステートマシンができることがあります。StepFunctionsgateventsForECSTaskRuleは最初は存在せず、ステートマシンの作成を持って出来あがり、以降それを更新するにはこのルールに対する更新権限が必要という流れだと想像しています。↩
兼安 聡(執筆記事の一覧)
アプリケーションサービス部 DS3課所属
2024 Japan AWS Top Engineers (Database)
2024 Japan AWS All Certifications Engineers
認定スクラムマスター
広島在住です。今日も明日も修行中です。