Closed7

AWS App Runner の検討

ytaka95ytaka95

背景

現在構築中のAPIが AWS Lambda では計算時間上限である15分以内に処理しきれないものになったので、インフラを再検討している。

  • リクエスト数は少ないが一発が短いものから長いものまである(15分以上がありえる)
  • アプリケーションはDockerで作っている(ので代替サービスはいろいろありそう)
  • かけられる工数が少ないのでなるべくお手軽に構築できるものが望ましい

Cloud Functions の延長の感覚で使える Cloud Run はAWSには無い強みだなあと思ったけど、これに相当するAWSのサービスってなんだろう…と社内で相談したら AWS App Runner では?と教えてもらった。

そういえば数年前に自分でも調べていた(すっかり忘れていた)。

ということで App Runner についていろいろ調べてみる。

ytaka95ytaka95

料金

App Runner CloudRun
非アクティブ時の待機メモリ料金 (/GB/hour) $0.009 $0.009
アクティブ時のCPU料金 (/vCPU/hour) $0.081 $0.0864

補足

  • App Runner はもしソースコードからデプロイするには固定で$1とビルドで$0.005/min.かかる(ECRからならかからない)
  • App Runner はus-east-1とかだと2割前後安くなる

参考

ytaka95ytaka95

スケーリング

素晴らしい検証記事があった。

https://typewriter.hatenablog.jp/entry/2021/05/21/231428

約二年前の記事なので実験結果が今もあてはまるかわからないけど、裏側がFargateというのは変わらないだろうから、やはりここまでの急激なリクエスト増加には対応できなさそう(実際のケースとしてあるかどうかはおいといて)。逆にこれをさばけるCloudRunがすごいな…。

具体的なスケーリングに関する設定

スケーリング設定として以下の項目がある:

  • Max concurrency: 1つのインスタンスが同時に処理できるリクエスト数
  • Max size: スケールできる最大インスタンス数
  • Min size: App Runner がプロビジョンする最小のインスタンス数

引用元: Managing App Runner automatic scaling | AWS

Min size で立ち上がっているインスタンスのうちリクエストを処理していないものは非アクティブという扱いになり、CPU料金はなくメモリ料金だけかかる。

Max Concurrencyのところに以下の説明がある。現状ではスケールアウトに関する公式の情報はこれのみである。

When the number of concurrent requests exceeds this quota, App Runner scales up the service.
同時要求の数がこのクォータを超えると、App Runner はサービスをスケールアップします。

さらにAWSに問い合わせたところ「Max Concurrencyを超えたインスタンスが1つでも発生した場合」にスケールアップする挙動が確認されたとのこと。ただしこれは公開された情報ではないため予告なく変更される可能性がある。厳密なスケールタイミングに依存した設計をするべきではないと言える。

ちなみに、下の「スパイク(バースト)への対処」にもあるとおり、各インスタンスには前段にキューが挟まれているので、max concurrencyを超えたことがイコールでToo Many Requestsが返るというわけではない。

ただ、上記の検証記事を読むと事前にスケールしてるようにも見える。もしかして、max concurrencyに到達した(数値がイコールになった)らスケールする?ということは、max concurrency が1だと常にスケールしてしまう?そんなことある?

ytaka95ytaka95

スパイク(バースト)への対処

以下のブログで内部処理について書かれている。AWS社員とのことなので信じたい。

https://nathanpeck.com/concurrency-compared-lambda-fargate-app-runner/#aws-app-runner-set-and-enforce-concurrency-limits-for-your-application-process

噛み砕くと以下のようになる。

まず構成としては「ロードバランサー → キュー → コンテナ」となっている。コンテナが受けられるリクエスト数が設定にある「max concurrency」であり、これを超えるものはキューで待機する。これによりスパイク(バースト)があってもエラーを返すことなく処理ができる。

もしキューが一杯だった場合、別のキューにリクエストが流される。

もしすべてのキューが一杯だった場合、 429 Too Many Requests が返される。これにより既存の処理中のリクエストやシステムが落ちることが防げる。

ここではスケールアウトのアルゴリズムについては書かれていない。

ytaka95ytaka95

まとめ

そもそもではあるが、App Runner で長時間の処理はあまり想定されていなさそうである。HTTPのレスポンスで処理結果を返さないといけないし。15分以上HTTP繋ぎっぱなしも良くない。

また、キューで一時的にリクエストを溜めることでバーストへ対処するという仕組みも、1つのリクエストの処理時間が短いことを想定している(例えば1秒の処理が2秒になっても文句は無いが、15分のリクエストが30分になったら文句が出ると思う)。

スケールアウトの戦略も具体的には公開されておらず、細かいリクエストがじわじわと増減するようなシステムで使うのがふさわしそうである。

ただ強いて言えば、最大リクエスト数を決めてしまってその範囲で使うという形が取れればありかもしれない。別でフロントエンドがあるシステムとか、またはApp Runner 上のAPIを社内の別システムから呼ぶようなパターンであれば、 429 Too Many Requests が出たら同時実行数を超えていることをユーザーに通知すればいい。

じゃあどうする?

1つの処理に何十分もかかるようなものは、AWSであれば AWS Batch (on Fargate)、Google Cloud であれば Cloud Run Jobs がよさそう(ここでは特に比較しない)。

Hidden comment
このスクラップは2024/04/23にクローズされました