Lambdalithアプローチとは
AWS Lambdaをモノリシックに使うLambdalithというアプローチはご存知でしょうか。端的に言うと単一のLambdaファンクションでRest APIのすべてのルーティングとその処理を実装しようというアプローチです。AWSの構成図で言うと以下のようになります。
Operating Lambda: イベント駆動型アーキテクチャの設計原則 – Part 2のブログにはAWS Lambdaの設計原則として以下のように書いてあります。
ほとんどのアーキテクチャは、より大きな少数の関数よりも、より短い多数の関数にすることが推奨されます。Lambda 関数をワークロードに合わせて独自にチューニングすると簡潔で、一般的には実行時間が短くなります。各関数の目的はワークフロー全体またはトランザクションのボリュームに関する知識を持たず、関数に渡されるイベントを処理することです。これにより関数が他のサービスとの結合を最小限に抑え、イベントのソースについて関知しないようにできます。
つまり、できる限りLambdaファンクションは単一の目的で細かく分割することで、実行時間を短く出来ます。更に全体的にも疎結合なアーキテクチャになるため、スケーラブルで安定しますよという意味です。AWS Lambdaをモノリシックに使うということはそもそもAWSが推奨している設計原則から外れているように見えます。
The Lambda monolithのブログにもモノリスはアンチパターンであり、APIのルーティング毎にLambdaファンクションを分割するマイクロサービスを採用するべきだと記載があります。しかしながら、実際のAPI開発を進める上で、Lambdaファンクションをモノリシックに実装することでデメリットは感じたことが無いですし、むしろメリットしか感じないというのが正直なところです。本ブログではLambdalithなアプローチのメリットを紹介したいと思います。
Lambdalithアプローチのメリット
ランタイムアップデートのストレスが軽減
AWS Lambdaで一番めんどくさい事と言えばランタイムのアップデートではないでしょうか。必要だからやらないといけないけどそれなりに労力がかかるのでやりたくないというやつですね。中には数十、数百のLambdaファンクションがテストコード無しで存在しており、どこから手をつけて良いのかわからないというプロジェクトもあるのではないでしょうか。
LambdaファンクションがAPIエンドポイントに対して一つであれば、全体のアーキテクチャにあるLambdaファンクションの総数はかなり減るはずです。結果としてランタイムアップデートにかかるストレスは大きく軽減します。
認知負荷が軽減する
認知負荷とは、全体を理解するためにかかる負荷の度合と言えるでしょう。AWSの構成図を見て一目で全体を理解できるアーキテクチャは認知負荷の低いアーキテクチャと言えます。例えば、Amazon EC2 + Amazon RDSの構成は一目見れば全体がつかめるので認知負荷は低いです。逆にLambdaファンクションが50個にEventBridgeと、SQSとSNSにDynamoDB...みたいなプロジェクトにいきなりアサインされたら全体のアーキテクチャや各サービスの用途などを理解するのに時間が掛かるでしょう。この状態だと認知負荷が高くなります。
認知負荷の高さはサーバレスアーキテクチャのデメリットの一つでしょう。更に大量のLambdaファンクションがある場合にはログを追うのもなかなか大変になります。読者の中にはログの通知からLambdaファンクションを逆引きして、その役割を思い出すといった経験がある方もいるのではないでしょうか。
そんな中でLambdalithな構成にすることで構成・仕様・IAMポリシーは単純になり、認知負荷は大きく軽減します。更にLambdaファンクションが一つになることでログの監視とトレースも簡単になります。これは運用の効率化という意味では大きな意味があります。
リソース数の量が減る
CloudFormation1スタックのリソースの上限は500個までですが、以前は200個まででした。APIのルートを一つ作るとAmazon API Gateway + AWS Lambda + Amazon CloudWatch Logsの3リソースを消費します。APIのパスとメソッド毎にLambdaファンクションを作っていた場合には1スタックあたり60 - 70個くらいまでしか、APIのパスとメソッドを作れないため、割とすぐに上限に達していました。
Lambdalithな構成にすることでCloudFormationのリソースの上限を気にする必要がほとんどなくなります。ここもある種認知負荷の話に繋がりますが、メンテナンス上のメリットがあります。
既存のWebフレームワークが使える
Amazon EC2やAmazon ECSでWebアプリケーションを開発する場合には大概何かしらのWebフレームワークを使うと思います。Lambdalithな構成にすることでそういったWebフレームワークがAWS Lambda上でも使えます。普段、AWS Lambda独自の開発手法に慣れていない開発者にとって敷居を広げる方法であり、開発の選択肢が大きく増えることになります。
更にAmazon EC2やAmazon ECSベースのアプリケーションをそのままAWS Lambdaに移行することが出来るのでそのポータビリティも上がります。既存のWebフレームワークを使うためにはaws-lambda-web-adapterというAWSが公開しているOSSを使う必要があります。Dockerを使わない、Remix / Next.js 14 など最新ウェブフレームワークのAWS完全サーバーレス構成と環境構築方法という記事の中で詳細を説明してますので興味があれば読んでみてください。
AWS Lambda Function URLsが使える
AWS Lambda Function URLsとAmazon API Gatewayの違いの記事の中で紹介していますが、AWS Lambda Function URLsはLambdaファンクションにHTTPリクエスト用の受け口を追加する機能です。Amazon API Gatewayに対して機能が少ない代わりにコストは安くなります。Lambdalithの場合だとすべてのリクエストを単一のLambdaファンクションで受け取って、Lambda内に実装されたルーティングの仕組みを使うことで処理を捌きます。つまり、自ずとAWS Lambda Functions URLsを使用するほうがアーキテクチャ的にもフィットし、コストもレイテンシもAmazon API Gatewayに比べて優れたAPIを提供することが可能になります。
コールドスタートの頻度が低くなる
当然ながら一つのLambdaファンクションに集約した構成となるため、リクエストのヒットもそのLambdaファンクションに集約されるためコールドスタートの頻度も低くなります。
Lambdalithアプローチのデメリット
以下のようなデメリットは確かにありますが、あまり実開発及び運用の中で目立ったペインポイントにはならないでしょう。
- ポリシーとか権限周りが大雑把になりやすい
- 複雑化しやすいため熟練した内部設計が必要
- 非同期バックエンドLambdaのリトライなど、処理内容によってはハンドリングしにくくなるタイプのワークロードもある
- パッケージ肥大化で250MBの上限打ちやすくなる
まとめ
本記事ではLambdalithなアプローチのメリットを紹介しました。率直なところメリットしか感じなアーキテクチャかなと考えています。開発も運用もやりやすく、コスト自体もAWS Lambda Function URLsを使うことで安くなるからです。是非、皆さん次回APIの開発が必要になったらこのアプローチを検討してみてください。