とにかくシンプルに始めるサーバーレス、初心者が最初はやるべきではない3つのこと

初心者が最初に学ぶときや企業が最初にサーバーレス開発をする際にやらなくても良いこと、ここは妥協しないほうが良いことなどを極めて個人的な見解に基づいてお伝えします。
2020.12.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

おはようございます、加藤です。「サーバーレス開発を初めたい!!」ってなったときに多くの人は登壇動画やスライド、ブログ、書籍などから情報をキャッチアップされると思います。昨今はCOVID-19の影響でリモートでの勉強会も大幅に増えて後から登壇スライドだけでなく動画も見ることができ、非常に情報に恵まれています。しかし、サーバーレスに限ったことではありませんが、登壇やブログはどうしても慣れた人が尖った内容になることが多く初心者にとっては「最初はどこまでやれば良いのか?」が難しいかなと感じました。

なので、このブログでは初心者が最初に学ぶときや企業が弊社のようなAPNパートナーの様なプロフェッショナルの力を使わずに最初にサーバーレス開発をする際にやらなくても良いこと、ここは妥協しないほうが良いことなどを極めて個人的な見解に基づいてお伝えします。まずはとにかくシンプルにサーバーレスデビューしましょう!

前提

サーバーレス開発について調べたり勉強はしたが本格的に作ったことはないという方を対象としています。

本記事で言うサーバーレス、サーバーレスアーキテクチャとはAPI Gateway + Lambdaを主に使って構築するWebAPIを指します。私はS3へのPutをトリガーとしてLambdaが動いて…という仕組みもサーバーレスだと認識していますが、本記事はWebAPIをテーマにしているため、前述のとおりです。

本記事では「○○しない」という言葉を使いながら話を進めて行きますが、これは技術・方式を否定しているのではなく、あくまでも初めて開発する方にとっては無理に取り組まない方が良いという意図です。また初めて開発する方でも「〇〇したほうが良い」場合もなるべく記載しています。

イベント・ドリブンしない

サーバーレス = イベント・ドリブンぐらいに世の中では言われていますが、WebAPIを作るケースだと基本的にリクエスト・リプライ方式で構築します。

一度、イベント・ドリブン方式とリクエスト・リプライ方式とは何かを整理しましょう。

イベント・ドリブン方式とは発行元がイベントバスにイベントを発行し、任意数の購読先がイベントを取得して処理を開始するというシステム間の連携方式です。AWSにおいては、あるLambdaがEvent Bridgeにイベントを発行して複数のLambdaがそれを購読して処理を行う、S3バケットにオブジェクトがPushされた際にEvent Bridgeにイベントを発行してStep Functionsがそれを購読して処理を行うなどがあります。発行元からはイベントバスがイベントを受け付けてくれたことしかわからず、何個購読するシステムが存在するか、それらで行われる処理が成功したか知ることができません。発行元、購読先は互いには依存し合っておらずイベントのスキーマに依存しています。つまり両者は疎結合であり非同期です。

リクエスト・リプライ方式とはリクエスト元がリプライ先にリクエストを渡し、リプライ先がリプライを返したら次の処理を行う方式です。AWSにおいては、API Gatewayがクライアントから受け取ったリクエストをLambdaに渡して、帰ってきたリプライをクライアントに返す、あるLambdaがDynamoDB にデータを書き込む成功したと結果が帰ってくる *1などがあります。リクエスト元はリプライ先がどういったリクエストを要求しているか知っており、処理の結果をリプライとして受け取ることができます。つまり両者は密結合であり同期です。

サーバーレスなWebAPIを開発する際にはAPI Gatewayは同期的にLambdaを呼び出します。クライアントからリソースを作成、更新する際にAPI GatewayからLambdaを非同期で呼び出し、レスポンスは常に202 Acceptedを返すということはユーザー・エクスペリエンスを高めるために有効な手段ですが、クライアントを楽観的更新 *2に対応させる必要があり、もしくはPush通知が必要な場合はWebSocketが必要になります。最初からここに挑むべきではありません。

ただし、DynamoDBのデータが更新されたときにイベントを発行してくれる、DynamoDB ストリームという仕組みがあります。イベント・ドリブンしないを絶対として考えると、Lambdaを定期的に動かしてDynamoDBをスキャンして事前に保持していたデータと比較して...となってしまうと思います。これは止めてください、DynamoDBは頻繁にスキャンされることに適したデータストアではありません。この様に明らかにイベント・ドリブンすることが筋が良い場合はイベント・ドリブン方式を選択してください。他にはS3バケットにオブジェクトがPutされたときの処理やMQTTを使う都合上デバイスとの通信が非同期になるAWS IoTなどがイベント・ドリブン方式を採用するべき代表的な存在です。こういった場合でも全てをイベント・ドリブンにするのではなく必要な部分だけをイベント・ドリブンにするのが良いと考えます。

マイクロサービスしない

サーバーレスはマイクロサービスアーキテクチャを実現するために適したアーキテクチャではありますが、大規模なものは例外としてモノリシックアーキテクチャも実現できます。そしてサーバーレスにおいても一般的な場合と同様に開発者が多くコミュニケーションがしにくい、依存関係が複雑になっておりテストに時間がかかる、利害関係者が多く構成変更に調整が必要といった自体になっていなければモノリシックアーキテクチャの方がスムーズに開発が行えます。思い切って言い切ってしまえば、サーバーレスだとしても新規開発ならモノリシックアーキテクチャを採用しましょう。WebAPIとしてバックエンドを作りフロントエンドと分離ができていれば十二分だと私は考えています。しかし、既存の大きなモノリシックアーキテクチャが存在し、これが前述のしたような問題を抱えているならばマイクロサービスとして1つずつ切り出しながら構築していくのは妥当でしょう。

「マイクロサービスしない」に関して伝えたいことは上記で完結するのですが、文章が少なく寂しいので前述したサーバーレスがなぜマイクロサービスアーキテクチャに適しているか見解をお伝えします。それは可用性・耐障害性と保守性です。

サーバーレスアーキテクチャはAWSによって高い可用性が提供され、スケーリングも自動で行われます。とりあえず構築するだけでもAvailabilityZoneレベルの障害耐える構成ができあがります。

AWSにおいてスケーリングというとEC2でもAutoScalingがあるじゃないかと思われるかもしれませんが、設定してスケーリングができるEC2 AutoScalingと元々スケーリングが前提になっており調整をするだけで良いAPI Gateway、Lambda、DynamoDBのスケーリングは一線を画すると考えています。

サーバーレスアーキテクチャは大部分をAWSによって管理されているため、一部のAWSサービス使い方を理解さえすればソフトウェア設計、構築ができるエンジニアであればWebAPIを構築できます。TCP/IPの知識、Linuxサーバーのロギングやセキュリティ考慮といったことに明るくないバックエンドエンジニアを中心としたチームでも自分たちだけで開発、運用が行えます。出来上がったインフラはコンパクトに収まり、継続的に保守が行いやすいです。しかし、これはEC2で作る場合と比べて柔軟な設定ができないということの裏返しでもあります。

このようにサーバーレスアーキテクチャには高い可用性と保守性を持ちます、そのためマイクロサービスの文脈でチームが自分たちでインフラからAPIエンドポイントまで一気通貫で構築し、自分たちで構築するがゆえに自由にデータストアなどまで含めた継続的なデプロイが行えます。これが私がサーバーレスアーキテクチャがマイクロサービスに向いている理由の1つです。逆に中央集権的に管理が行いたいならばコンテナの作成までとそれ以外といった感じで責務分割しやすいコンテナの方が向いていると思います。

VPC Lambdaしない

LambdaはVPC外で動くのがデフォルトであるためVPCが必須のRDS *3やEFSなどを利用するためには、LambdaをVPC内で実行する必要があります。以前と比べてコールドスタートが約15秒→1秒になったり、大量にLambdaが同時実行した際にRDSコネクションを使い尽くす問題の対処としてRDS Proxyが登場したり様々な改善が行われています。

しかし、VPC Lambdaしないでください。VPC LambdaはLambdaの抽象レイヤーを1つ下げます、AWSが管理してくれていた部分を自分たちで管理する必要が出てきます。具体的に言うとVPCを構築し、LambdaにSecurity Groupを割り当て、Nat Gateway経由でインターネットアクセスを可能にしないといけません。VPC Lambdaをするとコールドスタートが遅いとかそういう話ではなく、そもそもVPCを意識して構築しないといけないこと自体がサーバーレスという文脈だとかなり大きなデメリットであると考えています。前述のしたようなリソースの構築、運用が必要になるし、これらのサービスや技術に関する知識も必要になります。

ではRDSやEFSを使いたい場合はどうしたら良いのか、素直にECSでコンテナを使って構築するのが良いと思います。私はサーバーレスアーキテクチャは様々な制約 *4を受け入れる代わりに高い可用性、保守性という恩恵を受けられる手段と捉えています、なのでVPCを使わざるを得ないならば制約の緩いコンテナ系サービスのECSを使うのが良いでしょう(本記事ではマイクロサービスしないという文脈があるので、EKSをはオーバースペックと考えECSを挙げました)。ALB、ECSであればAPI Gateway、Lambdaと比べて同期処理する際に実行時間の制限を受けないし、コールドスタートも発生しません。

アーキテクチャを検討していくなかで、実はサーバーレスにミスマッチだったという状況は良く見かけます。AWSが様々なサービス、機能追加をしてくれたおかげでミスマッチな場合でも回避策は確かにあります。そういったときは、なぜサーバーレスアーキテクチャを採用候補に挙げたのか思い返してください。VPCとその周辺の構築・運用コスト、Nat Gateway・RDS・RDS Proxyの利用費用が掛かってもVPC Lambdaという選択をするか、RDS・EFSなどのVPCが必要になるサービスの利用をやめるか、ECSを使いコンテナで構築するかしっかりと再度最初から検討しましょう。

あとがき

初めてサーバーレスに挑戦するときにマイクロサービス、イベント・ドリブン、VPCとその周辺について知識を持たないなら、紹介した内容をしないことで集中して挑戦できると思います。まずはシンプルに初めてぜひサーバーレスデビューしてみてください。あと、この記事変に捉えられて燃えそうで怖い。

脚注

  1. 正確にはDynamoDBは2/3に書き込みが完了した段階でクライアントに完了を返します。
  2. https://qiita.com/devneko/items/a636b81be76b9e2137f2
  3. パブリックアクセスを許可すればVPC外から利用できますが、一般的に
  4. Lambdaのデプロイパッケージ50MB制限、API Gatewayの最大タイムアウト29秒制限など