logo

アルパカログ

Next.js製のNotion BlogをVercelからGoogle Cloud Runに移行した

Next.js 製アプリケーションである Notion Blog を Vercel から Google Cloud Run に移行しました。

今見ていただいているこのブログがそうです。

このエントリでは、移行のきっかけと移行方法を説明します。

警告メールは突然に

このブログのアクセス数はゆっくりと増えてきていて、前日のPV数を確認するのが最近の楽しみでした。

しかしある時、Vercel から警告のメールが届きました。要約するとこうです。

Fair Use Policy に違反しています。対応しないと BAN します。

Fair Use Policy とは、各プランごとに定められた利用制限のことです。

確認すると Image Optimization Source Images が直近30日間で Hobby プランの上限である 1,000 を超えてしまっていました。

BAN だけは何としても避けなければ。急いで対応策を検討しました。

Proにアップグレードすると14日間は生き延びられる

警告メールの中で「Pro プランにすれば大丈夫だよ」と書かれていたのと、Pro プランには14日間の無料トライアルが含まれていたので、まずは Pro にアップグレードして時間の猶予を確保することにしました。

Pro にアップグレードするには Team を作成し、作成した Team の中へ Hobby プランで作成していたプロジェクトを引っ越し(Transfer) する必要があります。

一息ついた上で検討した対応策は次のようなものです。

  1. Vercel Pro をそのまま使い続ける
  2. Hobby プランの Fair Use Policy を満たすように何とかする
  3. Google Cloud に引っ越す
    1. Next.js を Google Cloud で動かす
    2. Next.js をやめて1から作り直す

1 の Vercel Pro をそのまま使い続けるについて、正直これが一番楽なのですが、毎月$20という費用は収益ゼロの個人ブログには結構な重荷です。

2 の Fair Use Policy の制限以内に何とかして収めるについては、実は Serverless Function Execution もすでに80%を超えていたので Image Optimization Source Images を何とかしても再度 Fair Use Policy に引っかかるのは時間の問題でした。

3-a についてはありがたいことに先人たちが資料を公開してくれていました。

ISR が使えなくなってしまうのは残念ですが、Cloud CDN を使えば ISR の基となる考え方である SWR は実現できることがわかりました。

また費用についても、コールドスタート前提であれば Cloud Run の無料利用枠に収まる見積もりでした。

3-b の Next.js 以外で作り直すについても検討しましたが、Next.js は SSG と SSR の併用が簡単であることから Next.js のままで続けることにしました。

ISRをSSRに書き換える

参考記事にも書かれている通り ISR (Incremental Static Regeneration) は Vercel でしか動作しないため ISR をやめて SSR (Server-Side Rendering) に書き換えます。

ISR から SSR への移行は簡単です。ソースに下記の変更を加えるだけです。

  • 各ページの getStaticProps({ props })getServerSideProps({ res, props }) に変更する
  • res.setHeader("Cache-Control", "public, s-maxage=300, stale-while-revalidate=86400") を追加する
    • Cache-Control ヘッダの有無による違いを体感してみたい場合は後から追加しても良い
  • return していたオブジェクトから revalidate を削除する
  • getStaticPaths() を削除する

SSR に移行したことにより、リクエスト毎に Notion API をリクエストすることになります。

ここで気になってくるのが Notion API のリミットです。

200 レスポンスは SWR に基づいてキャッシュを返すとして、問題はそうでないケースです。

タグ一覧 /tags/hoge-tag のように、動的ルーティングで値が取りうる範囲がわかる場合は検証し、そうでない場合は Notion API のリクエストが最小限になるようにします。

あわせて Cloud Monitoring などの監視ツールで 4xx や 5xx ステータスを監視し、これらのステータスの急増にすぐに気付けるようにしておきます。

Dockerfile を作成する

Cloud Run 用に Dockerfile を作成します。

あわせて .dockerignore を追加してビルド時にホストマシンの node_modules/ などが転送されないようにしておきます。

余談:リクエストを受け付けるポート番号

Cloud Run はデフォルトでポート 8080番をリッスンします。

一方、 next start はデフォルトでポート3000番を、環境変数 PORT があればそのポート番号をリッスンします。

Cloud Run のポート番号は環境変数 PORT によって渡されるので、ポート番号については特に何も設定する必要はありません。

Google Cloud にリソースを作成する

Google Cloud に必要なリソースを作成します。

  1. Artifact Registry に Docker イメージ用のリポジトリを作成する
  2. Secret Manager に NOTION_API_SECRETDATABASE_ID を登録する
  3. Cloud Build にトリガーを作成する
    1. cloudbuild.yaml
    2. NEXT_PUBLIC_URLNEXT_PUBLIC_GA_TRACKING_ID はビルドの環境変数に設定しておく
  4. Cloud Run サービスを作成する
    1. Secret Manager から NOTION_API_SECRETDATABASE_ID を環境変数として追加する
  5. Cloud Load Balancing で HTTPS ロードバランサを作成する
    1. バックエンドサービスを Cloud Run で作成する
    2. Cloud CDN の設定はデフォルト(静的コンテンツをキャッシュ)のままでOK
  6. IAM でサービスアカウントにロールを付与する
    1. Default compute service account に サービス アカウント ユーザーSecret Manager のシークレット アクセサー
    2. Cloud Build のサービスアカウントに サービス アカウント ユーザーCloud Run 管理者

リソースを作成したら自身のドメイン管理で HTTPS ロードバランサの IP アドレスに対して A レコードを作成します。

Cloud Run へのデプロイと SSL 証明書のプロビジョニングが完了すると SSR 版の Next.js が見えるようになります。

Vercel から移行してみて

移行にあたって Cloud Run で検証していたときに、パフォーマンスを改善できる部分がいくつか見つかりました。

それらの遅い原因があったのにもかかわらず、仕組みに乗っかっていれば速くしてくれる ISR は偉大だと改めて感じました。

ISR は使えなくなってしまいましたが、キャッシュが生成されてからの表示は高速なので及第点かなと思います。

以上です。このエントリでは、Next.js アプリケーションの Vercel から Google Cloud Run 移行のきっかけと移行方法を説明しました。