STORES 予約 エンジニアの水野です。STORES 予約 ではこれまでにEC2で稼働していたシステムのECS移行を進めてきました。このECS移行については過去に前編と後編でブログにしておりますので興味のある方はぜひこちらもお読みいただけると幸いです。
ECS移行はほぼ完了していますが唯一定期実行バッチだけその多くがEC2で動き続けていました。これらバッチの実行環境もECSに移行すると完全にEC2を撤廃することが可能となります。そしてEC2に伴う以下のデメリットも解消することができます。
- バッチが要求する最大のCPU/Memoryのスペックで常時起動しておく必要がありコスト効率が悪い
- セキュリティアップデートやAMIの作成など運用・保守のコストがかかる
- EC2が残っているためEC2用のデプロイ設定が残り続けている
そのため年内にバッチのECS移行もやりきることを目標に作業を進めました。
移行後
EC2上ではRubyのgemであるwheneverでcronを設定しバッチを定期実行していました。移行後はEventBridge SchedulerとStep Functionsを利用して定期実行しています。簡単に説明するとEventBridge Schedulerで設定した時間にStep Functionsを実行、Step FunctionsがECSタスクを起動しバッチを動かすためのコンテナを立ち上げるという処理をしています。
この構成はほぼSTORES ネットショップのバッチ基盤と同様です。
バッチの実行中にのみECSタスクを起動し処理が終了すればコンテナ毎破棄しているので不要なコストを抑えることができます。
この方法でバッチの定期実行をするにあたって考慮したことが2つほどありました。
AWSリソースのキャパシティーエラーを可能な限り避ける
まれにAWSリソースの確保ができずECSタスクの実行が失敗する場合があります。
ref: https://repost.aws/knowledge-center/ecs-fargate-runtask-capacity
とくにキリの良い時間、例えば0分や30分など世のバッチが多く実行されることが予想される時間帯はリソース確保の失敗の確率が高くなります。過去にはこれが原因で失敗していた例が実際にありました。
タスクの実行に失敗したらStep Functionsでリトライさせればいいのですが可能な限り失敗自体を避けることが理想的です。そこで厳密な実行時間が不要なバッチについてはEventBridge Schedulerの「柔軟な時間枠」を利用し実行タイミングを散らすようにしました。
Terraformでは以下のように設定できます。
resource "aws_scheduler_schedule" "scheduled_task" { ... // 柔軟な時間枠の設定 flexible_time_window { mode = "FLEXIBLE" maximum_window_in_minutes = 10 } }
実行間隔の短いバッチはSidekiqに定期実行させる
実行間隔の短いバッチの場合はそれだけ多くStep Functionsの起動からコンテナ構築までを行うことになります。これはコンテナの起動時間の方が支配的になりあまり効率が良くありません。そこで実行間隔が短く且つ処理時間も短いバッチの場合はSidekiq Periodic Jobsで定期実行させることにしました。これは前提としてSidekiq Enterpriseを利用してる必要があります。
ref: https://github.com/sidekiq/sidekiq/wiki/Ent-Periodic-Jobs
バッチをSidekiq Workerで動くように実装したらSidekiqのinitializerでスケジュールを設定します。
Sidekiq.configure_server do |config| config.periodic do |mgr| mgr.register('0 * * * *', "HogeBatchWorker") ... end end
ECSのStopTimeout(30秒〜2分)を超えるようなバッチについてはタスクの切り替えでジョブが中断される恐れがあるためSidekiqは利用していません。
最後に
なんとか2024年内にバッチのECS移行が完了しました。EC2への依存がなくなったことで先述したデメリットも解消し個人的にもEventBridge Schedulerの「柔軟な時間枠」やSidekiqの定期実行など学びのある取り組となりました。