ランニングコストほぼゼロから始めるスケーラブルな本番環境
はじめに
私は個人開発で一山当てたいと常々思っていて、そのためにいくつかヒットしそうなサービスのアイデアがあります。エンジニアであればアイデアを具現化することに躊躇してはいけないと思うわけですが、一度リリースしてしまうとランニングコストが発生するわけで、仮に全く人気がでなかったとしたらランニングコスト分の赤字を垂れ流すことになります。
一方、個人開発者というのはおそらく誰しも夢見がちなので、リリース後バズったりしてユーザーが大量に押し寄せてきてしまってサーバーダウンする可能性も考えてしまいます。
その結果、「全く誰も来なくてランニングコストが赤字になったらどうしよう」という不安と「めちゃくちゃバズってしまってサーバーダウンしてチャンスを逃したらどうしよう」という不安が、心の中でせめぎ合うことになります。
そこで、今回はその2つの不安を一気に解消する「使われなければランニングコストが限りなく0円」なインフラ構築を解説したいと思います。
なお、私が使い慣れているという理由で、Laravelでの利用を考慮した話になっていますが、特にどの技術スタックに限らず有益な内容にはなっているはず。
時代はスケーラブルなサーバーレス環境
先ほどあげた不安の最たるものは、アプリケーションサーバーの問題です。世の中、単にコストをかけたくないだけであれば、Herokuの1xDyno(512MB)、GCPのGCE E2-Micro(0.25vCPU/2GB)あたりを使えば永続的に無料ですし、Oracle Cloudならx86コアメモリ1GBのVM2台と、Arm4コア24GBのVM(最大4台まで分割可能)が永続的に無料で使えます。私もすでにリリース済みのサービスにてOracle Cloudのお世話になっており、細々とサービスを運営するのであればサーバーリソースには全く困らないという状態です。
しかし、先ほど述べた2つの不安のうち「バズってしまったらどうしよう」は払拭されません。スパイクするほどのトラフィックは、仮にOracle Cloudであっても安定して捌くことは厳しいでしょう。たぶん。
そう考えると、スケーラブルな環境が必要だとの結論になるわけです。加えて人が来なかったらランニングコストが発生しないよう、マシンの稼働時間ではなくリクエストの処理時間で課金して欲しいとなります。
よって、自ずと選択肢はリクエストベースのサーバレス環境に絞られます。しかしこういったサーバーレス環境は、どこもPHPに対してはしょっぱいので、消去法として残るのはAWS Lambdaのみだったりします。実はLambdaもPHPをサポートしていませんが、brefというパッケージのおかげでレイヤー上にPHPのカスタムランタイムをアップロードすることができるため、Laravelを実行することができるのです。
実際Laravel公式では、Laravel VaporというLambdaで動くサーバーレスLaravelの環境構築ツールを月額39ドルで提供していたりします。
加えてLambdaの場合、常時無料枠として100万リクエストおよび40万GB-秒のコンピューティングタイムが無料となるため、この点も大きな魅力でしょう。
例えば、リリースしたサービスがスタートダッシュを決めて月間100万リクエストに達したとします。Laravelの実行に毎回メモリ512MBで1秒かかったとすると、リクエスト数に対する課金は0円、コンピューティング時間は80万リクエスト分の時間が無料枠として減額されることになります。結果20万秒分だけが請求対象となります。512MBの場合、1sで0.0000083円となるため、1.66ドルとなります。これにAPI Gatewayが1ドルぐらい追加で乗ってくる計算になります(12ヶ月の無料枠あり)。
なおLambdaでは、x86以外にArmベースのGraviton2も提供していて、x86に比べてメモリあたりの料金が20%割安な上に処理速度も19%ほど向上しています。そちらを使うことができれば、リクエストあたりの料金を更に下げることができる見込みですが、現在bref側がArm版のPHPランタイムを提供していないため、導入を進めようとはしているものの、まだ先の話となりそうです。
DBもスケーラブルで行こう
こうしてアプリケーションサーバーは、スケーラブルな環境を手に入れることができました。しかし、Lambdaでスケーラブルな環境を夢見た人の前には、RDS+RDSプロキシの壁が立ちはだかります。スケーラブルな環境であるということは、必然的にDB側のコネクション数がボトルネックになるため、それに適した構成が必須となります。この構成、個人開発者にとっては高嶺の花過ぎます。仮にvCPU1つのt2.smallインスタンスを用いた場合でも、30日で最低21.6ドルの料金が発生します。これに従来のRDSの料金が乗ってきます。これは全くユーザーが来なければ非常に辛い金額でしょう。
そこで、今回私が採用したのは外部のDBです。その名もPlanetScale。最近、エンジニア界隈でもQiitaの記事がバズったりして名前が知られるようになりました。もともとは使うにあたって金額面での制約が大きかったのですが(無料枠と有料の固定枠が小さい上にその枠を超えた時の従量課金額が痛い)、VCから多額の投資を受けた結果、2/24に大幅な価格改定が行われて、個人開発者にも安心して利用できる料金体系となっています。
このサービスは、YoutubeでMySQLサーバーのスケールに使われていたOSSのVitessをベースとしており、同時接続数は無料版で1000まで、29ドルの有料版で10000まで対応しています。
よって今回使用するLambdaとは非常に相性が良いのです。しかも無料版でさえストレージに10GBを使えます。加えて東京リージョンまで選べるという充実っぷり。またMySQL互換であるため、普段からLaravelを使ってる人間には扱いやすいとも思います。
なお、他に似たようなサービスとしてCockroachDB Serverlessという名前が非常にアレなものがありますが、こちらはストレージが5GBで東京リージョンはなく、Postgres互換であり、2022年3月現在はまだベータ版ということもあって、今後に期待といったところです。それでもHerokuから無料で提供されているPostgresよりは、魅力的だとは思いますが。
Redis使いたいよね
以上LambdaとPlanetScaleさえあれば、すでに大抵のユースケースでは大丈夫なはずですが、セッション管理やキャッシュに関してはリクエストベースのサーバーレス環境特有の辛みがあり、どうせならRedisを使いたいよねとなります。
具体的に何が辛いかというと、セッションで管理する情報はファイルが使えずcookieに書き込むことになる(普通はキーのみで情報は別途サーバーに照会する)ためセキュリティの面で不安があります。DBに格納することも可能ですが、その場合定期的にデータを削除する手間が発生します。キャッシュについても同様です。
AWSならこのソリューションとしてDynamoDBも魅力的に映りますが、料金体系が複雑で、高額請求されそうになった話も転がっていたりします。それでは今回の目的を逸脱してしまいます。
そこで今回は、サーバーレスRedisとして注目されているUpstashを使うことにします。これは以前、Lambda Storeというサービス名で一時話題になっていたものです。いつの間にか改名したのですね。このサービスはコマンド単位の課金体系となっていて、かつ日に10000までのコマンドなら無料枠で利用が可能です。ただしデータサイズは256MBの制限があるためその点は注意が必要です。なお有料固定枠は100Kコマンドごとに0.2ドル、データサイズ1GBで0.25ドルです。つまり毎日無料枠の10倍の10万コマンドを実行し、かつデータサイズは常に1GBを使い続けるケースでは、月に6ドルほどの出費となります。
アセットはCDNから配信したい
さて、今度こそ本当に完璧な構成と言いたいところですが、Lambdaではアセットをs3に置くことになるものの、それをそのまま使ってしまうと、gzipやBrotliで圧縮されて配信することができません。かといってs3にアップロードするタイミングで毎回圧縮をかけるのも非効率です。
また、全く何もしないというのも、個人開発とはいえGoogleのCoreWebVitalの数値の悪化、ひいては利用してくれるユーザーの体験を損なうことになりかねません。そこでCDNを使おうということになります。
一昔前だと、このCDNの利用料金もそこそこした記憶があるのですが、最近はFastlyやCloudflareなどの競合を意識してか、AWSのCDNであるCloudFrontにおいて2021年の11月にかなりインパクトのある料金見直しが行われています。特に1年間の無料枠が常時無料枠に拡大したことにより、サービスの利用状況に限らず安心してCDNを最初から使っていけるようになったと言えるでしょう。
なお、上記無料枠拡大により、CloudFrontからのデータ転送量は1TB/月まで無料、リクエスト件数1000万/月まで無料となっています。AWSの場合、転送料金もボディブローのように響くことが知られていましたが、上記改訂により個人開発では余程のことが無い限り、1ヶ月を0円で乗り切ることができるようになったと言えるでしょう。
クレデンシャル情報を安全に扱いたい
サーバレスな環境では、クレデンシャル情報をデプロイのたびに渡す必要があります。しかしローカル環境や標準的なGitのリモートリポジトリで管理するのは危険です。そこで、デプロイ時にAWS System Manager内のパラメータストアを使うことになります。似たようなサービスとして、AWS Secrets Managerというのがありますが、有料な上に一般的なユースケースでは過剰な機能と言えるため、無料で使えるパラメータストアで十分です。
金食い虫となるBotを防ぐ
スケーラブルな環境で怖いのは、広告収入やサブスクに全く関係がないトラフィック(例えばDDoS攻撃)が来ることです。リクエスト単位での課金となるわけですから、このトラフィック1つ1つが出血に繋がります。なので、この無駄なトラフィックへの対策はとても重要です。
この点においても、CloudFrontを通しておけば、自動でAWS Shield Standardが有効になるため一定の効果が期待できます。また特定地域からの過剰なアクセスも、手動での設定となるものの一定弾くことが可能です。
ただそれだけでは不十分な点もあり、アプリケーション側の実装で、Botが来た場合にUpstashのコマンドを実行しないようバイパスする処理も併せて必要かなと思います。
デプロイ環境を作る
Lambdaにデプロイする方法として、世間で出回っている方法は全部で3つあります。
AWSが公式に提供しているSAMコマンドを使ったデプロイ、Laravelが公式に提供している有料のVaporを使ったデプロイ、そしてServerless FrameworkというOSSを使ったデプロイです。
このうちVaporを使う余裕があるなら、そもそもこんな企画は必要無いわけで、選択肢は実質2つとなります。私は両方とも使ってみましたが、SAMコマンドを使ったデプロイは最初のセットアップが結構手間だと感じたので、Serverless Frameworkを使うことをお勧めしたいと思います。ほぼゼロコンフィグな状態でデプロイできるためです。
ただ、先に述べたパラメータストアとの連携やs3へのアセットのデプロイも行う場合は、それなりに設定は必要です。
想定される月額料金は?
10万リクエスト未満ではs3とAPI Gateway以外には課金されないはずなので、おそらく2〜3ドルの範囲で収まると考えられます(12ヶ月無料枠があるなら0ドル)。
それを超えるとUpstashの無料枠での利用が厳しくなるはずなので、有料枠に切り替える必要が出てきます。これにより、+1〜2ドル程度で50万リクエスト/月までは対応できると予想しています。
80万リクエストを超えると、次はLambdaの無料枠を使い切ってしまうため、100万リクエストではおよそ8ドル(LambdaとAPIGが4ドル、s3が2ドル、Upstashが2ドル)ほどかかってくる計算となります。
初心者にはお勧めしない
ここまで書いておいてひどい話ですが、一通り構築してみた感想はこれに尽きます。
いくらランニングコストがお安いうえに高いスケーラビリティを手に入れられるとしても、動くようになるまで1週間ほどトライアンドエラーな日々でした。特に、CloudFront周りの設定がなかなかに厄介でしたし、UpstashとのTLSベースの疎通はほぼノーヒントで試行錯誤するはめになりました。Serverless Frameworkを経由したパラメータストアとの連携も情報が少なくて難儀しました。
またPlanetScaleも無料枠が拡大したとはいえ、readの制約条件に罠があって、レスポンスされた件数をカウントするのではなくスキャンした件数をカウントする仕様です。つまりインデックススキャンやテーブルフルスキャンを意図せず起こしてしまうようなエンジニアだと、簡単に無料枠を突き抜けてしまうリスクがあります。
今後、構築手順についても詳しくまとめようとは思うものの、プログラミングを勉強してポートフォリオを作るぜ!ぐらいのレベルの人は、大人しくエックスサーバーのような性能とコストのバランスの良いレンサバを使うのが幸せかなと思います。
参考までに、今回利用した技術アセットを全部並べると以下のようになります。
- AWS
- Lambda
- API Gateway
- CloudFront
- S3
- CloudWatch
- Lambda@Edge
- IAM
- AWS System Manager Parameter Store
- AWS Certificate Manager
- AWS Shield Standard
- PlanetScale
- Upstash
- Serverless Framework
- bref
さいごに
元々私は、EC2などのインスタンスorサーバーを使ってインフラを構築していて、サーバーレスで構築したのはGAEを組み合わせた時ぐらいでした。
特にLambdaなどのリクエストベースのサーバーレス環境は、DB側がボトルネックになってお金で解決しない限り辛そうと思ってました。
しかし今回、DBの分野でPlanetScaleのような野心的なサービスが登場し始めたことで導入ハードルが一気に下がったように感じています。
まだまだ事例が少ないので当面は個人開発で使うぐらいですが、新規事業のプロダクト開発では、近い将来こう言った組み合わせも選択肢になってくるのかもしれません。
Discussion