Next.jsでマルチテナントのサイトホスティング -SSGからSSRへの移行の裏側-

はじめに

RightSupport by KARTE(以下RightSupport)には簡単にサポートサイトを立ち上げる機能があり、RightSupportのサポートサイトもその機能を利用して構築されています。

RightSupportのサポートサイトのキャプチャ

RightSupportのサポートサイトのキャプチャ

サポートサイト機能はNext.jsで静的生成したファイルを配信する仕組みで運用していたのですが、いろいろな課題が出てきてサーバーでSSRへと移行をしました。先日、無事にアーキテクチャの移行が完了したので、SSGでぶつかった課題やどうやってSSRに移行したのかその裏側を書こうと思います。

サービス概要

RightSupportは施策を実現するために「アンサー」と称してクライアントのFAQやナレッジなどの記事を管理する機能があります。外部から定期的に取り込むことはできますが、“正” のFAQデータを預けてもらうとユーザーにとっても運用が楽です。

せっかく“正”のデータを預かっているのだから、誰もが必要とするFAQサイトを簡単に立ち上げることができたら嬉しいはずです。これが出発点でした。

ホスティングする内容は割とシンプルで更新頻度が少ないので、静的生成にどんぴしゃなユースケースだと思い、初期版は次のようなアーキテクチャに決めました。

  • 更新がある都度、サイト全体をNext.jsで静的生成(SSG)する
  • 生成されたサイトをAWS S3に上げて前にCloudFrontをかます
  • 管理画面の操作に応じてRoute 53とACMのAPIをバックエンドから叩いて弊社ドメインのサブドメインをCloudFrontに向けたり証明書を発行したりする
  • 独自ドメインを使う場合の証明書まわりを含む諸々のDNS設定を管理画面から提供する

結果、サイトを簡単に立ち上げられ、デプロイ後は爆速でAWSそのものがコケなければ絶対落ちないFAQサイト機能ができました。

課題続出でSSRへの移行を検討

この構成でサービスを運用しているうちに次の欠点が見つかりました。

  1. 完全SSGのため、ビルド時間がFAQ数に比例して増えてしまい、編集からサイトへの反映までの時間がだんだん伸びました。
  2. バックエンドサーバがない手軽さの反面、認可・認証が実装しづらかったです。CloudFront関数やLambdaである程度がんばれたかもしれませんが、制限が厳しく、知見があまりない中で「サイトを本番運用する前に手軽に限定公開するためのBasic認証」がやっとのことでした。
  3. サイトの数だけCloudFront distributionを立ててCloudFront関数を作るという、王道からだいぶ外れた仕組みのため、AWSの様々なリソース枯渇に悩まされる運命を辿りました。上限緩和を申請しても“裏上限”があり、そのまま進んだらAWSリソースの自前ロードバランシング的な仕組みを入れる必要が出てきたはずです。
  4. ビルド処理はなんちゃって手作りジョブキューみたいな仕組みで管理していて、割とバグの温床となっただけでなく、サーバへの負荷が高かったです。ちゃんとしたジョブキューシステムに載っければよかったと反省しています。

この中で3,4は運用者マターなので顧客には関係がないですが、顧客目線で1がかなり目立つ課題でした。コンテンツを編集したら即反映が当たり前の時代なのに、いけてないと反省しました。

そこでSSGからSSR、つまりサーバを立てる形に舵を切りました。

サーバを運用するデメリットを呑めば、それ以外の欠点が全部吹っ飛びます。

どうやって移行したか

Next.jsはサーバ機能も備わっていて、SSGとSSRが両方できるので、「モードを切り替えればいいじゃん」と安易に妄想してしまうかもしれませんが、そう簡単にはいきません。なぜなら、前提が真逆だからです。

SSGしていた仕組みでは、「全データを集めておいて必要なものを取捨選択して生成しちゃう」という世界でした。今度SSRでは次のようになります。

例えば https://hoge.example.com/faqs/my-faq/ というURLを踏んだらNext.jsサーバがリクエストを受けます。

このサーバはマルチテナントなので、まずどの顧客のデータを見ればいいのか特定できなければなりません。

Next.jsのmiddlewareの中でHostヘッダーを見て /hoge.example.com/faqs/my-faq/ という形にパスを書き換えて /pages/[domain]/faqs/[slug] のNext.jsページに当てはめます。

これでdomainがhoge.example.comでmy-faqという名前のFAQであるという2点の情報から、必要最低限のリソースをピンポイントで効率よくとってくるという構図です。

難点をかいつまんで紹介すると、

  • RightSupportのドメインオブジェクトは複雑なものもあって、このデータフローを実現するために利用中のリソースを特定するロジックの作り込みがやや大変でした。
  • Next.jsアプリケーションは3つのランタイムが動いていて、場所によってNode.jsの機能が使えなかったりで割と制限がシビアでした。相性のいいDBライブラリの特定(既存の仕組みが相性悪いとわかるまでの検証)も時間を要しました。特にmiddlewareでDBが叩けなかったのが全体のフローに大きく影響しました。
  • 主な取得ロジックができた段階ではサイトは動いていましたが、パフォーマンスはSSGとは雲泥の差でした。調査したらDB接続に関する凡ミスと、DBネットワークとのVPC peeringで概ね解消しましたが、突き止めるまでのフットワークに時間がかかりました。
  • サーバを動かすクラウドの選定。サーバ自体はどこでも動かせますが、うまくいけば大量のドメインが向けられるので大量のTLS証明書を設定できるのはマストです。幸いGoogle Certificate Managerの証明書マップエントリー、つまり1つのロードバランサーに紐付けられる証明書の数などが十分に多く、安心してサーバをGoogle Runに載せることにしました。

上記を経てSSRサーバで動く新サポートサイトの誕生が迫りましたが、そこで2つの難関が現れました。

SSGの同時運用

SSR版の開発をゴリゴリ進めている中、SSG版の改修がある程度行われていました。

最初は両方でUIコンポーネントを共有する構想もあったのですが、実現が難しくて諦めました。

代わりにどうしたかというと、コード変更を検知して、なるべく差分を自動で適用する仕組みを作ってCIで運用しました。

  1. SSG版を触ったPRが上がると、前回同期したコミット以降の差分をCIが見る
  2. 差分があったら、エラーログを出して担当者を巻き込むよう指示すると同時に、ローカルsyncコマンドを案内する
  3. ローカルで叩くsyncコマンドを用意しておいて、軽微な変更なら素早くSSR版にも適用できる

こんな半自動な形でSSR版の完成、SSG版のフィーチャーフリーズまでなんとか事故なく漕ぎつけました。

悪魔のDNS

SSR版が爆誕しても、SSG版のクライアントを勝手にマイグレーションできるわけではありません。

なぜなら独自ドメインを使っている場合はクライアント所有のDNSを変更してもらう必要があるからです。

DNSが厄介な理由はいくつもあります。

  • 比較的詳しい人でも滅多に触らないので思い出しながら作業したり案内したりせざるを得ません。
  • 弊社としてDNS設定を代行できるわけではなく、必要な値を伝えることしかできません。顧客は内部でハンドリングできても詳しい担当者がいる確率が低く、外部に委託している場合は作業のリードタイムが長くなります。
  • 操作方法は先方のDNSプロバイダによるので画面操作の案内ができません。
  • 今時は変更はすぐ反映される印象を受けますが、「最大72時間かかる」と案内されることもよくあり、反映が遅いのか何かを間違えたのか判断しにくいことがしばしばあります。今回はDNSの新規設定ではなく、SSRへのマイグレーションなので設定変更であるという一捻りもありました。

SSG版で案内したのは次の2つ:

  1. AWS Certificate Managerの証明書発行用のCNAMEレコード
  2. ドメインをAWS CloudFrontに向けるCNAMEレコード

ですが、今回は次の2つに変わります:

  1. Google Certificate Managerの証明書発行用のCNAMEレコード
  2. ドメインをGoogle CloudのLBに向けるAレコード

ここで惜しくも予想できなかった落とし穴として、上記2と4は共存できないので、顧客のDNS管理画面の仕様によっては設定がしれっと失敗したり、エラーやガイダンスが表示されても気づけなかったりで、結果として4の設定に失敗するケースが続出しました。

弊社サポートサイトの記事やサクセスチームからの案内、移行フローの改善(DNS変更の自動検知)などで挽回して、SSR版の爆誕から8ヶ月かけて全てのクライアントのサイトの移行が無事に完了しました。

おわりに

なるべくSSGにしてサーバの負担を減らす世の中の流れとは逆方向に進めたのは少し意外性のある話かもしれません。

ですが、現段階ではメリットが大きいと感じており、技術選定や方針策定を行う際はサービスの性質を見極める必要性を改めて実感させる案件でした。

積極採用中!

RightTouchではカスタマーサポート市場の改革に挑むプロダクトエンジニアを採用中です!

もし興味があれば、ぜひお話ししましょう!