👏

WebAPIを構築する際にAPI Gateway+Lambdaを選択するべきか?

2022/07/30に公開

はじめに

https://twitter.com/r_karotou/status/1552641055933095939?s=20&t=iNYm1WuqC4zZsjQEiypj3w

このツイートに結構反響があったので、雑になるがとにかく自分の考えをダンプする。もともと書いていた記事はうっかりやらかしてデータロストした、泣きたい。
話をわかりやすくするために、ALB+ECS(Fargate)を使ってWebAPIと対比して説明しているが現実はもっと複雑である。

引用リツイートをもらえた部分などについてもアンサーっぽいことも書いていく。

AWS利用費と人件費の話

AWS上にWebAPIを構築する際に、AWS利用費の削減をモチベーションとしてApiGW+Lambda構成が、採用されることがある。確かにAWS利用費は下がるがApiGW+Lambda構成を設計〜運用するためにはAWSに関する知識の中でもとくに専門的な知識が必要になる。こういった人材を雇用または外部へ発注し続けることは人件費に跳ね返ってくる。
ApiGW+LambdaがWebAPIのための構成として唯一無二の選択ならともかく、同期APIとして構築するのであればALB+ECSで代替可能である、というかそちらのほうがメジャーな構成である。

そもそもAWS利用費に影響が大きいのはデータベース

ApiGW+Lambdaの場合はデータベースにDynamoDB、ALB+ECSの場合はデータベースにRDS/Auroraが、採用されることが多いからかこれらは何が支配的であるかが抜けた状態で伝播されている様子を見かける。

AWS利用費はApiGW+Lambda < ALB+ECSであるが、それ以上にDynamoDB << RDS/Auroraである。もちろん、常にDynamoDB << RDS/Auroraな訳ではなく、極端にI/Oが多いなどこの関係が逆転することもあるがベースとしてはこの感覚で良い。

なので、AWS利用費の削減をモチベーションにApiGW+Lambda+DynamoDBの採用を検討する場合は、ソフトウェアがLambda上で実行できるか(コールドスタートなどが実用に耐えうるか)に加えてデータベースにDynamoDBを採用できるかを検討する必要がある。そして後者の方が開発者により専門的な知識を求める事になり採用のネックになりがちである。

ApiGW+Lambda+DynamoDB構成の場合に必要になる専門的な知識

  • HTTPエンドポイント(メソッド・パス)ごとにLambda関数を分割するべきか
    • 分割する場合はそれぞれのデプロイパッケージを共有/個別/バンドルやコンパイルするべきか
  • ApiGWはREST API or HTTP APIどちらを採用するべきか
    • REST APIの場合はLambdaプロキシ統合するべきか?
  • DynamoDBテーブル設計はシングルテーブル戦略を採用するべきか
  • DynamoDBのキャパシティ設定はどうするべきか
  • SAM/Serverless Framework/CDKどれを採用するべきか
  • ローカル環境でのテストはどうするべきか

一部であるが、上記のような知識が必要になる。もちろんALB+ECSの場合も専門的な知識は必要になり、この知識量についてはALB+ECSの方がやや少ないとは考えるが大きな差でない。
知識の量ではなく汎用性による差が大きいと考えていて、ALB+ECSの知識を必修科目とするならば、ApiGW+LambdaそしてDynamoDBの設計に関する知識は選択科目であると考える。ただし、今回の話はあくまでもApiGW+Lambdaの話で、AWSを扱う上でLambdaは使えた方が圧倒的に選択肢が増えるのでLambdaはぜひ習得して欲しい。
ApiGW+Lambdaな場合のベストプラクティスがInfrastructure as Codeツールやサービス自体のアップデートによる機能追加によって、日々更新されているため体系立てて書籍で学ぶことが難しい点も大きい。ALB+ECSも、もちろん日々更新されているのだが、ApiGW+Lambdaと比べると良い意味で枯れており、こちらの方が学びやすくそしてそれで大抵はWebAPIを作るには十分である。

ApiGW+Lambdaはアンチパターンなのか

そんなことはない、私はApiGW、LambdaそしてDynamoDBは大好きだし、優れたアーキテクチャパターンであると信じている。ただし現状ではピーキー過ぎてWebAPIの用途としては大衆化できていないと考えている。
ベンチャー・メガベンチャーのように、AWSに強いエンジニアを採用・育成し継続的に雇用できる環境であれば、ALB+ECSの場合と比較して必要に応じて採用して良いが、AWSをあまり採用していない段階やフル外注の場合などは慎重に検討して欲しい。

つまりアーキテクチャとして優れている = あなたの組織が採用するべき、ではないということである。組織のAWSに対する習熟度や支払えるコストに応じてアーキテクチャを採用して欲しい。類似で勉強会や事例では映えることに重点が置かれてピーキーなアーキテクチャが話され/運営によって採択されがちである、自分たちが採用できるかは別問題なので鵜呑みにしないように気をつけたい。かといって現状で困っていないからと枯れきった技術スタックだけで放置していると、それはそれで別の問題が出てくるので難しい。

ApiGW+Lambdaを採用すればネットワークから開放されるか?

ApiGW+Lambda構成はVPCを必要としないので、ネットワークに関する知識が不要となり、必要なAWSリソースが少なくなる。だが、①RDS/Auroraを採用する ②外部サービスとの通信に固定IPアドレスが必要でNAT Gateway経由の通信が必要 ③オンプレミスのサーバーと専用線経由で通信が必要、などの場合はVPCを作りLambdaをVPC内で動かす必要がある。

その場合でも、こまめに実行環境が破棄されることやスケーラビリティなどのLambdaの恩恵を受けることはできる。しかし、VPCを作るという前提の上で、採用できる言語・フレームワークなどの制約を受けてもECSではなくてLambdaを採用するかは慎重に判断する必要がある。またスケーラビリティに関しては他の部分がボトルネックになるということはよくある。

そもそもインフラ費削減を最優先にAWSを利用するべきなのか?

オンプレミスからの移行などのシチュエーションにおいて、初っ端から最優先事項にインフラ費の削減を持ってくると変なことになりがちなのを多数見てきた。AWSを使うことでソフトウェアの素早い開発・デプロイを実現し、それによっと高速でユーザーからのフィードバックをもとに改善というループを回して価値を最大化(売上向上による利益向上)を優先事項にしたほうが良いと思う。インフラ費(AWS利用費)の削減はその後漸進的に進めていけば良いと思う。
(妄想だが、インフラ費の削減は経営層への説明として使いやすく、これと矛盾するとまずいので事例としてインフラ費削減を説明しているものは多いが、真の採用理由は違うというのは結構あると思う)

。。。と偉そうに書いたが、トータルコスト的レイヤーはにわかなので、移行を検討する場合はAWSJやそのパートナーに相談してぜひ深堀りしよう。
また、適切なアーキテクチャは技術以外を含むさまざまな外的要因によっても変化するので、すでにAWSを利用している場合も自信がなければどういったアーキテクチャを採用するべきか相談しよう。

でもServerless Frameworkとかを使って簡単にWebAPIを作れるの便利じゃない?

わかる、便利だよね。

簡単に作れることが重要なら、それで何ら問題ない。規模が小さいなら、開発した人がいなくなった場合でも調べながら運用ができるし、別のアーキテクチャへリプレイスすることもできる。ただし、エンドポイントが増えて規模が大きくなる場合はこのブログをぜひ思い出して欲しい。
AWSを使うメリットは従量課金とサービスがすぐに利用できることによって、アーキテクチャを漸進的に作り変えていけることだと思っているので定期的にアーキテクチャを見直して欲しい。

ただ、ある程度の規模のプロジェクトかつAWSの経験値が浅い場合は、まずALB+ECSから始めた方が失敗しにくいのでこちらをオススメしたい。失敗は悪ではないが、ステップは踏んだほうが良いよというのが私の考え。

テストの話

同期的なWebAPIだけの話でいうと、DynamoDBをDynamoDB LocalやLocalstackをDockerと組み合わせて実行できるので、下記のブログで言うところのSmallおよびMediumテスト(自分はこれらをユニットテスト、インテグレーションテストと呼ぶことが多い)は問題なく行えている。とはいえ、これらはエミュレーションなのでうまく行かないこともあり、その部分のテストは諦めている。また、依存関係の方向制御や差し替えなどテストしやすいコードを書けることが前提となる。

Google Testing Blog: Test Sizes

なので、Largeテスト(E2Eテストと呼ぶことが多い)としてAWSアカウントへ実際にデプロイして挙動をテストすることでフォローしている。

しかし、Lambdaを使っているとその実行時間の制約から、SQSやStep Functionsを使って処理を非同期に切り離したり、DynamoDBの検索能力に要件に対して不足がありS3へダンプしてからAthenaでクエリーなど非同期処理・複数のAWSサービスの利用が必要になる場合がある。こうなってくるとSmallおよびMediumテストは難しくなる。(イベント・ドリブンなアーキテクチャであるならばイベントのIN/OUTを与えればロジックをテストできるはずという理屈はわかっているが、自分はまだこれを複数のAWSサービスが絡む場合の実際の手法に落とし込めていない)

なので、Largeテストで対処するしかない。テストできるならそれでOKなんじゃ?と机上では思いがちだが、デプロイしないとテストできないというのは開発者に与える負担が大きい、コードを書いてからテスト実行まで数分かかると集中力を維持しながら開発するのが難しい。

まとめると、ApiGW+Lambdaでもテストしやすいコードが書ければ、大部分は特別な影響なくテストができる。ただし、非同期処理などのためにAWSのサービスを組み合わせて機能を実現する場合はローカルでのテストが難しい。Largeテストでカバーできるが、開発効率が落ちる。(現状の著者の知識・経験をベースとした話、もっと経験を積めば色々とやり方は見えてくる気がする)

将来的な話

この話はあくまでも現時点の話で、エコシステムの発展やサービスのアップデートによって当然覆る。
過去を振り返ると、ここ数年でのECSのエコシステムの発展やSaving PlansやARMプロセッサ対応によるコスト削減などは目覚ましい、App RunnerというWebAPIに特化する代わりにより簡単に運用できるコンテナ実行サービスも登場した。一方、ApiGW+LambdaもAWS CDKのaws-lambda-nodejsやGoを使ったバンドル・コンパイルによってデプロイパッケージ戦略を失敗する可能性が減るなど改善が見られる。
また、AWSに限らずいえばGoogle CloudのCloud RunやCloudflareのWorkerとD1、R2など新しいものはどんどんと登場している。

あとがき的な

  • ApiGW+LambdaのAWS利用費が安いという話には大きなコンテキストが隠れている
  • 良いアーキテクチャだからといって、あなたにとって最適なアーキテクチャとは限らない
  • ソフトウェアはもちろん、アーキテクチャも更新し続ける必要がある

ざっくりまとめると言いたいのはここらへんの話でした。私はApiGW+Lambda+DynamoDBを扱った経験が多く、「AWS CDKでApiGW+Lambda+DynamoDB使った構成楽ちん!べんり!」って気持ちなのですが、使ってきてわかった現実と、世の中の期待値にギャップを感じてブログを書いてみました。

Discussion