概要

ECS Taskを定期実行したい要件があり、それをただ実行するならEventBridgeからcronのように呼び出すことになる。
しかし、ECS Taskそのものにはリトライ処理が無く、EventBridgeだけだと複数のジョブの処理などに限界があったので、今回はStepFunctionsを検証した。その辺りをざっくりまとめる。
なお、今回はAWSコンソールの作業のみでIaCは検証のため無し。

EventBridgeのリトライ処理について

EventBridgeでもリトライ処理はできるが、意図したエラーハンドリングが出来ない可能性がある。
例えば、ECS Taskでアプリが例外を吐いてもリトライされない。
おそらくECS Taskそのものが起動しないなどのエラーだけをリトライ処理できると思われるので、何をトリガーにリトライ処理をしたいかは確認しておく必要がある。
アプリのエラーについては素直にStepFunctionsを使うと良さそう。

下記の記事で詳しく記載されていた。
ちなみに自分の場合、FargateのECS Taskでも同様だったので、EventBridge自体の制約だと思われる。

[AWS] EventBridge Rules による ECS Scheduled Task はエラー時リトライできない

要件

  • 自分で作成したコンテナイメージを使いたい
  • コンテナイメージにコマンドを上書きして、実行させたい(イメージを共通化)
  • リトライ処理したい
  • 定期実行したい

コンテナイメージ

今回はシンプルにこんなコードを用意した。
コードの良し悪しはさておき、引数を取って、各関数を実行させられるかの確認。

<?php
function main(){
    global $argc;
    global $argv;
    print("start: run php test function\n");

    if ($argc != 2){
        print("info: please set argument\n");
        return;
    }

    $argval = $argv[1];
    print($argval());
}

function example_1(){
    print("result: exmaple 1\n");
}

function example_2(){
    print("result: exmaple 2\n");
}

main();
?>

Dockerfileはこんな感じ。

FROM php:8.2

COPY main.php /usr/local/src/app/main.php
WORKDIR /usr/local/src/app

CMD ["php", "main.php"]

ECRとECS

まずはECRとECSを作成しておく。
ECRには先程のDockerfileをビルドしてPushしておく。

ECSは最小構成で、クラスター、タスク定義とサービスまでOK.

StepFunctions

ECS Run Taskを選択して、下記JSONを設定する。

{
    "LaunchType": "FARGATE",
    "Cluster": " arn:aws:ecs:ap-northeast-1:XXXXXX:cluster/example",
    "TaskDefinition": "example:1",
    "Overrides": {
        "ContainerOverrides": [
            {
                "Name": "example",
                "Command.$": "$.commands"
            }
        ]
    },
    "NetworkConfiguration": {
        "AwsvpcConfiguration": {
            "Subnets": [
                "subnet-XXXXXX"
            ],
            "SecurityGroups": [
                "sg-XXXXX"
            ],
            "AssignPublicIp": "ENABLED"
        }
    }
}

ポイントとしては、パラメーターでOverrideしている箇所。
公式ドキュメント通りで、個人的にこのStepFunctionsの書き方が分かりにくいのだけど、commandsで渡した配列が、タスクのCommandに渡されるようになる。

https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/connect-ecs.html

    "Overrides": {
        "ContainerOverrides": [
            {
                "Name": "example",
                "Command.$": "$.commands"
            }
        ]

リトライ処理をコンソールでポチポチ設定すると下記のようなjsonが追加される。

      "Retry": [
        {
          "ErrorEquals": ["States.ALL"],
          "BackoffRate": 5.0,
          "IntervalSeconds": 1,
          "MaxAttempts": 2
        }
      ]

実行する際には下記のようにコマンドを渡せばOK。

{
    "commands": ["php", "./main.php", "example_2"]
}

ポイント: buildとIAMロールに注意

最初手で実行した際にいくつかエラーが出た。
そのうちの一つがexec /usr/local/bin/docker-entrypoint.sh: exec format error
これは開発環境がM1 Macだったことによる起因。
build時にplatformを指定すればOK.

docker build --platform linux/amd64 . -t XXXXX:latest

次に、IAM PassRoleでエラーになった。
これは下記の記事がドンピシャ。
ECSのタスク実行ロールとタスクロールを許可するポリシーを作って、StepFunctionsで実行する際に使用されているRoleにアタッチしてあげれば解決した。

Step FunctionsからECS RunTaskしようとしたら「ECS.AccessDeniedException」と出た時の対処法

EventBridge

EventBridgeの定期実行の設定は、今はスケジューラという箇所で設定できるようになっている(と最近知った)。

結果はCloudWatchLogsかStep Functionsで確認

下記はCloudWatchLogsで確認。Step Functionsの画面でも確認できるので、どちらも見ておくと良さそう。

まとめ

シンプルなECS TaskをStep Functionsで動かして、EventBridgeから呼び出す方法を書いた。
個人的には、Step FunctionsをEventBridgeから呼び出せるし、ECS Taskも実行できると知って、とても知識の手段が幅が広がったと感じてる。
適材適所で対応していきたい。