こんにちは!
AWS 上でアプリケーション開発を行う際、Lambda 関数を用いる機会が多いのではないでしょうか。
Lambda をはじめ、AWS は多くのサーバーレスなサービスを提供していますが、誤った認識で構築を行うと、知らず知らずのうちにユーザビリティ低下・スロットリングなどの問題を引き起こしてしまうかもしれません。
本記事は、Lambda のオートスケーリング機能について、できれば避けたいスロットリングの観点から解説していきます。
本記事では、Lambda のオートスケーリングに絞ってご説明します。
Lambda のユースケースやコーディングの記法については解説致しません。
AWS Lambda はサーバーレスで処理を提供するサービスです。
バーストアクセスが発生し、Lambda 関数への処理リクエストが増えると、AWS 側は関数を処理するインスタンスを自動で増やし(オートスケーリング)、レスポンスを返します。
このように Lambda のインフラリソースは AWS 側で管理されるため、ユーザーはインフラリソースについて気に掛ける必要はありません。
しかし、サーバーレスだからといって「 Lambda はどんな量のリクエストにも対応できる」と考えて良いのでしょうか…?
結論から言うと、Lambda のオートスケーリングは有限です。関数へのリクエスト量が増えすぎると Lambda は処理が追いつかなくなり、スロットリングエラーを引き起こします。(スロットリングについては後述で解説します。)
そのため、Lambda 実装時は Lambda のオートスケーリングとスロットリングについて事前に理解しておくことがとても重要です。
まず、Lambda オートスケーリングを解説する上で欠かせない『スロットリング』についてご説明します。
スロットリングとは、「一定時間内に受信可能なリクエスト数を制限し、制限を上回るリクエストがなされた際には受信(実行)を拒否しエラーをレスポンスすること」です。
Lambdaでは、関数の起動制限を上回る処理がリクエストされた場合にスロットリングが発生し、HTTP ステータスコード 429 で応答されます。
他の AWS サービスでもスロットリングの閾値は設けられていますので、興味のある方はドキュメントを確認しておきましょう。
不自由な制限のように感じるかもしれませんが、従量課金制である AWS において、スロットリングは予期せぬバーストアクセスでサービス利用料が急増することを防止する大切な仕組みです!
それでは、Lambda でスロットリングはどのような場合に発生するのでしょうか?
まず、同時実行数とは、同一アカウント・同一リージョンで同時に実行できる Lambda 関数のことです。
例えば、処理に10秒かかる関数が、毎秒50件リクエストされ起動される場合は、同時実行数10 × 50 = 500となります。
この初期バースト時の同時実行数(バースト同時実行クォータ)には上限があり、以下の通りです。
例えば東京リージョンでバーストアクセスがあった場合、上限1000まで Lambda インスタンスを呼び出し、初期バーストでこれを超えるリクエストがあった時にスロットリングが発生します。
上記の初期バースト時、バースト同時実行クォータの値まで Lambda インスタンスは指数関数的に急速に呼び出されますが、それ以上のリクエストがあった場合、Lambda インスタンスは直線的にスケーリングしていくことになります。
具体的には、1分ごとに500ずつが同時実行数スケールアップの上限です。このスケーリング速度以上のリクエストがあると、その分スロットリングが発生してしまいます。
この時、同時実行数の上限までスケーリングされますが、東京リージョンにおいてデフォルトで上限は1000です。
1000以上の同時実行数が必要な場合は、AWS サポートに上限緩和申請をしましょう。
Lambda では「予約された同時実行数(Reserved Concurrency)」をオプションで設定できます。これは、リージョン内にある特定の Lambda 関数用に同時実行数を確保しておく機能です。
同一リージョン内の他の関数が、この予約数の残りの数を超えるインスタンスを呼び出そうとすると、スロットリングエラーが発生します。
例えば東京リージョンに Lambda 関数A, B, Cがあり、関数Aが500、関数Bが300を予約していた場合、関数Cが同時実行数200を超えるリクエストを受けるとスロットリングが発生します。(1000 – 500 – 300 = 200)
そのため、Reserved Concurrency を設定する場合は、同一リージョンの他の関数用に十分な同時実行数が残っているかを確認しておく必要があります。
スロットリングはクライアントにエラーコードを返し、ユーザー満足度の低下を引き起こすため、できるだけ回避しなくてはなりません。そのための方法についてご説明します。
スロットリング発生要因である同時実行数ですが、関数による一回あたりの処理時間が長いほど、同じリクエスト数でも同時実行数は増大します。
そのため一回あたりの処理時間がなるべく短くなるように、コードを工夫することが何より重要です。
同期処理の場合、エラー時のリトライ処理をクライアント側で組み込み、関数の呼び出し元でリトライ処理を実行させることでスロットリングを回避できる可能性があります。
非同期処理の場合は、1分から最大6時間の間隔を空けてリトライ処理を設定することができます。
先にご説明した『 Reserved Concurrency 』と似ているため一瞬混乱するかもしれませんが、日本語に直すと Provisioned Concurrency = 「プロビジョニング済み同時実行」です。
Provisioned Concurrency を対象関数に設定し、Lambda インスタンスを事前にプロビジョニングさせておくことにより、バーストアクセス発生時の影響を緩和することができます。
前項でご説明した通り、バースト同時実行クォータを超えるバーストアクセスが発生した時、従来であれば1分あたり500インスタンスずつしか同時実行数をスケールアップできず、それ以上のスピードで関数へのリクエスト増加するとスロットリングエラーとなります。
しかしながら Provisioned Concurrency を設定しておけば、このスケール速度制限に関係なく同時実行数を確保しておくことができ、バーストアクセスによるスロットリングを防止できます。
なお、Provisioned Concurrency は最大同時実行数を超えて設定できないため注意が必要です。
また、Provisioned Concurrency を設定する際は、運用時に DataDog や GA などのツールを使用してアクセスをモニタリングし、バーストアクセスを予測してどの程度インスタンスをプロビジョニングするべきかを検討することが重要です。
その上で Application AutoScaling を設定し、使用率や時間帯でプロビジョニング数をオートスケールさせることで利用料金の節約もできます。
重たい処理を Lambda にさせている場合は、同時実行数が増大しがちです。代わりに Fagate などのコンテナサービスを利用することも検討しましょう。
ただしこの場合、実際に処理をしていなくても月額費用が発生するので注意してください。
上記の手段だけでは、どうしてもスロットリングを回避できなそうであれば、AWS サポートに依頼して同時実行数の上限を増やしてもらうことを検討しましょう。
サーバーレスといえど Lambda の起動リソースは有限で、特にバーストアクセスへの配慮は重要です。
また、Lambda に限らず従量課金制である AWS サービスを利用する上では、スロットリングと上手に付き合っていきましょう!
サーバーレスでの運用、開発相談はお気軽にお問い合わせください。
スモールスタート開発支援、サーバーレス・NoSQLのことなら
ラーゲイトまでご相談ください
低コスト、サーバーレスの
モダナイズ開発をご検討なら
下請け対応可能
Sler企業様からの依頼も歓迎