
最近ゴルフを始めて打ちっぱなしに通うようになり、野球部だった中学生ぶりに手の皮が剥けました、Eight Engineering Unit Product Dev Groupの平石です。
今回はEightで行ったEC2インスタンス撲滅の取り組みについてお話しします。
なお本記事は Sansan Advent Calendar 2025 20日目の記事です。
背景
1年前に公開した「Eightの技術的挑戦」の記事で、中長期的にやりたいことの1つとしてEC2からの脱却を挙げていました。
buildersbox.corp-sansan.com
Eightは2012年のサービスローンチから13年以上の歴史があり、長らくAWSを利用してきました。Batchシステムや非同期Jobは、EC2インスタンス上にサーバーを立ち上げて運用していました。
しかし、この構成には以下のような課題がありました。
- OSやミドルウェアのメンテナンスコスト: EC2インスタンス内のOS・ミドルウェアのアップデートやパッチ適用を自前で管理する必要がある
- スケーラビリティの制約: Jobの増減に応じた柔軟なスケーリングが難しい
- セキュリティリスク: 昨今のランサムウェア被害の増加を踏まえ、サーバーを直接管理する構成からの脱却が求められていた
これらの問題を解消するため、Batchシステムをコンテナベースに移行するプロジェクトが動き始めました。
移行方法
EC2インスタンスからコンテナベースへ移行するにあたり、考慮すべきシステム構成が3つありました。
- Webサーバー等からキューに積まれ非同期で実行する「Jobシステム」
- 指定した時刻に実行される「スケジュールBatchシステム」
- アプリケーションコードが無限ループで実行される「常駐Batchシステム」
それぞれの移行方法についてご紹介していきます。
Jobシステム
移行前の構成と課題
長らくJobシステムには delayed_job_active_record を使っていました。DelayedJobはDBのレコードをキューとして利用する仕組みです。常駐Batchシステムがレコードを監視し、レコードが作成されたら対象の処理を実行してレコードを削除する、という流れで動作します。
この仕組みだとレコードがキューのメッセージ代わりになるため、トランザクション内で非同期処理を登録した場合、ロールバック時にはメッセージも一緒にロールバックされます。トランザクションの整合性をあまり意識せずに実装できる点は便利でした。一方でDelayedJobはDBをキューとして使う構造上、以下のような課題がありました。
- Jobの増加に伴うDBへの負荷
- 常駐BatchそのものがEC2インスタンスで動いていることによるスケーラビリティの制約
移行後の構成
これらの解消をするため、SQSとECSを組み合わせたActive Jobベースの構成に移行しました。
| 移行前 | 移行後 |
|---|---|
| EC2 + DelayedJob + DB(キュー) | ECS + Active Job + SQS |
新しい構成では、キューにSQSを利用し、ワーカーをECS上のコンテナとして稼働させています。これにより、Jobの増減に応じた柔軟なスケーリングが可能になり、DBへの負荷も軽減されました。
トランザクションとの整合性
DBトランザクション内でSQSメッセージを送信すると、コミット前にジョブが実行されてしまったり、ロールバック時にもジョブが実行されてしまう問題があります。そのため、ActiveRecord.after_all_transactions_commit を使い、トランザクションがコミットされた後にメッセージを送信するようにしました。これにより、ロールバック時にはメッセージが送信されず、安全に移行を進めることができました。
スケジュールBatchシステム
移行前の構成
ニュース配信やメール配信など、特定の曜日や時刻に実行されるシステムが数多く存在しています。これらは whenever を使いつつ、独自のBatchシステムで動かしていました。wheneverで該当時間になるとBatchタスクレコードを作成し、指定したマシンがタスクレコードを取得して実行するという仕組みです。DelayedJob同様、DBでプロセスを管理できるので運用の面では便利なことも多いのですが、その便利さ故なかなか移行に踏み出せずにいました。
移行後の構成
これらの仕組みをEventBridge Scheduler + ECS Run Task(ECS Scheduled Tasks)に移行しました。
| 移行前 | 移行後 |
|---|---|
| EC2 + whenever + Batch | ECS Run Task + EventBridge Scheduler |
スケジュール定義はアプリケーションコードではなく、Terraformで管理することにしました。
常駐Batchシステム
移行前の構成
退会処理や課金の更新処理などを常駐Batchで実行していました。対象データをRDSから無限ループでクエリし、データがあれば処理を実行する仕組みです。
例えば、ユーザーの退会処理は以下のようになります。ResignUser.ready_to_deleteに該当するレコードが作成されると、Batchがそれを検知して退会処理を実行します。
def execute loop do resigned_users = ResignUser.ready_to_delete # 対象となるユーザを取得 resigned_users.each do |resigned_user| # 各ユーザに対して退会処理を行う end end end
移行後の構成
このような常駐Batchは、2つのパターンに分けて移行しました。
状態変化をトリガーにできるパターン
退会処理のように、レコード作成が処理のトリガーになるケースです。ActiveRecordの after_save コールバックでレコード作成時にキューへ積み、Jobとして処理を実行する形に変更しました。
時刻ベースの処理パターン
課金の更新処理のように「更新日になったら実行する」という時刻ベースの処理はトリガーが存在せずJobへの移行が困難でした。このようなケースはScheduled Taskとして1時間ごとに実行する形式に変更しました。
処理量が多いと1時間以上かかることもあるため、Scheduled Taskの実行状態をDBで管理し、重複実行を防ぐ制御を加えています。
| 移行前 | 移行後 |
|---|---|
| EC2 + 無限ループBatch | SQS + ActiveJob または Scheduled Task |
移行作業の進め方
当時これら3つの仕組みで動いていたものは数多くあり、DelayedJobが30、常駐Batchタスクが20、スケジュールで動いているBatchタスクが150以上も存在していました。これらの移行はWebエンジニアはもちろん、SREメンバーも協力して行いました。
数多くの作業が定型化できたこともあり、Devin等のAIを活用しながら進めていきました。最終的に半年以上かかる作業になりましたが、無事完了することができました。
移行後の成果と課題
紹介した3つのシステム全てがコンテナベースに移行が完了したことにより、現在EightではEC2サーバ上で直接動くアプリケーションはなくなり、スケーラビリティやセキュリティ面の課題は解消されました。一方でいくつかの問題点が上がっています。
DLQ(Dead-Letter Queue)への対応
SQSではJobの実行中に例外などで異常終了した際、DLQにキューイングする運用にしています。DLQに入ったメッセージは削除するか、再度SQSキューに戻して再実行することができます。
しかし、1日1件以上の頻度でDLQへのキューイングが発生しており、その都度エラーの内容やJobの処理内容からメッセージを手動で処理しています。そのため、定期的に追加の運用作業が発生してしまっています。
現在は以下のような改善を進めています。
- 継続的にエラーになってもDLQに入らずリトライする仕組みの導入
- 冪等実行可能なJobに対してはDLQに入っても自動でキューに戻す仕組みの導入
Spotタスクに移行できないJob
ECSではSpotインスタンスを利用することでコストを大幅に削減できます。しかし、Spotインスタンスは需要状況によって中断される可能性があり、中断通知から終了までの猶予時間は2分程度しかありません。
もともとEC2インスタンス上で長時間稼働する前提で設計されていたため、1つのJobが数十分から1時間以上かかるものも少なくありませんでした。このようなJobはSpotインスタンスの中断に対応できず、On-Demandインスタンスで実行せざるを得ません。
現状では、短時間で完了するJobはSpot、長時間かかるJobはOn-Demandという形で使い分けていますが、コスト最適化の観点では課題が残っています。
Rails8.1で「Action Job Continuations」という機能が入っており、これを用いればJobプロセスの一時停止と再開が可能になります。Rails8.1にアップグレードしたタイミングで、全てのJobをSpotに移行できるかもしれません。
まとめ
EC2インスタンス撲滅とクラウドネイティブ化は、僕が入社した2021年からずっと掲げられていた悲願でした。4年越しに達成でき、Eightもまた一つモダンなアプリケーションになったと思います!
最後に、Eightで一緒に働いてくださるエンジニアを募集中です。専門領域を深めたい方はもちろん、アプリケーション、SRE、モバイルアプリの領域を超えてチャレンジしたい方、Ruby、Rails、AWSに興味を持っている方、ぜひ一緒に作っていきましょう!
カジュアル面談、お待ちしております!