ヘッドレス CMS 運用におけるデプロイと環境差分について

こんにちは。武田(@tkdn)です。

MVP でスタートさせた au Webポータル 無料ゲーム というサービスを、手製のビルドスクリプトと手製のコンフィグで生成していた静的サイトから、Next.js SSG + ヘッドレス CMS へ 9 月にリニューアルしました。

リニューアルした理由に触れつつどうやってデプロイ・運用しているか、あたりを中心に今日は書いていきます。

なお、この記事は Jamstack Advent Calendar 2020 15 日目の記事です。


前提

Next.js

Next.js は React を利用したフロントエンドフレームワークです。SSR/SSG などを実現できるほか、ディレクトリ構成による動的ルーティングや API ハンドラの取り付けなども可能で、要件次第ではありますが個人的には積極採用できるメリットが多いと考えています。

昨今のフロントエンド事情を汲み取ったうえで、最初から開発者のパフォーマンスを引き出せるだけではなく、Web パフォーマンス指標(最近の Web Core Vitals など)の取り組みにも力が入っており Next.js Conf での発表が記憶に新しいところです。

ヘッドレス CMS:microCMS

microCMS は API ベースでテーブルを作成し多用なフィールドに対応したカラムを追加することで直感的に操作できるヘッドレス CMS です。Contentful、GraphCMS、Prismic など候補はあったのですが、日本語でわかりやすく運用メンバーでも容易に操作できるという点で microCMS を選択しました。

microCMS さんには紹介記事でも取り上げていただいています。
microCMS 導入事例 - 開発を介さなくてもコンテンツが更新できるように!

リリースまでのリードタイムを短くしたい

なぜリニューアルしたかについては、ソース管理のつらさ・それに伴うリリースの人依存を排除して、リリースまでのリードタイムを縮めたいという思いからでした。

MVP としてリリースした 2019 年春段階では 10 本ほどだったゲームタイトルも、今では 100 本以上になっています。リニューアル以前はゲームタイトルなどのデータを記述したファイルをマニュアルで更新し、ビルドスクリプトやテンプレートによって静的サイトを生成していました。これが運用において非常にしんどくなっていたのです。

さらにゲームタイトルがソース管理されているため、ビジネスサイドでゲームタイトルを追加したくても、開発者が別の作業で対応できない場合はスプリント中のリリースを諦める必要がありました。定期的なリリースによってユーザーに新しいゲームと届けられないというのは口惜しい限りです。

リニューアル後のデプロイ、環境差分チェック

microCMS 導入によりリニューアル後の運用は想定よりもだいぶ楽になりました。デプロイ・リリースに関しても CMS webhook を介し自動化されており、作業コストはかなり低くなり気軽にリリースできる環境が整っています。

以下ではデプロイフローをどう工夫しているか、CMS の制約により本番環境とステージング環境のデータ差分チェックを自動化していることについて触れていきます。

デプロイフローの工夫

デプロイフローはチームメンバーが下記のように組んでくれています。

  1. リリース作業者が CMS 上でリリース用レコードを追加します
  2. CMS 上に設定した webhook により AWS API Gateway + lambda へリクエストが送られます
  3. 通知を受けた lambda は webhook では不足している情報を取得するためさらに microCMS のリリース API へリクエストし情報を取得します
  4. 取得した情報からデプロイする環境をパラメータにセットし、ジョブをトリガする CircleCI API へリクエストします
  5. ジョブが起動すると Next.js が適切なパラメータを受け取り環境向けに SSG のビルドを始めます
  6. ビルドされた成果物を S3 に PUT してデプロイ完了です(Cloudfront を利用しているのでデプロイ後は Invalidation も挟みますが)

microCMS webhook の注意点

前述の 2 と 3 の工程を見ていただくと分かるように、microCMS webhook のリクエストは以下のようにレコードに対する情報をすべて付帯していません(記事投稿時点)。

{
    "service": "awesome-game-app",
    "api": "release",
    "id": "5wig8fqa6",
    "type": "new"
}

id がユニークキーとなるので /release/5wig8fqa6 へ再度リクエストし情報を取得します。webhook だけでは CMS で管理する情報が得られないので注意が必要そうです。

不要にムダなリクエストを発生させない

我々は Standard プランで利用しているので(料金参照)、データ転送量は 200GB となっています。ゲーム用のサムネイルやバナーなどの画像を microCMS からアップロードしたホスト先のままにしては転送量のリミットを超えると判断して、画像も自前の S3 でホスティングしています。もちろんハンドルしやすさを自分たちの手元に寄せるという意味も含みます。

さらに転送量だけではなくビルド時もエコな考慮をしました。Next.js ビルド前(前述の工程では 5 の前)に画像をダウンロードするスクリプトを実行しているのですが、前回ビルド時のタイムスタンプを status.json のように S3 バケットに同梱しておき、ゲーム情報のレコードに追加・更新があった場合のみ画像をダウンロードするようにしています。スクリプトのサンプルも掲載しますが、リトライを 2 度目まで行うこと(vercel/async-retry を利用)、並列実行できることなどを実現しています。

スクリプト参考CI でのダウンロード実行

制約による、ステージングと本番データの差分チェック

ステージング環境と本番環境などを用意するのはどのプロジェクトでも当然ですが、microCMS で環境によるデータの棲み分けをひとつのプロジェクトで実現しようと考えた場合、データが公開状態か下書き状態(いくつか種類があります)かで判断する必要が出てきました。

開発当時 microCMS では単一のレコード取得で下書きを取得できたものの、リストでは下書きを取得できないという問題がありました(現在では取得可能です)。またテーブルにカラムを追加したいなどの改修に対応しづらいということもあったので、プロジェクトは「商用向け」「ステージング向け」と 2 つに分けデータも二重で管理しています。

ただステージング環境では本番環境同等のデータを入れて QA の受け入れをクリアしなくてはいけません。そのため二重管理というのはステージング環境のテストデータを作りやすいという反面、本番と同等のデータを作るために人間によるオペミスのリスクが 2 倍になります。 具体的にはステージングで入れたデータと同じだと思って入れた本番データに差分がありリリースしてから発覚するようなケースでしょうか。

そういったリスクのために複眼チェックを人間がやるのはかなり馬鹿らしかったので、CircleCI Cron Job でステージング環境と本番環境の差分チェックを自動化しています。毎日 10:00 ころに実施され結果は Slack に報告されます。

ヘッドレス CMS の運用

開発者フレンドリーに作られたヘッドレス CMS は有効な選択肢であると今回採用して感じました。かゆいところに手が届くという開発者の気持ちが汲まれているだけではなく、採用した microCMS はなんと言っても日本語で直感的に操作が可能という、開発者と運用するメンバーにとってバランスが取れた製品であることも大きなメリットだと感じています。

API が GraphQL であるとか REST であるなどは関係なく、何よりサポートの手厚さや問い合わせなどスムーズにコミュニケーションできるというのはヘッドレス CMS を実際にプロダクトで採用するにあたって一番重要だと考えます。microCMS さんはツイートしたり管理画面からのチャット問い合わせなどにめちゃくちゃ早く対応してくれますし、数ヵ月前のやりとりを実装後にフォローしていただく場面もありました。

質問と返答機能実装後のケア

最終的に microCMS をべた褒めするような記事になってしまいましたが(笑)、本日は下記をお伝えしました。

  • ヘッドレス CMS webhook から CircleCI での Next.js ビルドのためのジョブ起動、そしてデプロイ
  • ヘッドレス CMS で商用環境・ステージング環境のデータ差分をチェックするためのしくみ

以上、武田(@tkdn)でした。