ECSで運用しているLaravelアプリのbatchを起動させるインフラ構築で詰まった内容まとめ（備忘録）

## はじめに
この記事は、すでにECSで運用されているアプリのbatchのインフラ構築の困った事例とその解決策に関する内容になっています。

アーキテクチャ図は以下の通りです。

![構成図_20221201 のコピー-ページ1のコピー.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555157/7467a86b-145f-f352-4408-58df7b5f3a47.png)


## 詰まった箇所とその解決方法

#### ①cronなどのbatchの機構は本当に必要なのか

###### 詳細
batchを実行するだけなら既に走っているLaravelコンテナで事足りているし、そこで走らせればいいのではと筆者は思っていました。
アプリでできる設定をインフラでしなければならないのがめんどくさいためです。

###### 解決した内容
~~Fargateだとコンテナが負荷に耐えきれず勝手に入れ替りbatch処理が途中で終わることがあるなどの理由から、ecsのデフォルト機能でLaravelによるcron設定ができなくなっていました。
そのためEventBridgeなどアプリ外部の機能（インフラ）により、動作を担保された機構を構築する必要がありました。~~
↑ごめんなさい、こちらは間違ってました。

Laravelのスケジューラーは一度`php artisan schedule:run`を走らせればよしなにcronを組んで指定の日時に勝手にコマンドを走らせてくれるわけではなく、`php artisan schedule:run`が走った時だけ該当する日時のcommandがあったら動いてくれるという仕組みになっているので、別途cronを仕込む必要がありました。

そのため、各コンテナごとにcronを仕込むロジックをDockerfileに仕込んで管理するなら、同じくらいの手間でちゃんと動いているか稼働状況も簡単に監視できるようになっているEventBridgeとStep Functionsを使った方が良いため、このインフラ構築は必要なものでした。

また補足ですが、障害時の調査箇所は、
・アプリによる管理だと`２（スケジューラーとcron） + コンテナ数`箇所
・AWSによる管理だと`２（EventBridgeとStep Functions）`箇所
となりサービスがスケールするにつれて管理工数の差が大きくなってくるため、売上が立っているサービスの場合はスケールを見越してあらかじめインフラでcronを管理した方が良いのかなと思いました。


#### ②batch用のコンテナはどうやって作るのか

###### 詳細
ECRに新しくbatch専用のimageを保存する仕組みが必要かどうかで少しつまづきました。

###### 解決した内容
結論としては不要でした。
Laravelのimageをbatchコンテナとして実行した場合に自分でコンテナを終了させる仕組み(`php-fpm`のshellコマンドを実行しない条件分岐)を仕込むことで、共通のimageから `batchのためのコンテナを作る→コマンドを走らせる→コンテナを維持しない` というbatch専用の機構を作ることができました。

```shell:start.shの該当箇所
〜前略〜

# 環境変数IS_FPMを設定することで、batchとして使用するかアプリとして使用するかを分岐させています。
# 上がアプリで、下がbatchです。
if [ $IS_FPM -eq 1 ] ; then
  php-fpm
else
  echo "Skip run fpm"
fi
```


#### ③batch専用のクラスタを設定しコンテナを立ち上げてみたら異常終了してしまう

###### 詳細
LaravelアプリのECSと同じ設定にしているはずなのに正常に立ち上がらないという現象が起きました。
エラー文を見るとredisに繋がらないから閉じたとなっていました。

###### 解決した内容
まず原因はSecurityGroupsの設定ミスで、具体的にはECSとElastiCacheは同じセキュリティグループにするとエラーとなるからでした。
（言われてみればセキュリティの観点からredisやrdsはアプリのコンテナより狭い穴にする必要があるという理屈は理解できるのですが、当事者になるとなかなか気付けなかったです。）

当初はlocalでよくあるポートの競合だと仮定して調査を進めてしまったため、自分の知らない斜め上からのエラーで調査に苦しみました。

ちなみに[こちらの記事](https://qiita.com/ashley123/items/75489784c11708810e82)のおかげで脱出することができました。


#### ④slackのwebhookで設定した通知が飛んでこない

###### 詳細
Laravelのアプリコンテナでは通常通り使えている通知機能がbatchになると機能しなくなりました。

###### 解決した内容
まず原因はLOG_CHANNELの設定ミスでした。
dev環境だったためslackに飛ばないようになっており、その設定がうまく書き換えられていませんでした。


#### ⑤上記の④の原因調査のためのecs-execがうまくいかない

###### 詳細
④の原因調査中にLOG_CHANNELももちろん当たりをつけて、タスク定義で環境変数の上書きをすることで対応していたのにうまくいきませんでした。

この原因は立ち上げていたタスクのバージョンが古いものになっていたからでした。（使用するタスクはlatestにしていたけど、一度成功した古いタイミングでのlatestを何度も実行していました。）

しかし作業時はその原因がわからず一度環境変数を見るべく、batchコンテナをアプリコンテナとしてecsのサービスと一緒に立ち上げてecs-execをしようとしました。

そしたらbatchコンテナだけecs-execすることができませんでした。

###### 解決した内容
ecsのサービスのCloudFormationに `"enableExecuteCommand": true` を設定してやる必要がありました。
タスク定義のtaskRoleArnで設定されていると思っていたため、別途準備が必要と分からずつまづきました。
そしてこの問題は[こちらの記事](https://qiita.com/ritya/items/15f4a35d77566acff063)を参考に突破することができました。


#### ⑥EventBridgeからStep Functionsを起動できない

###### 詳細
Step Functions,ECSの設定がうまく進みその２つでは起動が成功するようになった後、最後にEventBridgeでcronを仕込んだのですが、Step Functionsが発火しませんでした。

###### 解決した内容
結論としては、ターゲット設定欄の実行ロールをStep Functions用のもので設定してしまっていたためでした。
なのでEventBridge用の実行ロールを設定することで無事動かすことができるようになりました。

## おわりに
作業していて気づいたのは、awsは知っておくことは多くないけど一つの設定ミスでエラーも吐かず全く動かなくなるもので、ミスしている箇所に気付けるかどうかが勝負だということでした。

なので、本記事によって同じ内容はすぐに解決できるようになり、後続の皆さんが悩む時間を少しでも減せたらいいなと思います。