背景
DROBEでは、日々数千のバッチが動いているのですが、そのほとんどがSchedule Taskで実行されています
ECS の Schedule Task (Fargate) と Laravel
DROBE では cron として実行したい Application にまつわる処理を ECS の Schedule Task を利用して運用しています
info.drobe.co.jp

その中でコンテナを立ち上げる途中のネットワークエラーやOOMに強制終了させられるなど、稀にインフラ起因で失敗し、PHP側では検知できないケースがあり、下記のような構成で異常終了時に slack に通知を行うようにしました
特に ecs では、終了した際一定時間しかログを記録しないため、エラーの原因を追いにくく、現状の構成ではエラーの検知およびログの保存が必要でした
※ aws chatbot を用いてもっとお手軽に通知が可能なのですが、カスタマイズが難しく、今回はlambda を使って通知を実装することになりました
導入
今回は terraform を用いて環境を作ったので tf ファイルの中身を公開しつつ簡単に説明します
まずは EventBridge です
ECS の任意のイベントを検知することができます
event_pattern で filter をかけることができるのですが、下記のパターンを検知するようにしました
- ECS Task State Change (タスクのステートが変更した時にとぶもの)
- cluster は 本番で使っているもの
- ステータスが終了状態
- 終了コードが存在しない もしくは exitCode が 0 以外
- reason が
Scaling activity initiated by以外で失敗している場合
次にlambda です
通知するためのSLACKのwebookのURLなどは環境変数として渡してあげるため、Lambda を作るときに 環境変数を定義しています
一応ログも出すようにしました
Lambda を作ったので eventBridge の target にも入れてあげました
resource "aws_cloudwatch_event_target" "ecs_event_handler" {
rule = aws_cloudwatch_event_rule.ecs_event_handler.name
arn = aws_lambda_function.ecs_event_notification.arn
}そしてとってもハマったのが下記で、lambda の起動には aws_lambda_permission によって↑に起動の権限を追加してあげる必要があるのですが、statement_id や principal などが typo していてもリソースは作られてしまうので、一見ちゃんと作られているけどなぜか lambda が実行されない... という事態に遭遇してだいぶ苦しみました
resource "aws_lambda_permission" "lambda_ecs_event_notification" {
statement_id = "AllowExecutionFromEventBridge"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.ecs_event_notification.function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.ecs_event_handler.arn
}lambda のコードはこちら
eventBridge の event をうけとって整形し、slack に post しているだけです。
環境変数は事前に aws_lambda_function.ecs_event_notification で定義してあげる必要があります
あまり情報が多くても不便なので、下記を通知するようにしてみました
- TASK定義
- 終了理由
- オーバーライドしたコマンド(DROBEにおいて、schedule taskは特定のタスク定義のコマンドを上書きして使っています)
- 終了コード
- 詳細へのリンク
こちらを apply し、エラーが発生すると下記のように通知が飛んできます 🙌
終わりに
lambda と slack を使って ecs の異常終了を検知する方法を紹介しました
同じような悩みを抱えている方の参考になれば幸いです