AWS Lambda / AWS Fargate / AWS App Runner における同時実行の考え方

# 概要

* 今では PC でもスマホでも、あるアプリケーションを実行した際に、マルチプロセスで動作するのが当たり前ですが、例えば MS-DOS などの古い OS では一度に1つのプロセスしか動作させられないシングルプロセスという時代がありました。
* そのため、ユーザは1つのことしか処理できないため「シングルタスク」などとも呼ばれていました。
* これが現在だと、OSで同時に複数処理が行えるマルチタスクとなり、1つのタスクを複数のプロセスで実現しているよう動作となります。
* 例えば、あるサービスを公開したときに、そのサービスを利用するユーザは複数人いると思います。
* そうすると下記のようにサービスが稼働するサーバは多数のユーザから同時に要求され一度に処理する必要がある場合が発生する可能性があります。[(画像引用元)](https://nathanpeck.com/concurrency-compared-lambda-fargate-app-runner/)
* AWS などのクラウドで提供されるコンピュートリソースでは、この同時実行を捌くための仕組みがあります。
* 今回は、そんな AWS における各種サービス(**Lambda**, **Fargate**, **App Runner**)についての同時実行の比較についてをまとめていこうと思います。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/725e7c4d-0ba9-24b1-2f50-f1606946d180.png)

## AWS Lambda

* Lambda関数における同時実行制御については公式ドキュメントに下記のように記されています。

> 関数が最初に呼び出されると、Lambda サービスは関数のインスタンスを作成し、ハンドラメソッドを実行してイベントを処理します。完了後、関数は後続のイベントを処理するために一定期間使用できます。関数がビジー状態のときに他のイベントが到着すると、Lambda は関数のインスタンスをさらに作成して、これらのリクエストを同時に処理します。

[引用元](https://docs.aws.amazon.com/lambda/latest/operatorguide/scaling-concurrency.html)

* ここで言っているハンドラメソッドというのは、下記のように実際にLambdaで実行する処理のことだと思っていただけるとよいでしょう。

```Python
exports.handler = (event, context, callback) => {
  callback(null, "Hello world!");
}
```

* つまり、Lambda 関数の各インスタンスが1度に1つのイベントを処理していることがわかります。
* イベントを処理している間は、関数はビジーと見なされるため、到着する同時イベントは関数の別のインスタンスで処理されます。
* 関数の新しいインスタンスを作成する必要がある度に、「**コールドスタート**」による遅延が発生します。

### Lambda 関数の Lifecycle

* Lambda 関数があるイベントにより呼び出された場合の一連の動作を下記に示します。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/3739f1a0-bead-54f9-e44f-95ff5de14ebe.png)

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/9fee7232-9756-0068-b328-2e40e41c0754.png)

* これにより、AWS Lambda の同時実行モデルは、多くのプロセスを並行して起動することで並行性が実現しています。
* 1つのプロセスは、一度に1つのイベント(例えばEventBridgeなどからのキック)またはリクエスト(API Gateweyなどから)に対してのみ動作します。
* プロセスレベルの同時実行をさせようと思った時の課題の1つに、プロセスを分離することがあります。
* これができないと、1つのプロセスが大量のリソースを消費し、同じマシンで実行されている他のプロセスのパフォーマンスに影響を与える可能性があります。

## AWS Fargate

* AWS Fargate は コンテナを実行するためのサーバレスなコンピューティングエンジンです。
* また、Fargate はそれ単体で利用することはできず、ECS ( Elasthic Container Service )という AWS 独自のコンテナオーケストレーションサービスで使用します。
* Fargate における同時実行については、ユーザの設定次第となります。

## ユーザの設定次第？

* ECS を使用し、Fargate を管理した際に、ユーザは、Fargate で実行するコンテナに割り当てるリソース量などを設定します。
* また、コンテナをスケーリングさせたいとなった場合は、ユーザで ECS が取得するメトリクス(例えば、CPU 使用率やメモリ使用率等)を使用し、スケーリングルールを作成することになります。
* また、同時リクエストやリクエストのレイテンシなど、LB からのメトリクスからスケーリング ルールを作成することもできます。
* これにより、アプリケーションのスケーリングと同時実行を制御します。
* なので、Faragte には組み込まれた同時実行制御のようなものはないことになります。

## なぜ組み込みの同時実行制御が無いのか

* なぜ組み込みの同時実行制御が無いのか。それは、コンテナの前段に配置する LB が多数のトラフィックを受けた時にリクエスト可能なコンテナに分散する仕組みがあるためです。
 * ALB を使用すると、2つのルーティングアルゴリズムを選択できます。
   * ラウンドロビン
   * 最小未処理リクエスト
 * ユーザはトラフィックパターンに応じて、これらのルーティングアルゴリズムを設定できるのが Fargate の良さにあると思います。

## AWS App Runner

* AWS App Runner とは、コンテナ化されたアプリ用のサーバレスコンピューティングエンジンです。
* え、AWS Fargate との違いは？となるかもしれませんが、APP Runner ですと ECS のようなオーケストレーションサービスを意識することなく、Fargate でユーザが設定していた内容をもっと楽にしたものだと思っていただければと思います。

※ なおより詳細は下記をご参照いただけますと幸いです。

https://qiita.com/yoshii0110/items/ec209712cafa7547c680

* App Runner では、リクエストを受け取ると自動でスケーリングします。
* App Runner を管理するユーザが意識することは、アプリケーションを動かしているコンテナの数と、各コンテナが受け取る同時トラフィックの量です。

* この時、スケーリングに使用される設定項目として同時実行数があります。
* コンテナが稼働する特定のインスタンスが受信する同時要求の数を指定します。
* 次に、App Runner は、そのターゲットの同時実行数を維持しながら、受信するリクエストをコンテナが稼働するインスタンスに自動的に分散します。[(画像引用元)](https://nathanpeck.com/concurrency-compared-lambda-fargate-app-runner/)

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/25d6d5ef-2869-c776-b4e7-808cb3ef49c1.png)

* 面白いのは、同時リクエストの数を App Runner が監視し、受け取ったリクエストはキューに入れられ、大量のリクエストを一旦吸収し、ある同時実行率でコンテナが稼働するインスタンスに分散します。
* これにより、コンテナが稼働するインスタンスが過負荷にならないように制御されています。
* ちなみに、キューがいっぱいになると、どうなるかというと、別のインスタンスに対してリクエストが再試行されます。
* まとめると AWS App Runner は、個々のコンテナが稼働するインスタンスが互いに分離されており、ユーザが指定した制限まで、多くの同時要求を処理することができるということです。

# 参考文献
https://nathanpeck.com/concurrency-compared-lambda-fargate-app-runner/