🏗️

「SQS*Step Functions*Fargate」でジョブの待ち時間を解消したら、コストも80%削減できた話

に公開

こんにちは、ハコベルでエンジニアリングマネージャー兼テックリードをやっている吉岡です。
この記事では、ハコベル配車計画 チームで実施した、インフラアーキテクチャ改善事例についてご紹介します。

これまでのアーキテクチャと課題

ハコベル配車計画が提供する機能の一つに、配送ルートの最適化計算があります。ユーザーは、この計算が想定している時間通りに終わることを前提に業務を組んでいるため、遅延すると配車業務全体が遅れてしまいます。

これまでも、ユーザー数の増加を見越してECSタスク数を調整するなど、運用でカバーすることで影響を防いできましたが、その対応頻度も高まっていました。旧アーキテクチャのままでは、いずれこの運用も限界を迎え、計算の待ち時間が顕在化し、ユーザーの業務フローに支障をきたすのは時間の問題でした。

旧アーキテクチャの構成

実際の構成を少しシンプルにしたものですが、旧アーキテクチャは次のような構成で稼働していました。

旧アーキテクチャ

  1. バックエンドアプリケーション(BE Application 1)がSQSキューにジョブをエンキューします。最適化計算ワーカーは複数バージョンが存在し、バージョンごとにキューが分かれています。
  2. ECSクラスター上では、複数バージョンの最適化計算ワーカー(Worker v1/v2)が常時起動しており、それぞれのキューをポーリングし、ジョブをデキューすると計算を開始します。この計算は長いものだと1時間近くかかります。
  3. 完了すると結果を別のバックエンドアプリケーション(BE Application 2)に送ります。
  4. バックエンドアプリケーションは結果をデータベースに保存して一連の処理が完了します。

この設計の主な問題点は、同時に処理できる数が常時起動しているECSタスク数に依存してしまう点でした。つまり、同時処理数を増やしたい場合、その数だけ常にECSタスクを起動しておく必要があり、コスト効率の面でも課題となっていました。

新しいアーキテクチャの構成

これらの課題を解決するため、私たちのチームは「最適化計算を制限なく並列実行できる仕組み」を考えました。これを実現するには、常時リソースを確保し続ける方式から、必要な時に必要なだけリソースを利用するイベント駆動型のアーキテクチャへ移行する必要がありました。

以上を踏まえ、改善したアーキテクチャがこちらです。

新アーキテクチャ

改善されたワークフローは、次のようになります。

  1. バックエンドアプリケーション(BE Application 1)が、一つに統合されたSQSキューにジョブをエンキューします。
  2. EventBridge PipesがSQSキューからメッセージをデキューします。
  3. EventBridge Pipesが後続のAWS Step Functions ステートマシンを起動します。
  4. ステートマシンは、受け取ったメタデータをもとに実行するタスクを動的に判断し、適切なバージョンの最適化計算ワーカーをFargateタスクとしてオンデマンドで実行します。
  5. Fargate上で実行されたワーカーは計算を行い、完了後に結果をバックエンドアプリケーション(BE Application 2)に送信します。
  6. バックエンドアプリケーションは受け取った結果をデータベース(DB)に保存して一連の処理が完了します。

この構成により、ジョブごとにFargateタスクが一つ起動する、というシンプルな仕組みが実現しました。これにより、同時実行数の上限を気にする必要がなくなり、需要に応じた柔軟なスケーリングが可能になりました。また、キューも一つに統一することができたので、仮に今後最適化計算ワーカーのバージョンが増えたとしても都度キューを増やす必要がなくなりました。

ステートマシンが担う、動的なタスクの振り分け

どのバージョンの最適化計算ワーカーを動かすか、という判断はStep Functionsのステートマシンが担当しています。

ステートマシン

  • ワークフローが始まると、まずChoiceステートがメッセージのメタデータに含まれるworkerVersionの値("1"か"2"か)を評価します。
  • その値に応じて、WorkerV1またはWorkerV2というように、実行するECSタスク定義を動的に切り替えています。
  • また、このステートマシンには、エラー発生時の再実行処理も組み込んでおり、ワークフロー全体の耐障害性を高めています。

アーキテクチャ刷新がもたらした、嬉しい副産物

スケーラビリティの確保が一番の目的でしたが、今回の刷新によって他にも嬉しい効果がありました。

  • インフラコストの大幅な削減: 常時起動のECSタスクを廃止し、オンデマンドでFargateタスクを実行する構成に変更したことで、最適化計算で使用するAWSサービスのコストを約80%削減することができました。また、旧アーキテクチャでワーカーが常時キューをポーリングしていたことで発生していたSQSへのAPIリクエスト(ReceiveMessage)コストも、EventBridge Pipesが効率的にメッセージを取得するようになったことで、合わせて削減できています。具体的なコストはお見せできませんが、新アーキテクチャ移行前、移行中、移行後のコスト遷移は以下のようになっています。
    コスト遷移
  • 柔軟なリソース割り当て: 最適化計算の特性やユーザーの要望に応じて、Fargateタスクのスペックを動的に変更することが可能になり、リソースの最適化が実現しました。
  • 安全なデプロイプロセスの確立: 旧アーキテクチャの常時起動ECSタスクは、デプロイ時にコンテナを安全に停止させるためのstopTimeout(最大120秒)の制約があり、長時間実行中のジョブが強制終了されるリスクを抱えていました。新アーキテクチャでは、オンデマンドで実行されるFargateタスクはデプロイの影響を受けないため、日中の時間帯でも安全にデプロイができるようになりました。

おまけ: AIとペアプロして開発を加速

少し余談ですが、新アーキテクチャの導入にあたり、PoCとしてCDKによるインフラ構築を行いました。その際、CDKのコードはAIエージェントと対話しながら作成を進めました。

これにより、インフラの専門知識がそこまで深くないアプリケーションエンジニアでも、AIに設計の相談をしながら、実際に動作する環境を迅速に構築できました。普段インフラ構築を担当する別チームとの連携においても、このCDKコードをベースに議論することで、コミュニケーションコストを大幅に削減し、開発をスピードアップさせることができました。

まとめ

今回は、長時間ジョブ処理のアーキテクチャを刷新した事例をご紹介しました。SQS、EventBridge Pipes、Step Functions、そしてAWS FargateといったAWSのマネージドサービスを組み合わせることで、スケーラビリティの課題を解決し、同時にコスト効率や運用面の改善も実現できました。

この記事が、同じような課題を持つ方の何かのヒントになれば幸いです。

Hacobell Developers Blog

Discussion