wheatandcatの開発ブログ

技術系記事を投稿してます

Vercelの運用コストをCloudflareで1/10まで下げた話

概要

少し前に KAWKAW という架空ショッピングサービスを公開したところ、予想外にバズって想定を大きく超えるアクセスが発生した。

kawkaw.app

最初は Vercel 単体で運用していたが、アクセス数の増加により Edge Request だけで 1日あたり 10 ドル以上 かかる状態になった。趣味で続けるには厳しいコスト感だったため、Cloudflare を組み合わせて構成を見直したところ、最終的に 1日 1 ドル以下 まで抑えられた。その手法をまとめる。

前提

KAWKAW は基本的に静的コンテンツ中心のサービスで、唯一ユーザー投稿のレビューだけを DB で管理している。
そのため、キャッシュをかなり有効に使える構成になっている。

ただし、そのような「ほぼ静的なサイト」でも、Vercel 単体運用だと思った以上にコストがかかる落とし穴があった。

初期の実装

少人数利用を想定していたため、まずはデプロイしやすさを優先して以下の構成で作成した。

  • デプロイ先: Vercel
  • フロントエンド: Next.js
  • バックエンド: Next.js API Routes
  • DB: Neon

実際に発生していたコスト

ピーク時は Edge Request だけで 1日 16 ドル 発生していた。

VercelのEdge Requestとは?

Vercel の Edge Request は、ネットワークで処理されたリクエスト数に応じて課金される仕組み。
Pro プランでは 月 1000 万リクエストまで無料 だが、それを超えると 100 万リクエストごとに 2〜3 ドル 発生する。

実際の推移は以下の通りで、無料枠を使い切るとアクセスのたびにかなりの料金が積み上がる。

Vercel 自体にもキャッシュ機能はあるが、キャッシュヒット時でも Edge Request としてカウントされる 点に注意が必要。

つまり、Edge Request の課金を抑えるには キャッシュ時にそもそも Vercel へ到達させない 構成が必要になる。
そこで Cloudflare を導入した。

Cloudflareとは

www.cloudflare.com

Cloudflare は CDN などを提供するサービス。名前は以前から知っていたが、自分の開発してきたサービスでは使う機会がなく、今回が初導入だった。実際に使ってみるとかなり良く、今後利用機会が増えそうだと感じた。

今回使ったのは以下の 2 つ。

Cloudflare CDN

Cloudflare CDN を使うと、指定したドメインへのアクセスを Cloudflare が中継し、キャッシュが存在する場合は Cloudflare 側からそのままレスポンスを返せる。
キャッシュルールも Cloudflare のコンソールから設定可能。

しかも、複雑なルールを使わない範囲であれば 無料で利用可能。KAWKAW でも無料の範囲で運用できている。

当初は「HTML を Cloudflare CDN でキャッシュすれば、Vercel の Edge Request 問題は解決する」と考えていた。
ただ、実際に導入して様子を見るとキャッシュ率が 30% 前後 までしか上がらず、想定よりかなり低かった。調べたところ、以下の記事を見つけた。

zenn.dev

HTTP の標準には Vary というキャッシュ判断に関わるヘッダーがある。

developer.mozilla.org

Next.js はキャッシュ制御に Vary を使っているが、Cloudflare は Vary をキャッシュキーの判断に使わない設計 になっている。

developers.cloudflare.com

この設計差によって、Cloudflare CDN 単体ではうまくキャッシュが効かなかった。
ここを解決するために Cloudflare Workers を使った。

Cloudflare Workers

Cloudflare Workers は、Cloudflare CDN 内部で動作するサーバーレスな JavaScript / TypeScript ランタイム。

Cloudflare CDN までのアクセス経路が以下だとすると、

User → Cloudflare CDN → Vercel

Workers を挟むことで、以下のような構成にできる。

User → Cloudflare CDN → Cloudflare Workers → Vercel

この構成にすることで、Cloudflare Workers 内で問題になっていた Vary ヘッダーを加工 し、Cloudflare 側でキャッシュ可能なレスポンスに変換できるようになった。

Cloudflare Workers の料金体系は 月 1000 万リクエストまで無料。それ以降は 100 万リクエストごとに 0.3 ドル

単純なリクエスト単価で見ても、Vercel と比べておおよそ 1/6 程度 まで下げられる計算になる。
実際には HTML 以外の静的ファイルは Cloudflare CDN 側で完結するため、体感ではさらに安くなった。

実装後のコスト変化

以下の通り、16 ドル → 0.27 ドル と大幅なコスト削減に成功した。
もちろん Cloudflare Workers 側でも課金は発生するが、それを含めても 1日 1 ドル未満 に収まっている。

まとめ

  • Vercel 単体では、キャッシュが効いていても Edge Request 課金が積み上がる
  • 静的コンテンツ中心のサイトでは、Cloudflare を前段に置くことで大きくコストを下げられる
  • 特に Next.js と Cloudflare の間では Vary の扱いがネックになりやすく、Cloudflare Workers で吸収する構成が有効
  • 最近はコストカット系の開発をやる機会が少なかったので、今回の改善はかなり楽しかった