🐝

AWSでバッチ処理を実装する際の選択肢とサービス比較

2021/05/01に公開
2

概要

以前、SaaS系スタートアップのリアルなAWSアーキテクチャ設計 という記事で弊社のAWSアーキテクチャを紹介した。記事執筆時から時が経ち、バッチ処理を本格化する必要が生じたためAWSでバッチ処理を設計・実装しようとしたが、組み合わせが多くて辟易した。

自分たちのサービスにあった方法を見つけようとする中で色々と調べたので、どんな選択肢があるかや、重要と思われる項目で各サービスを比較してみた。特にサーバレス以外を選択するメリットはないと思うのでサーバレスなバッチ環境を前提とする。

結論

サービスの選択肢と比較

Lambda ECS(Fargate) AWS Batch(Fargate)
起動のオーバーヘッド ○〜△ ○〜△ ×
大量データ処理 ×(重要)
大量リクエスト処理 ○〜△
複数ジョブの制御 Step Functions利用 Step Functions利用 単体で可能
他サービスとの連携
環境構築の容易さ ×
ローカル開発環境との相性

若干 Lambda を贔屓したような比較項目になっている気もするが、実際 Lambda は使い勝手がいいので適切だと思う。重要なのは大量データ処理には向いていないということで、そのために ECS(Fargate) や AWS Batch(Fargate) があるといえる。

ユースケースに応じたサービス選択

ユースケース 選択すべきサービス
実行時間が15分を超えない + 少量リクエスト Lambda
実行時間が15分を超えない + 大量リクエスト SQS + Lambda
実行時間が15分を超える + 少量リクエスト ECS(Fargate)
実行時間が15分を超える + 大量リクエスト AWS Batch(Fargate)

処理が複雑でジョブの依存関係を定義したい場合は、AWS Batch 単体で制御するか、より複雑な場合は Step Functions を用いて Lambda、ECS(Fargate)、AWS Batch(Fargate) を組み合わせる。

AWSにおけるバッチ処理の選択肢

ざっくりとした選択肢は下記。

  • Lambda
  • ECS(Fargate)
  • AWS Batch(Fargate)

これらのサービスに実際は SQS や Step Functions を組み合わせることもあるので選択肢はさらに広がる。

ちなみに、SQS + Fargate(常時起動でポーリング) という構成や、SQS + Lambda + Fargate(都度実行) という構成は、AWS Batch が Fargate に対応した現在は特にメリットがないので取り扱わない。

2021/5/2 追記
「常時リクエストがくるユースケースでは、SQS + Fargate(常時起動でポーリング) が有効な手段ではないか」とコメントをいただいた。確かに絶え間なくリクエストがくることが想定できるケースではこの構成がいいと思う。ただし処理によってスペックを柔軟に変えづらい点と、コストがかかる点には注意だ。

また、各サービスの基本的な説明は分かりやすい記事がごろごろあるので割愛する。

各サービスの共通点

各サービスの比較を始める前に、どのサービスでも実現可能な2点をあげておく。

実行環境
今回あげたサービスは全てコンテナで動かすことができるため、実行環境は好きなように選ぶことができ特に差異はない。

スケジューリング実行
CloudWatch Events と連携することでどのサービスでも簡単に定期実行ができる。

比較軸

上記2点を除いた上で重要と考えられる比較軸を設定する。まず、バッチ処理と構成の観点で、下記を比較軸とする。

  • 起動のオーバーヘッド
  • 大量データ処理
  • 大量リクエスト処理
  • 複数ジョブの制御
  • 他サービスとの連携

また環境構築の観点で、下記も比較軸に加える。

  • 環境構築の容易さ
  • ローカル開発環境との相性

サービス比較

さっそくこれらの比較軸で各サービスについて説明していく。

起動のオーバーヘッド

特にバッチ処理でオーバーヘッドは気にならないかもしれないが、やや重めの処理をできるだけスピーディに処理したいというアプリケーション要件が実際にあったため取り上げる。

Lambda
コールドスタート問題がよく言われたのは昔の話で、オーバーヘッドは非常に小さい。この記事によると、コールドスタートでも大体2秒未満で実行されるようで、バッチ処理をやる上で問題になることはないだろう。もし問題だとしたらバッチ処理に切り出す設計がおそらく間違っている。

ただし、SQS + Lambda という組み合わせをとった場合はメッセージがキューに入ってから Lambda がトリガーされるため、直接invokeするよりは長いオーバーヘッドが発生するので注意が必要。

ECS(Fargate)
直接実行し、その都度コンテナを起動する場合は、Lambda より少し遅いという感覚。常時起動の Fargate を直接実行する場合はオーバーヘッドはない。が、この構成はお金がかかるため余程のことがない限り採用しない。

AWS Batch(Fargate)
AWS Batch は キューとコンピューティングリソースがセットになっているサービスのため、「キューのメッセージを取得し、Fargate を起動する」という動作があるためどうしてもオーバヘッドは大きくなる。EC2を使うより圧倒的に速くはあるものの、1分待つようなことはよくあるとAWSの方も言っていた。

大量データ処理

大量データ処理では、メモリや実行時間が争点となる。

Lambda
Lambda はメモリが10GBまで対応している(2020年12月に対応)ためほとんどのケースで問題はないと思うが、15分という実行時間がネックになる。15分を超えるようなバッチ処理の場合は、問答無用で Lambda は選択肢から除外される。

参考 New for AWS Lambda – Functions with Up to 10 GB of Memory and 6 vCPUs
参考 Lambda のクォータ

ECS(Fargate)
Fargate は 30GB までメモリを拡張でき、実行時間にも制限がない。

参考 指定された CPU またはメモリの値が無効

AWS Batch(Fargate)
こちらも実態は Fargate のため、メモリは 30GB、実行時間にも制限はない。

大量リクエスト処理

Lambda
直接実行の場合、呼び出し回数に制限はないものの、同時実行数に制限がある(デフォルトで1000、数十万まで引き上げが可能)ため、この制限に引っかかる可能性があるケースではリトライ処理を実装する必要がある。制限内であれば構成はシンプルになるが、Lambda の処理先に制限がある場合(例えばデータベースアクセス)、実行数を制御することができないので注意。

SQS + Lambda という構成をとれば、同時実行数を制限できるため大量のリクエストをいい感じに処理できる。例えば、Lambdaがデータベースアクセスを伴うケースでも、コネクションが枯渇しないように制御ができる。

参考 [レポート] SNSとSQSとLambdaによるスケーラブルでサーバーレスなイベント駆動アーキテクチャ #reinvent #svs303

ECS(Fargate)
直接実行の場合、Lambdaと同じく同時実行数に制限がある(デフォルトで1000)。さらに、直接実行する場合 RunTask API を用いるが、タスクの同時実行制限というものがあり、要はAPIの同時コール数に制限があるということで、こちらの記事で対策を施しているがかなり大変そう。なお制限の緩和は可能。

SQS + Lambda + Fargate という構成をとればいいと思うかもしれないが、前述した通りこの構成は AWS Batch の下位互換と言っても差し支えないので考慮しない。

参考 Amazon ECS のサービスクォータ
参考 AWS Fargate スロットリングの制限

AWS Batch(Fargate)
キューの機構が標準装備されているため、SQS + Lambda と同じくいい感じに大量リクエストを捌ける。一応SUBMITTED状態のジョブの最大数に100万という制限があるが大抵のケースでは引っかからないだろう。

参考 AWS Batch のサービスクォータ

複数ジョブの制御

「Step Functions + Lambda、Fargate、AWS Batchの組み合わせ」 vs. 「AWS Batch単体」 という構図になるが、AWSに問い合わせたところ、ジョブの複雑性に応じて使い分けるのがいいとのこと。

AWS Batch よりも Step Functions の方がより細かい要件に対応できるので、開発初期のシンプルな場合は AWS Batch を使い、段々複雑になってきたら Step Functions を導入するのがいいかもしれない。

他サービスとの連携

Lambda
SQS、S3、DynamoDB、API Gatewayなど多種多様なサービスと簡単に連携できるのが強み。特にバッチ処理は、キューのメッセージをトリガーとしたり、S3のPutイベントやデータベースの値更新などをトリガーとすることが多いため非常に扱いやすい。

ECS(Fargate)
特に連携できるものはないため、連携するなら前段に Lambda を配置することで連携の強みを借りることになる。API Gateway + Lambda を前段に置くと REST API として扱えるようになるメリットがある。

AWS Batch(Fargate)
同上。

環境構築の容易さ

構築の容易さはバッチ処理という性質上、一度きちんと設計して構築してしまえばそこまで問題にならないが、急ぎたい場合は割と重要な指標だと思っている。

Lambda
圧倒的に簡単。コンテナを準備し、メモリや実行時間を設定するだけで良い。また「他サービスとの連携」で記述したようにAWSによって統合されているサービスが多いため構築もそれに比例して容易になる。学習コストも低い。

ECS(Fargate)
クラスターやタスク定義などの設定が必要で、Lambda と比べて面倒。学習コストは Lambda ほどではないが高くはない。

AWS Batch(Fargate)
ECS をラップしているため、ECS + アルファ の設定が必要になる。また、優先キューの設定や優先順位に合わせたコンピューティング環境の構築、ジョブ定義の紐付けなどの設計は骨が折れるし、それに伴って学習コストもやや高くなる。

ローカル開発環境との相性

バッチ処理は一般的にS3にオブジェクトをアップロードしたり、アプリ本体のAPIをコールしたり、データベースの更新を行ったりする。AWSリソースをローカルで再現できない場合、自前でローカルに擬似環境を作る必要がある。

個人的にはここがかなり重要で、ベストプラクティスなどあったら教えていただきたい。

Lambda
LocalStack や AWS SAM、 Serverless Framework 等を用いることでローカルにクラウドと同じ環境を構築できる。

ローカルで再現ができると、例えばバッチ処理内でデータベースにアクセスする場合、同ネットワーク内であれば問題なくアクセス可能になる。

また、アプリからバッチを呼び出す際も、API Gateway + Lambda の構成をとっていれば、環境変数でエンドポイントをローカル用に変えるだけでいいので、ソースコードに分岐を挟む必要がない。

ECS(Fargate)
Lambda と違いローカルで再現する手段があまりない(LocalStackの有料プランだとあるみたいだが未検証。記事も少ないのではまること必至と思われる)。環境変数 local=true といった一手間は必須で、実際に構築したことはないが、ローカルではDockerコンテナを起動、クラウドではSDKなどを用いてECSタスクを実行するといった処理になると思う。

AWS Batch(Fargate)
ECS同様にローカルで再現する手段があまりなく、ECSと同じような解決策を取ることになる。キューの機構の再現もしようと思ったら大変だし、再現しない場合はローカルとクラウドでの動作の違いは避けられない。またバッチ処理のステータス管理ももちろんできない。

ユースケースとサービス選択

何を優先するかで変わってくるので、アプリケーション要件を満たすということを最優先、次に構築・運用コストという順で簡単な2つのユースケースを取り上げて考える。

ユースケース❶

時間はそこまでかからない小さめの処理で、できるだけ速く完了させたい

具体例
アップロードされた画像を加工し、加工した画像をできるだけはやくユーザーに見せる

選択すべきサービス
Lambda

解説
実行時間が15分以内に収まりそうなので、起動のオーバーヘッドが小さい Lambda が良い。Lambda を選択すると構築・運用コストも下がる。もし大量リクエストやバッチ処理にRDB系のデータベースへの大量アクセスが想定される場合は、オーバーヘッドのチューニングは捨てて、SQS + Lambda の構成を取るのが良い。

ユースケース❷

時間はかかってもいいから大量データを一括で処理したい

具体例
大量件数の画像の加工、フォーマット変換処理を行い、S3にアップロード後、データベースを更新する

選択すべきサービス
ECS または AWS Batch

解説
実行時間が15分を超えそうなため ECS か AWS Batch。特に大量リクエストが想定されない場合は、ECS を選択する方が構築・運用コストが下がるのでベター。大量リクエストが予想される場合(将来も含めて)は、AWS Batch を用いると良い。

処理が複雑な場合は、その複雑性に応じて AWS Batch 単体か、Step Functions でリソースを組み合わせて制御すべき(後者はより複雑な場合)。

最後に

こうしてまとめてみると、基本的には15分で終わるか終わらないかがサービス選びのポイントになることは間違いない。15分以内であれば Lambda を選択するのが大体正解で、大量リクエスト(内容次第ではある)なら SQS を前段に置く。

Lambda が使えない大規模な処理のときに、大量リクエスト(内容次第ではある)なら AWS Batch、少ないなら ECS といった具合だろうか。

バッチ処理周りは選択肢が多すぎる。そしてサービスも日々アップデートされており最新の情報で正しい選択をすることが非常に難しいと感じるので、その一助になれたら幸いだ。

よかったらTwitterフォローしてね。

Discussion

tmokmsstmokmss

大量リクエスト(絶え間なくリクエストくる程度)なら、SQS + Fargate(常時起動でポーリング)の案も効率的で良い気がしましたが、いかがでしょうか?
起動のオーバーヘッドが無いので

DaiDai

コメントいただきありがとうございます。
「絶え間なくリクエストがくる」という用件であればおっしゃる通りその構成が一番だと思います。
経験上そういった用件に出くわしたことがなかったので盲点でした。
参考になります!記事に追記しておきますね。