はじめに

コネクトデータのウェブサイトは WordPress を利用したウェブサイトで、配信に Google Cloud CDN(Cloud CDN)を利用しています。この記事では、 WordPress と Cloud CDN を連携させる手順について解説します。

WordPress と CDN

WordPress は多くの企業でも利用されているコンテンツ管理システムです。コネクトデータも、 WordPress を利用しています。

WordPress はお手軽にサイトを構築できるのですが、パフォーマンスがそこまで優れているわけでもないので、特に貧弱な環境で動作させると悲惨なことになります。 CDN はこのような貧弱な配信環境においても、コンテンツをインターネット配信できるような仕組みで、各種クラウドサービスでも提供されています。ウェブサーバーのリソースを強化してパフォーマンスをあげる方法もないことはないのですが、費用対効果の面でおすすめできません。

CDN を使うメリットとして、コンテンツ配信の高速化以外にも、フロントエンドのセキュリティ対策といった副次的な効果も得ることができます。デメリットとしては、単純なサーバークライアント環境ではないため、設定が複雑になる、キャッシュされたコンテンツの削除に手間暇が発生する、特に動画などの大容量通信を行う場合は高額な費用が発生しうる1、などが挙げられます。

コネクトデータの環境

コネクトデータは Google Cloud Platform(GCP)の Always Free の無料枠を利用するために、ウェブサーバーは Google Compute Engine(GCE)の f1-micro のインスタンスを利用しています。 f1-micro は仮想 CPU が 1 コア、メモリーが 0.6 GB しかないので、かなり貧弱です。このようなリソース制約の中、 Cloud CDN を利用することで、ウェブサイト配信の負荷を軽減しています。

Cloud CDN を使った WordPress の配信

SSL 証明書の取得

このページの URL を見ればわかる通り、通信内容の保護のために HTTPS 通信でのコンテンツ配信を行なっています。 CDN サービスによっては、 HTTPS 通信に必要な共有 SSL 証明書も用意してくれますが、 Cloud CDN は自分で SSL 証明書を用意しなければなりません。

SSL 証明書にはドメイン認証(DV)、企業認証(OV)、 EV 認証(EV)の 3 種類があり、後ろに行くほどウェブサイト運用組織の証明になるのですが、コストを抑えるためにも、本ウェブサイトでは DV 認証を採用しています。

DV 証明書には、無料で利用できる Let’s Encrypt を採用しています。初期設定はドキュメントの通り certbot を利用すればよいので説明は割愛します。 SSL 証明書には期限があるため、期限に伴う更新の話は後述します。

Cloud CDN の設定

Cloud CDN を利用したコンテンツの配信は、 Cloud CDN の概要を参照してください。以下のような手順になります。

  1. WordPress の設定を行う。
    • 詳細は後述します。
  2. ロードバランサーのバックエンドサービスとしてインスタンスグループを作成する。
  3. ロードバランサーを作成する。
    • 今回は f1-micro インスタンス 1 台構成なので、実際にロードバランサーとして働くわけではなく、概要の図にある通り、 Cloud CDN との情報のやりとりに利用されます。
  4. Cloud CDN を有効にする。
    • ロードバランサーの設定上、 Cloud CDN を有効にするかのチェックボックスがあるため、それにチェックを入れます。

WordPress の設定

Cloud CDN を利用して WordPress を配信する場合の注意点は 2 つです。

一つ目は、 SSL を Cloud CDN にオフロードするため、ロードバランサーとウェブサーバーは HTTP 通信になります。 WordPress の is_ssl 関数のリファレンスにもある通り、ロードバランサーが HTTP_X_FORWARDED_PROTO ヘッダーを設定するため、これを元に HTTPS 通信であることを判定しなければなりません。

二つ目は、 Cloud CDN でキャッシュを有効にするために、 Cache-Control ヘッダーに public が含まれており、かつ Cache-Control ヘッダーの max-ages-maxage が設定されているか Expires ヘッダーが設定されてなければなりません[^control-cache]。また、管理ページや記事のプレビューがキャッシュされると都合がよくないので、これらについてはキャッシュが無効になるようにしなければなりません。管理ページについては /wp-login.php/wp-admin/ 以下などの特定のパスについて指定し、プレビューについては preview=true がリクエストパラメーターに含まれているかをチェックすればよいです。

キャッシュの有効・無効の設定については「キャッシュの詳細 | Cloud CDN のドキュメント」に詳しく書かれています。基本的には上記のように Cache-Control ヘッダーを設定すればよいのですが、それとは別にキャッシュを無効にする指定もあることに注意しなければなりません。コネクトデータを設定する際にハマったこととして、特定のプラグインがページにかかわらず Set-Cookie ヘッダーを設定するというようなことを行なっていました。現在はそのプラグインは無効化しています。

SSL 証明書の自動更新

Let’s Encrypt の SSL 証明書は、有効期間が 3 ヶ月のみで、継続的にサイトコンテンツを HTTPS 通信で配信するためには、証明書の更新が必要になります。幸い GCP で SSL 証明書を取得してロードバランサーに設定することは、全てシェルスクリプトで実行できます。

サービスアカウントの権限

GCE のインスタンスには、サービスアカウントを設定することができます。そのサービスアカウントの権限を設定することで、 GCE インスタンスから GCP の操作を行えるようになります。 SSL 証明書の更新に必要な権限は、以下の 2 つです。

  • Compute ネットワーク管理者
  • Compute セキュリティ管理者

スクリプト

SSL 証明書を更新して、 Cloud CDN に適用するまでのコマンドリストは、以下のようになります。

  1. certbot を利用して SSL 証明書を更新します。ここでは Apache を利用しているものとして、証明書の更新前・更新後に Apache を停止・起動するフック処理を記述しています。
    certbot renew \
      --pre-hook "systemctl stop httpd" \
      --post-hook "systemctl start httpd"
    
  2. 更新した SSL 証明書を、 GCP 上に作成します。 SSL 証明書の名前はユニークになるようにします。ドメインは自分のドメインに変更してください。
    gcloud compute ssl-certificates create <証明書名> \
      --certificate /etc/letsencrypt/live/example.com/cert.pem \
      --private-key /etc/letsencrypt/live/example.com/privkey.pem
    

    なお、作成できる SSL 証明書の数には制限があるため(デフォルトでは 10 個)、継続的に動かすためには、適宜削除しなければなりません。削除のコマンドは以下の通りです。

    gcloud compute ssl-sertificates delete <証明書名>
    
  3. ロードバランサーに紐づけられた HTTP プロキシーを取得し、その HTTP プロキシーの SSL 証明書を作成した証明書に設定します。プロキシーの取得には jq コマンドを利用しているため、必要であればインストールします。
    PROXY_NAME=$(
      gcloud compute target-https-proxies list \
        --filter='urlMap=<ロードバランサー名>' \
        --format=json \
      | jq -r .[].name
    )
    gcloud compute target-https-proxies update $PROXY_NAME \
      --ssl-certificates <証明書名>
    

まとめ

Cloud CDN を利用して WordPress のコンテンツを配信する方法について説明しました。

CDN はデメリットもありますが、セキュリティや安定配信といったメリットも大きいため、 CDN を利用していないサイトは、利用を検討してみてもよいかもしれませんね。


  1. Cloud CDN の場合、動画を含まない通常のウェブサイトであれば、 PV 数にもよりますが、ほとんどのウェブサイトで数十円〜数百円程度で収まると思います。