Tweet
Logo
    ECS の Schedule Task (Fargate) と Laravel
    ECS の Schedule Task (Fargate) と Laravel

    ECS の Schedule Task (Fargate) と Laravel

    • はじめに
    • Schedule Task とは
    • Schedule Task を使う事による利点
    • Schedule Task のつらみ
    • Laravel と一緒に使う時のデザインパターン
    • Schedule task からの呼び出し
    • 多重起動対策
    • その他

    はじめに

    DROBE では cron として実行したい Application にまつわる処理を ECS の Schedule Task を利用して運用しています

    この記事では Fargate Schedule Task を Laravel でどう使っているかや管理方法などを記載します

    Schedule Task とは

    ECS の Schedule task は、cron のようなスケジュールされた Task を起動してくれる機能です

    タスクのスケジューリング (cron)

    「翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。」 Amazon ECS は、 cron のようなスケジュール、または CloudWatch イベント に応答してタスクをスケジュールする機能をサポートしています。この機能は、Fargate および EC2 の両方の起動タイプを使用する Amazon ECS タスクでサポートされています。 バックアップオペレーションやログスキャンなど、クラスター内で一定間隔で実行するタスクがある場合は、Amazon ECS コンソールを使用して、指定した時間にクラスターで 1 つ以上のタスクを実行する CloudWatch イベント ルールを作成できます。スケジュールされたイベントルールは、特定の間隔 ( N 分、時間、日ごとに実行) に設定できます。または、より複雑なスケジュールに設定する場合は、 cron 式を使用することもできます。詳細については、 Amazon CloudWatch Events ユーザーガイドの「 ルールのスケジュール式 」を参照してください。 また、Fargate タスクを CloudWatch イベント のタスクターゲットとして設定できるようになりました。これにより、発生した変更に応答してタスクを起動することができます。さらに、CloudWatch イベント コンソールおよび AWS CLI を介して awsvpc ネットワークを使用するときに、ネットワーク設定を修正でき、CloudWatch イベント によってトリガーされた Fargate

    docs.aws.amazon.com

    DROBE では Cloud Watch の Event をトリガーとして、Fargate の Task を起動しています

    Schedule Task を使う事による利点

    Schedule Task を利用して Fargate を cront として活用する最大の利点は cpu や memory などのリソースが task 毎に完全に独立となるので、原理的に task 同士が干渉しずらくなる事だと考えています。例えばプログラムのミスでどこかの task が大量に memory を使ってしまったとしても、それが原因で他のタスクに影響が出るという事はほぼありません

    また Fargate は 1 秒辺りで課金されるので、cron 用に数台サーバーを常時持っていくよりも安くなる可能性もあります

    料金 - AWS Fargate | AWS

    AWS Fargate では、前払いは発生せず、使用しているリソースに対してのみ料金が発生します。コンテナ化されたアプリケーションで消費される vCPU およびメモリリソースの量に対する料金が発生します。 料金は、タスク または Pod のために要求された vCPU およびメモリリソースに基づきます。2 つの料金体系は単独で設定可能です。 Fargate の使用状況が一定の場合、Savings Plans が活用できます。Savings Plans を利用して、1 年間または 3 年間、特定量のコンピューティング使用量 (1 時間あたりのドル単位で測定) で契約をしていただくと、AWS Fargate の使用料金を最大 50% 節約できます。 料金は 1 秒あたりで、最低 1 分です。時間は、コンテナイメージのダウンロード (docker pull) を開始した時刻からタスクが終了するまでで計算され、最も近い秒数に切り上げられます。 コンテナで他の AWS のサービスを使用したり、データを転送したりする場合は、追加料金がかかります。例えば、コンテナがアプリケーションのログ記録に Amazon CloudWatch Logs を使用する場合は、CloudWatch の使用量に対して課金されます。 AWS サービス料金の詳細については、該当する AWS サービスの詳細ページにある料金セクションを参照してください。よく使用されるサービスの料金表へのリンクを以下に示します。 データ転送: 標準の

    aws.amazon.com

    料金 - AWS Fargate | AWS

    Schedule Task のつらみ

    一見いい事ずくめの Schedule task ですが、一つ大きなつらみがあります。それは task の多重起動問題です

    これは公式でも言及されており、残念ながら暫くは仕様として飲み込むしかなさそうだと考えています

    まれに、単一のイベントまたはスケジュールされた期間に対して同じルールを複数回トリガーしたり、特定のトリガーされたルールに対して同じターゲットを複数回起動したりする場合があります。
    CloudWatch イベント のトラブルシューティング

    Amazon EventBridge is the preferred way to manage your events. CloudWatch イベント and EventBridge are the same underlying service and API, but EventBridge provides more features. Changes you make in either CloudWatch or EventBridge will appear in each console. For more information, see Amazon EventBridge.

    docs.aws.amazon.com

    多重起動をインフラレベルで抑えるすべが無いとすると、アプリケーションレイヤーで対策を行うしかありません。DROBE に於いては以下のような対策を取っています

    1. Task で実行される処理は冪等性が担保されるように書く
    2. 多重起動されても大丈夫なように Task の処理にロックを取る

    1 はそもそも実行が失敗するなどもあり得るので頑張ってやっていこうという話だと考えています

    この記事では以降 2 の Task 処理のロックについて書いていきます

    Laravel と一緒に使う時のデザインパターン

    ここから Laravel と Schedule task を使う場合のデザインについて書いていきます

    Schedule task からの呼び出し

    まず Schedule task から任意の処理をどうやって呼び出すかですが、DROBE では Laravel の Artisan command を自作して、それを schedule task から叩いてもらうようにしています

    Artisan Console

    Artisan is the command line interface included with Laravel. Artisan exists at the root of your application as the artisan script and provides a number of helpful commands that can assist you while you build your application.

    laravel.com

    Artisan command はオプションや引数も受け取れるので、必要に応じて schedule task からそれらを受け取れるように汎用的に作る事も可能です

    多重起動対策

    多重起動対策としては redis の atomic lock 機能を利用しています

    Cache

    Some of the data retrieval or processing tasks performed by your application could be CPU intensive or take several seconds to complete. When this is the case, it is common to cache the retrieved data for a time so it can be retrieved quickly on subsequent requests for the same data.

    laravel.com

    atomic lock を使う事で分散環境においてもレースコンディションを心配する事なくロックを取る事が可能です

    具体的には以下のようなコードでロックを取っています

    $lockSec は task が終わるまでの時間によって増減させるのが良いと思います

    その他

    Schedule task の多重起動は、経験上最初の起動の 1 ~ 2 分後に次のタスクが起動してしまうといった事もありました

    その場合は、例えば task の処理がすぐに終わってしまったり、次のタスクの起動前に $lockSec 以上の時間がたってしまったりすると 2 つめの task の実行が始まってしまいます

    これに関してはあまり良い対策が思いついていないというのが正直な所で、 $lockSec の調整とアプケーション側の冪等性に頼るという辛みを抱えた運用になっています

    © 2025 DROBE All rights reserved.
    // lock を取る処理
    public static function runIfNotRunning(string $cacheKey, callable $func, int $lockSec = 120)
    {
    		$lockResult = Cache::lock($cacheKey, $lockSec);
    		if ($lockResult['locked']) {
    				$func(); // ここで処理を実行する
        } else {
            Log::info("{$cacheKey} is already running. do nothing.");
        }
    }
    
    // 使う側
    self::runIfNotRunning("key", function() {
        // task 内で行いたい処理
    });