1年付き合って見て思ったこと。
Lambdaは毎年たくさん機能アップデートがあるけど、やっぱりLambdaの醍醐味はそのシンプルさだと思います。シンプルに使えるように、Lambdaだけでシステムやロジック設計をするのではなくて、AWSのビルディングブロックの組み合わせで適材適所で活かしあった方が良いなと思う1年でした。
Provisioned Concurrencyによって、より多くのワークロードでLambdaが利用された1年になりましたが、それ以上にProvisioned Concurrencyを利用しなくても、そのまま素で自動的にスケールしてくれるLambdaの良さを再確認した1年でもありました。
この記事について
執筆時はre:Invent 2020の前日です。
ですので、re:Invent2020の内容は一切含みません。
そしてProvisioned Concurrencyは2019年のre:Invent時期に登場したAWS Lambdaの機能です。
今回は、そろそろこなれ感のあるこの機能について解説します。
このQiitaはServerless Meetup Japan Virtual #1で発表した内容に基づいて紹介します。
https://serverless.connpass.com/event/180731/
今回は紙面の都合上、特にユースケース部分にのみ焦点を当てて説明しますが、Provisioned ConcurrencyのBest Practiceについても記事が欲しいという場合は twitterなどでリクエストください。
https://twitter.com/_kensh
Lambdaの復習
Provisioned Concurrencyの機能を理解するには、Lambdaが解決したかった課題を理解する必要があります。
そのために、簡単にLambda自体の復習も兼ねて説明します。
まず、Lambdaサービスにリクエストが3つ同時にくると、Lambdaのインスタンスが3つ立ち上がります。これはDocker Containerなどで常駐プロセスによるマルチスレッドで受け付けるようなアプリケーションサーバーをご利用の方は???となるかもしれません。
実はLambdaは1リクエストに対して1Lambdaインスタンスが割り振られます。そしてそのインスタンスが処理中は次のリクエストを受け付けることはありません。
リクエストを受け付けるのは(絵的には黄色のハンドラー)処理が終わった後のアイドル状態になってからになります。
そして各Lambdaインスタンスごとに最初のリクエスト受付では白色の初期化処理部分が入り込んでいます。この初期化処理を含む呼び出しのことをColdStartと呼びます。2回目以降の呼び出しでは、インスタンスはすでに暖まっているので初期化処理は不要になります。これをWarmStartとよびます。
Provisioned Concurrencyはなぜ生まれたか?
上記のようなLambdaのColdStartの課題を解決するために生まれました。簡単にいうとLambdaのインスタンスを事前暖機しておく機能と言えます。
この絵のように、Provisioned Concurrencyを利用して事前暖機しておくと、最初の呼び出し時点ですでに暖まった状態でリクエストを受け付けれるため、WarmStartします。
Provisioned Concurrency のユースケース
Provisioned Concurrency のユースケースでよく挙げられるのが以下です。
- 予測できるスパイクへの対処
- ColdStartの抑制
- AWS Lambdaのバースト制限の抑制
一つ一つ見ていきましょう。
予測できるスパイクへの対処
以下の図は横軸が時間、縦軸が同時実行数になっています。そして黄色の枠がProvisioned Concurrencyを適用下部分になります。(Provisioned Concurrencyからはみ出た部分はSpillOverと表現しています。)
まず、予測できるスパイクへの対処ですが、これはECサイトのキャンペーンを時間指定で行ったり、コンサートの受付を時間指定で実施したりなど、あらかじめサービス運営者側が時間を決めてサイトを公開したり流入をコントロールできるパターンです。
このとき、Amazon EventBridgeなどのサービスを利用して、あらかじめスケジュールを組んでおくことができます。
この図の例だと、11:45からリクエストの波がくると予測できているので、そのタイミングでLambda関数の設定変更によるProvisioned Concurrencyのスケールアウトを、スケジュールで仕掛けていた別のLambda関数などで行い、また12:15には波が収まったのを契機にスケールインさせています。
このように一時期にアクセスが集中する場合でも予測可能なスパイクは、対応が可能ケースが多くなりました。
ColdStartの抑制
次のケースがColdStartの抑制です。
アクセス頻度は高くなくてもProvisioned Concurrencyを利用したいユースケースもあります。たとえば認証認可を伴うサービスでサインアップ機能を実装している場合、サインアップのユーザー体験をよくするためにサインアップ前LambdaトリガーをColdStartさせずに低レイテンシーでレスポンスを返したいケースです。
UXに直接つながる機能にProvisioned Concurrencyを用いることで、体験をよくしていくユースケースとなります。
もうひとつのColdStart抑制ケースですが、ある程度リクエストがあるシステムで、かつ、リクエストの波があるようなシステムです。
特に、JVMのような起動に多少時間を要するランタイムを利用している場合によく使われるパターンになります。
リクエストに波がある場合、波のリクエスト増加フェーズでLambdaインスタンスが新規に立ち上がることがあり、これがColdStartを生じます。
もちろん、ColdStartを抑制するだけであれば、べったりとProvisioned Concurrencyで上図のように覆ってしまえばよいのですが、余分なProvisioned Concurrencyの確保はコスト増大に繋がります。
そこで、Application Auto Scaling を使って適切にコントロールしてあげます。
このケースですと、ProvisionedConcurrencyUtilization = 0.7(=70%)を超えるとスケールアウトするようにApplication Auto Scalingを設定しています。これを下回るとスケールインするようにしておくことで、リクエストの波に沿ったProvisioned Concurrencyの利用が可能になります。
この設定はCLIでもSAMでもCDKでもできます。
AWS Lambdaのバースト制限緩和
またちょっと復習をしておきます。
Lambdaには二つの異なるスケールに関する制限があります。
- 同時実行数の制限 (アカウントリージョン単位で1000(soft limit))
- バーストリミット(リージョンごとに異なりますがTokyoリージョンでは1000)
こちらの図でわかるとおり、Lambdaは1000までバーストでスケールします(呼び出されればコールドスタートはするものの即時にスケールを開始します)。
そして同時実行数の制限緩和をTokyoリージョンで仮に3000まで緩和していたとしても、バースト制限は1000なので、そこまでしかバーストしません。1000を超えた分は1分間に最大500インスタンスづつスケールします。
実はこのバースト制限の緩和にProvisioned Concurrencyが利用できます。
Provisioned Concurrencyを利用することで、バースト制限を超えてProvisioned Concurrencyの設定値までバーストスケールします。
そしてもちろん、ColdStartの抑制もできているので、Provisioned Concurrencyに収まる範囲ではColdStartが緩和できています。
結果として、バースト制限が緩和され、コールドスタートが緩和され、システムのレイテンシーが改善されます。
まとめ
今回は、もう1年前の機能になってしまいましたが、Provisioned ConcurrencyをLambdaの復習も兼ねてお届けしました
大切なことは、Provisioned Concurrencyに頼らなくても良いように、非同期システムにすることや、静的WEB生成により、大量リクエストに対しての耐久性のあるシステムアーキテクチャを設計するなどの工夫をすることです。
その上で部分的に暖めておく必要があるエンドポイントがあるなどの理由がある場合、Provisioned Concurrencyを利用するようにしましょう!