プロフィール画像

Masaki Nishi
ソフトウェアエンジニア

GatsbyからNextJSに移行してブログを作り直した

WordpressをヘッドレスCMSとして運用し、ISRとSSRのブログを構築する

next14

GatsbyJSはReact製の静的サイトジェネレーターで、現行の最新バージョンは現時点でv5です。以前のブログはGatsbyJS v1で作成されていました。

SSGでセキュリティ的なリスクが低いため、アップデートをサボっていましたが、いつホスティングサイトでビルドのサポートがされなくなるかわからなかったため、NextJS v14でブログを再開発することにしました。

NextJSを選定した理由

Gatsby v1からv5にアップデートするのは面白みが感じられないのと、業務でも使用する可能性があるNextJSを触ってみたかったという単純な理由です。

開発体験と性能だけを考えるならAstroを採用していたと思います。

ブログアーキテクチャ設計

以前のアーキテクチャ

myblog-asis-architecture

以前はEC2の無料枠で構築したインスタンスでWordpressを運用し、REST API経由で取得した記事データを元にNetlifyでビルドとホスティングを行ってました。

画像配信は画像用CDNのimgixを使ったり等、紆余曲折ありましたが、S3とCloudFrontを経由して配信する形で落ち着いていました。

しかし、上記のアーキテクチャだと、NetlifyのCDNが無料枠で日本のエッジロケーションをサポートしていないため、レイテンシが遅いなどの問題がありました。GatsbyJS v1の従来のSPA方式だと、レイテンシの問題と相まって、初期ロードに時間がかかることが難点でした。

また、EC2の無料期間が終了し、利用料金が課されるようになりました。

移行後のアーキテクチャ

myblog-tobe-architecture

問題を解決するために、Oracle Cloudの無料枠のVMインスタンス上にWordpressコンテナを立ち上げて運用するように変更しました。また、GatsbyJSからNextJSに移行するため、ホスティングはVercelを利用することにしました。

Oracle Cloudは無料枠でも、4OCPU、24GB MemoryのVMインスタンスを東京リージョンで立ち上げることができるため、他のパブリッククラウドよりもかなり優秀です。アメリカの一部リージョン等でしか無料枠がなかったり、リソースのスペックも限られているパブリッククラウドが多いなか、Oracle Cloudは価格の面で優位でした。

Vercelは、無料でNextJSのISRやSSRを使用することができ、東京のエッジロケーションを使用することができます。

上記のアーキテクチャで、レイテンシとそれに伴う初期ロード時間を大幅に改善することができ、運用費もS3とCloudFrontのコストがほとんど掛からないため、ほぼ無料にすることができました。

インフラ構築

Oracle CloudのVMインスタンスを構築

構築済みのS3やCloudFrontを除いて、構築が必要なものはOracle CloudのVMインスタンスのみのため、比較的簡単にできるかと思いきや、東京リージョンで無料枠のインスタンスを建てると、リソースのキャパシティがないというエラーでリソースを立ち上げられなかったため、早い者勝ちとなっていました。

解決策を探していると、「oci-arm-host-capacity」GithubリポジトリのREADMEに行きつき、構築するインスタンスのTerraformファイルを使って、シェルスクリプトで繰り返しリソース構築のリクエストを送るという荒技でリソースを建てられるらしいという情報を得ました。

試してみると9回目のリトライでインスタンスが立ち上がってました。完全に運だと思うのでラッキーでした。

WordPressのコンテナ構築

公式ドキュメントの「Host a WordPress site in a Docker Container on OCI Free Tier Always Free services」を参考に、Wordpressのコンテナを立ち上げていきます。

ただし、上記のドキュメントでは、SSL証明書を噛ませるためにロードバランサを立ち上げていますが、一つのインスタンスにラウンドロビンは必要ないので、Docker ComposeでnginxのリバースプロキシとLet’s Encryptのcertbotのコンテナを立ち上げてSSL化するように変更しました。

併せて、ロードバランサを経由しないでインターネットに接続する必要があるので、別途ネットワーク関連の設定が必要になります。全体で必要な作業は以下となっています。

  • VCNのポート解放をするため、セキュリティリストを追加
  • Linuxファイアウォールの設定を変更してポートを解放
  • docker、docker-composeのインストール
  • gitのインストールと設定
  • WordPress, MySQL, Nginx, Certbotのコンテナを立ち上げるdocker-compose.ymlを作成
  • リバースプロキシ用のnginx.confとSSL証明書インストール用のnginx.confを作成
  • SSL証明書インストール用のnginx.confでコンテナを立ち上げてSSL証明書をセットアップ
  • リバースプロキシ用のnginx.confでコンテナを立ち上げ直す
  • インスタンスの再起動後にコンテナを復帰させるため、サービスファイルを作成しsystemctlを実行する

これでWordpressが立ち上がって、適切にアクセスすることができます。

WordPress移行

記事や画像データ、プラグインを移行する前に、以下の設定が必要でした。

  • プラグインを管理画面からインストールする際にFTP接続を求められないようにする設定
  • php.iniとnginx.confでタイムアウトの設定とファイルサイズを変更し、移行時の大きいデータを許容できるようにする設定

その後、Wordpressの純正のインポート・エクスポートツールを使用しましたが、アイキャッチ画像が移行されなかったため、Export media with selected contentプラグインを使用して移行しました。

ブログ開発

WordPress設定

使用しているプラグインは以下となってます。必須プラグインはAPIのプラグインと、画像プラグインのみです。他は任意ですが、バックアップとセキュリティ関連のプラグインは入れることを推奨します。

  • 記事作成
    • AddQuicktag
    • Advanced Custom Fields
    • Classic Editor
    • Search Regex
  • バックアップ
    • BackWPup
  • 移行
    • WordPress インポートツール
  • 画像
    • EWWW Image Optimizer
    • WP Offload Media Lite
  • セキュリティ
    • reCaptcha by BestWebSoft
    • SiteGuard WP Plugin
    • Wordfence Assistant
    • Wordfence Security
    • WP BASIC Auth
  • API
    • WPGraphQL
    • WPGraphQL for ACF
    • WPGraphQL JWT Authentication
    • WPGraphQL Offset Pagination
    • WPGraphQL Smart Cache

WordPress純正のREST APIだと制約が多いため、WPGraphQLを使用しています。

JWT認証しないとAPIを叩けないようにWPGraphQL JWT Authenticationを使用していますが、tokenを取得するエンドポイントだけは認証無しで叩けるようにするため、「Allow login mutation to be public when the endpoint is fully restricted」の公式ドキュメントを参考に設定を追加する必要があります。

また、SiteGuard WP PluginのreCaptcha設定がWPGraphQL JWT Authenticationと競合して認証が通らないため、reCaptchaはreCaptcha by BestWebSoftを有効化しています。これでJWT認証ではreCaptchaが無効化され、管理画面に入るためのログイン画面ではreCaptchaが有効化されます。

画像はEWWW Image Optimizerで圧縮とwebp変換をしており、WP Offload Media LiteでS3にアップロードするように設定しています。

画像サイズもいくつか生成されるように、Wordpress側で設定を行っており、記事作成時に画像を挿入したら、デバイスサイズごとに適切な画像サイズを配信できるようにするHTMLタグが挿入されるように設定しています。併せて、WPGraphQLで画像のカスタムサイズを取得できるようにカスタムフィールドを追加する設定をしています。

NextJS開発

スタイルはTailwind CSSを使用し、ルーティングはApp Routerを使用しています。App RouterでSSRを行う場合、cache-controlを設定できなかったため、検索ページなどの一部ページを除き、ISRで実装してます。

実装は、WPGraphQLの公式ドキュメントNextJSの公式ドキュメントを参考にAPIリクエストを行って、記事データを取得して表示しています。JWT認証と同様に、ページネーションの実装など、一部機能についてはWPGraphQL Offset Pagination等の拡張プラグインを使用しています。

SEO対策に伴う設定では、以下のような実装を行っています。

Google Search Consoleはドメイン単位で設定しているので、コード側で特に設定はしていません。

上記でクローラー向けのテクニカルSEOは最低限満たされていると思います。実際にインデックスされていますし、ワードによっては検索順位1位なので大丈夫でしょう。

ホスティング設定・デプロイ

公式ドキュメント「Deploying to Vercel」を参考にデプロイします。

WordPressで記事更新後は、ISRとSSRでは比較的リアルタイムに更新されるので、SSGの場合のようにWebhookでVercelに通知してビルドをフックするような設定は必要ありません。

デプロイ後はDNSを切り替えて移行完了です。

lighthouseの実行結果

移行前

asis-lighthouse

移行後

tobe-lighthouse

まとめ

運用コストとパフォーマンスが大幅に改善されて満足です。

シェアする

プロフィール画像

Masaki Nishi

サンフランシスコ・シリコンバレーでのインターンや外資SIer、メガベンチャーでのエンジニアを経験後、現在はAmazon Web Servicesに勤務。

詳細プロフィール