ECSのRunTaskはECSの起動を保証するわけではないという話

こんにちは。ASKULのほかほかごはんです。現在は商品チームで主にバッチアプリケーションの開発を担当しています 😀

本記事では AWS上でのECSを利用したバッチアプリケーション開発で遭遇した運用上の問題について記述します。

RunTask でECSを起動する

ASKUL 商品チームではAWS上のイベント (S3での新規オブジェクト作成イベントなど) でLambdaを起動し、 そのLambdaからECS (Fargate) を起動するというジョブを運用しています。
ECSの起動はRunTaskで実行します。次の例はGoのサンプルコードになります。
なお、本記事のサンプルコードは簡単のため一部を省略しています。

// ECSを起動するLambda Codeのイメージ
func invoke(_ context.Context, event events.S3Event) {
    // 前略
    c := ecs.NewFromConfig(Config())
    res, err := c.RunTask(context.Background(), runTaskInput)
 
    // RunTaskのerrorをcheck
    if err != nil {
        log.Fatalf("[ERROR] RunTask Error. err=[%v]\n", err)
    }

    // EC2起動タイプを指定していてコンテナインスタンスのリソースが空いていない場合Failuresが出力される
    if len(res.Failures) > 0 {
        for _, f := range res.Failures {
            log.Printf("[INFO] RunTask Failure. Reason=[%s] Detail=[%s]\n", *f.Reason, *f.Detail)
        }
        log.Fatalf("[ERROR] RunTask Error\n")
    }
}

RunTaskのエラーをしっかりとチェックしているので一見すると問題ないのですがここに落とし穴があります。

RunTaskが正常終了した場合でもECSが起動に失敗するケースがあります 😢 これはタスクをスケジューリングすることはできたものの、AWS上でのその後の処理で予期せぬエラーが発生したケースが該当します。

DescribesTasksでStoppedReasonを確認する

起動失敗の理由については taskArnを指定してDescribeし、StoppedReasonを見ることで特定できます。

res, err := c.DescribeTasks(context.Background(), describeTasksInput)
if err != nil {
    log.Fatalf("[ERROR] DescribeTasks Error. err=[%v]\n", err)
}

for _, task := range res.Tasks {
    if task.StoppedReason != nil {
        log.Printf("[INFO] ECS stopped. reason=[%s]\n", *task.StoppedReason)
    }
}

アプリケーションが自身でプログラムを終了した場合はEssential container in task exitedとなります。 一方で、Timeout waiting for network interface provisioning to completeなどのエラーの場合、起動に失敗したことを示します。

失敗したECSのリカバリーについて

理想的な解決方法はStep Functionsを利用することです。 Step Functionsからジョブを実行することで自動リトライを実現できます。

しかし、現実問題として、本番で稼働しているジョブをサクッと置き換えることは難しいです。 なので、ASKUL商品チームでは暫定対応としてスケジューリングしたECS Taskを一定時間Describeして正常に起動したことを担保する対応を導入しました。

一定時間で正常動作を確認できなかった場合アラートをSlackに飛ばすようにしています 🙃
現状、アラートが上がった後の対応が手運用になっています 😣
実際にこのようなエラーが発生することはほとんどないのですが、アプリケーション運用の自動化の促進のために今後処理自体をStep Functionsに切り替えていく予定です ✨ こちらも機会があれば記事にしたいと思います!

We are hiring!!

ASKUL 商品チームでは一緒に働く仲間を募集しています! 次のリンクよりお気軽にご応募ください。

https://hrmos.co/pages/askul/jobs/1000012

参考

https://aws.amazon.com/jp/premiumsupport/knowledge-center/ecs-fargate-network-interface-errors/ https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/connect-ecs.html

ASKUL Engineering BLOG

2021 © ASKUL Corporation. All rights reserved.