最低限の労力でLaravelのタスクスケジュール ( Task Scheduling ) + AWS ECS
※ ある程度LaravelのCommandやECSを利用している人向けの記事です
※ イメージにはphp:7.3-fpm-alpine
ベースで、他にミドルウェアを色々入れたものを利用
※ Laravelは6.0を利用
EC2 + cron + Laravelで動いていた定期処理実行サーバを、ECSに移すことになりました。
以前から、WebはECS + Laravel (+ Nginx)で動かしてました
まあ、インフラ管理方法が2種類あるのは負担でしかないですよね。。。
それで、方法を考えると、ECSの"タスクのスケジューリング"だろうとは思ったのですが
- たくさんECSのスケジュールタスクを作るのは面倒...
- CloudFormationを書くには、このプロジェクトは遅すぎた。cloudformationの運用も面倒...
そんなわけで、Laravelのタスクスケジュールと、ECSの”タスクのスケジューリング”を組み合わせてみました。
両方似たような名前で頭がオカシクなりそうです。
注意事項
これから紹介する方法は、以下を許容できる処理に限定した方が良いと思います
- スケジュールが多少いい加減で良い
- イメージの内容によるが、コンテナ(タスク)が立ち上がるまでに数十秒かかる
- 時間に対応したスケジュールが一個ずつ実行されるため、全ての処理が完了するまでに時間がかかる
- ちなみに
runInBackground()
を利用したら処理完了前にコンテナが終了すると思います(戒め)
- ちなみに
- 時間がかかる処理では、同じコマンドが並列実行されるリスクがある
- つまり、コンテナが多重起動している状態もあるということ
- 多重でScheduleを実行させないための実装がLaravelにあり
-
onOneServer()
,withoutOverlapping()
-
onOneServer()
の方が良さそう(小並感)
-
- コンテナ(ECSタスク)の多重起動をECS側で禁止する方法もあるが、大事なタイミングでコマンドが実行されない危険性があるので、おすすめしない
重要な処理は、個別にECSタスク定義とECSスケジュールタスクを作った方が良いかと思います。
(同じタスク定義を利用しても設定でCMDを上書きできます。しかし、タスクの設定でタスク定義のリビジョンを上げるたびに再度指定することになります。おすすめしません。)
全体像
Laravelのタスクスケジュールで、定期実行の設定を記述
以下を参考に、淡々と記述すればOK。
https://readouble.com/laravel/6.0/ja/scheduling.html
EC2なんかでcronですでに設定されていれば、cron()
使えば楽にスケジュールを指定可能。
ECSのスケジュールタスクを設定
- ECSでLaravelが動くDockerイメージが作成できていれば、それを利用してバッチ処理用のタスク定義を作成
- タスク定義では、コンテナ設定でCMDを
php artisan schedule:run
に変更- すでにLaravelがPHP-FPMとかで動くイメージを利用する場合、起動時の動作(CMD)を上書きできる状態に変更
- ENTRYPOINTが指定されていた場合、ENTRYPOINTは実行時に上書きできないし、CMDはENTRYPOINTの引数扱いとなり、実行時の挙動を上書きできない
- ベースがphp-fpmのイメージなら、DockerfileでENTRYPOINTを
[]
に上書き(無効化)して、CMDを["docker-entry-point","php-fpm"]とかに変えればいいと思います
- すでにLaravelがPHP-FPMとかで動くイメージを利用する場合、起動時の動作(CMD)を上書きできる状態に変更
- タスク定義では、コンテナ設定でCMDを
ECSで"タスクのスケジューリング"を設定
上記のタスクを毎分起動するように設定。
(おまけ) エラー通知
例外発生時には、通知はもちろんスタックトレースとかも確認できるようにしたい。
LaravelのScheduleには、before, after, onFailuereといったフックがあるんですが、出力を参照できないっぽいから、こいつらは大した事はできないです。これはすごい悩んだ。
いやまあ、杞憂でしたけどorz。
エラー時には、普通にHandlerに引っかかります。
つまり、app/Exceptions/Handler.php
内の処理。
既にSlack通知なりが設定されてれば、ECSスケジュールタスクでの例外発生時にも、同様にSlack通知されます。
定期処理だけ特別な処理をしたいなら、タスク定義に定期処理フラグ的な環境変数でも適当に設定して、その環境変数の値をもってHandlerの処理を分岐させれば良いと思われます。