STORES Product Blog

こだわりを持ったお商売を支える「STORES」のテクノロジー部門のメンバーによるブログです。

STORES 予約 のEC2で動いていたバッチをECSへ移行した話し

STORES 予約 エンジニアの水野です。STORES 予約 ではこれまでにEC2で稼働していたシステムのECS移行を進めてきました。このECS移行については過去に前編と後編でブログにしておりますので興味のある方はぜひこちらもお読みいただけると幸いです。

product.st.inc

product.st.inc

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 ネットショップのバッチ基盤と同様です。

product.st.inc

バッチの実行中にのみECSタスクを起動し処理が終了すればコンテナ毎破棄しているので不要なコストを抑えることができます。

この方法でバッチの定期実行をするにあたって考慮したことが2つほどありました。

AWSリソースのキャパシティーエラーを可能な限り避ける

まれにAWSリソースの確保ができずECSタスクの実行が失敗する場合があります。

ref: https://repost.aws/knowledge-center/ecs-fargate-runtask-capacity

とくにキリの良い時間、例えば0分や30分など世のバッチが多く実行されることが予想される時間帯はリソース確保の失敗の確率が高くなります。過去にはこれが原因で失敗していた例が実際にありました。

タスクの実行に失敗したらStep Functionsでリトライさせればいいのですが可能な限り失敗自体を避けることが理想的です。そこで厳密な実行時間が不要なバッチについてはEventBridge Schedulerの「柔軟な時間枠」を利用し実行タイミングを散らすようにしました。

ref: https://docs.aws.amazon.com/ja_jp/scheduler/latest/UserGuide/managing-schedule-flexible-time-windows.html

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の定期実行など学びのある取り組となりました。