Cloud Run でサーバーレス WordPress を構築する
2023年は「Cloud Run を触って覚える」をテーマとした ひとりアドベントカレンダー を開催しており、Cloud Run のさまざまな機能や Cloud Run でよく使う構成などをご紹介しています。
21日目は Cloud Run を使って WordPress サイトを構築する方法についてご紹介します。
Cloud Run の概要は「gihyo.jp」で解説していますので、こちらもぜひご覧ください。
WordPress on Cloud Run
WordPress は CMS の定番の一つと言える、主にブログやニュースサイトなどのコンテンツ管理のための無料で使えるオープンソースソフトウェアです。
WordPress はコンテナ イメージが Docker 公式で提供されており、コンテナで運用することが比較的簡単です。コンテナといえば Cloud Run と言うことで、WordPress を Cloud Run サービスとして構築するためのアーキテクチャ、手順、考慮した方が良いポイントなどをまとめてみました。
WordPress をコンテナとして管理するメリット
まず Cloud Run に構築する上ではコンテナ化することが必要ですが、WordPress をコンテナとして管理するメリットについて考えたいと思います。
WordPress はコンテナ アプリケーションが普及する前はソースコードを全てダウンロードし、自由にカスタマイズし、丸ごとデプロイするような運用多かったと思います。カスタマイズのために手を加えた箇所によっては WordPress のバージョンアップをするときにカスタム コードを退避しておかなければならず、複雑な運用フローになりがちでした。
WordPress のコンテナ化を考える上で「WordPress のソースコード」と「カスタマイズとして加えるソースコード」を分離することが求められます。これから WordPress サイトを作る場合はもちろんですが、既存の WordPress サイトをコンテナ化する場合についても管理しやすい形で開発を始めることができます。
もちろん、コンテナ化する上での一般的なメリット (例: 環境依存を無くす、冪等に動作、ポータビリティ) も得られます。Cloud Run を採用するかどうかに関わらず、コンテナとして開発・運用することをおすすめします。
構築する
この記事では、次のようなアーキテクチャを構築します。
アーキテクチャ
WordPress の実行環境として Cloud Run サービス、データベースとして Cloud SQL (MySQL) を使います。Cloud Run サービスから Cloud SQL に対してはプライベート アクセスをダイレクト VPC 下り (外向き) 経由で行います。
Cloud Run はステートレスなため、アップロードしたメディアファイルの永続化が必要です。Cloud Storage FUSE を使ってメディアファイルの格納先 (/wp-content/uploads
) を Cloud Storage バケットとしてマウントし、メディアファイルがアップロードされたら Cloud Storage バケットに格納されるようにします。
グローバル外部アプリケーション ロードバランサを Cloud Run サービスの手前に置くことで CDN を使えるようにし、かつ 1 つのドメインで Cloud Run サービスと Cloud Storage バケットをパスでルーティングできるようにします。カスタム ドメインも設定することができます。
Cloud SQL インスタンスを作成する
Cloud SQL インスタンスを作成します。ここでは gcloud
コマンドで構築します。WordPress の標準 DB は MySQL なので MySQL 8.0 で構築します。プライベートアクセスのみを許可し、コストを最小限に抑えた設定をしています。その他のオプションについては こちら も参照してください。
作業は Cloud Shell 上で行いますので、まずは Cloud Shell を開きます。
Cloud Shell の起動
次のコマンドを実行します。
gcloud sql instances create wordpress-db \
--database-version=MYSQL_8_0 \
--cpu=1 \
--memory=3840MB \
--region=asia-northeast1 \
--network default \
--no-assign-ip \
--storage-type SSD \
--storage-size 10GB \
--storage-auto-increase
Cloud SQL インスタンスの作成が完了するまで数分かかります。
作成が完了したら、WordPress 用のユーザーとパスワードを作成します(詳しくはこちら)。
gcloud sql users create wordpress-user \
--host=% \
--instance=wordpress-db \
--password=<PASSWORD>
WordPress 用のデータベースも作成しておきます(詳しくはこちら)。
gcloud sql databases create wordpress \
--instance=wordpress-db
インスタンス名、プライベート IP、データベース名、ユーザー名、パスワードは Cloud Run サービスの環境変数に設定するので記録しておきます。
Cloud Storage バケットを作成する
次のコマンドで Cloud Storage バケットを作成します。BUCKET_NAME
は適宜修正してください。
gcloud storage buckets create gs://<BUCKET_NAME> \
--location asia-northeast1
/wp-content/uploads
配下にアップロードされる公開用のコンテンツのためのバケットなので、バケットに対して誰でも閲覧できる権限を付与します。
gcloud storage buckets add-iam-policy-binding gs://<BUCKET_NAME> \
--member=allUsers \
--role=roles/storage.objectViewer
Cloud Run サービスを作成する
まず Cloud Run サービスに使用するコンテナ イメージを作るため、Dockerfile を用意します。とはいえカスタマイズをしない場合は追加すべきファイルもない(wp-config.php
を用意していない場合は自動作成されるようにコンテナ イメージが作られている)ので、設定することは何もありません。
FROM wordpress:6.4.2-php8.3
次に Cloud Run サービスに設定するサービス アカウントを作成しておきます。Cloud Storage をマウントするので Cloud Storage の読み書きが可能な権限を付与します。
gcloud iam service-accounts create fs-identity
gcloud projects add-iam-policy-binding <PROJECT_ID> \
--member "serviceAccount:fs-identity@<PROJECT_ID>.iam.gserviceaccount.com" \
--role "roles/storage.objectAdmin"
次に Cloud Run サービスをデプロイします。Cloud Run サービスは次の機能を使います。
- Cloud SQL インスタンスにプライベート接続するためにダイレクト VPC 下り (外向き) を使用
- Cloud Storage バケットをマウントするために Cloud Storage FUSE を使用
ここではコマンドでデプロイします。
gcloud alpha run deploy wordpress \
--region asia-northeast1 \
--source . \
--execution-environment gen2 \
--port 80 \
--service-account fs-identity \
--allow-unauthenticated \
--add-cloudsql-instances <PROJECT_ID>:asia-northeast1:<INSTANCE_NAME> \
--network default \
--subnet default \
--vpc-egress private-ranges-only \
--add-volume=name=gcs,type=cloud-storage,bucket=<BUCKET_NAME> \
--add-volume-mount=volume=gcs,mount-path=/var/www/html/wp-content/uploads \
--update-env-vars WORDPRESS_DB_HOST=<PRIVATE_IP> \
--update-env-vars WORDPRESS_DB_USER=<DB_USER> \
--update-env-vars WORDPRESS_DB_PASSWORD=<DB_PASSWORD> \
--update-env-vars WORDPRESS_DB_NAME=wordpress
デプロイ後、エンドポイントにアクセスすると WordPress の初期設定が開始できます。
WordPress の初期設定画面
初期設定を諸々済ませると(WordPress の標準の機能なので割愛します)、WordPress の管理画面を開くことができます。
WordPress の初期設定画面
メディア アップロード先が正しく Cloud Storage バケットに向いているか確認しましょう。適当な画像ファイルをいくつかアップロードしてみます。
メディア アップロード
Cloud Storage のコンソールで、バケット内にオリジナル画像ファイルと WordPress の機能でリサイズされた画像ファイルが格納されていることが確認できます。
バケット内のファイルの確認
グローバル外部アプリケーション ロードバランサ (CDN) を作成する
ここまでの手順で WordPress を最低限動かすことはできました。しかしこのままでは全てのリクエストが Cloud Run を経由してしまうため、CDN によるキャッシュを挟んだ方がコスト的にもパフォーマンス的にも良いです。
アプリケーション ロードバランサを使用すると、バックエンドとして Cloud Run サービスと Cloud Storage バケットを 1 つのドメインで扱うことができ(パスで振り先を変える)、その上で Cloud Run サービス、Cloud Storage バケットそれぞれにキャッシュの設定を一律で行うことができます。
Cloud Run では Cloud Run Integration として、グローバル外部アプリケーション ロードバランサとカスタム ドメインを簡単に設定する機能を提供しています(プレビュー)。まずはこの機能を使ってサクッと構築し、Cloud Storage バケットの設定を少しカスタマイズして使用します。
ドメインは取得している前提で進めます。Cloud Run サービスのコンソールで [統合] のタブを開き、[インテグレーションを追加] をクリックします。
[カスタム ドメイン - Google Cloud Load Balancing] を選びます。
カスタム ドメインを指定します。
[SUBMIT] をクリックすると、これらのリソースを自動的に作成していきます。アプリケーション ロードバランサ用の IP アドレスが作成されるので、カスタム ドメインの DNS の A レコードに登録しておきます。
ここまでできたらアプリケーション ロードバランサのコンソールを開き、Cloud Storage バケットの設定を追加します。
プロトコルが HTTPS
のロードバランサのオプションの [編集] をクリックします。
ロードバランサの編集
[バックエンドの構成] で、バックエンド バケットを追加します。[Cloud CDN を有効にする] にチェックを入れることで、CDN によるキャッシュ機能を使うことができます。キャッシュモードはここではデフォルトの設定のままにします。
バックエンド バケットの編集
[ルーティング ルール] を開き、[モード] を [詳細なホストとパスのルール] に変更します。ホストとパスのルールを追加し、ホストは *
にします。
ルーティング ルールの編集
パスマッチャーは次のような定義にします。
defaultService: projects/<PROJECT_ID>/global/backendServices/wordpress
name: path-matcher-1
pathRules:
- paths:
- /wp-content/uploads/*
service: projects/<PROJECT_ID>/global/backendBuckets/wordpress-uploads
routeAction:
urlRewrite:
pathPrefixRewrite: /
/wp-content/uploads/*
にリクエストが来たら Cloud Storage バケットに /wp-content/uploads/
までのプレフィックスなしでルーティングするように設定しています。マウントしたバケットのルートからファイルが作られていますが、そのままだとバケット内の /wp-content/uploads/
を参照してしまうため、/
にパスのプレフィックスを上書きすることで解決しています。
画像ファイルを直接見てみると、キャッシュ コントロール ヘッダーが付与されていることが確認できます。
キャッシュ コントロール ヘッダーの確認
テーマ・プラグインの管理
テーマ・プラグインはそれぞれ /wp-content/themes
と /wp-content/plugins
に保存されるので、これらに対応する Cloud Storage バケットを用意し、Cloud Storage FUSE でマウントすることで永続化することができます。
gcloud run deploy
コマンドに追加する場合は、次のようなオプションを加えます。
--add-volume=name=themes,type=cloud-storage,bucket=<テーマ用のバケット> \
--add-volume-mount=volume=themes,mount-path=/var/www/html/wp-content/themes \
--add-volume=name=plugins,type=cloud-storage,bucket=<プラグイン用のバケット> \
--add-volume-mount=volume=plugins,mount-path=/var/www/html/wp-content/plugins \
この構成の場合、テーマやプラグインは Cloud Run で立ち上げた WordPress 上で行います。
もう1つの方法として、ローカルでインストールしておく方法もあります。次のような手順になります。
- ローカルで WordPress を Docker Compose などで立ち上げる (
/wp-content/themes
と/wp-content/plugins
を特定のディレクトリにマウントしておく) - ローカルの WordPress を開き、テーマやプラグインをインストールする
- 2 を含めたコンテナ イメージを作成する or Cloud Storage バケットに Sync する
特に 3
の部分はテーマやプラグインの規模によって、どちらの方が良いか選択肢が変わります(コンテナ イメージに含める方が速く動作するがコンテナ イメージのサイズが大きくなってしまう)。
WordPress on Cloud Run で考えておきたいこと
WordPress on Cloud Run を構築するにあたって Cloud Run の特性を理解した上での利用 をおすすめします。ハマれば効率的かつ最小限のコストで運用できる可能性がありますが、銀の弾丸というわけではありません。
アクセス頻度
Cloud Run の最小インスタンスの設定を 0
にすることで、リクエストが無い時間帯はコンテナ インスタンスの費用を削減できる期待が持てます。しかし管理画面にそこそこアクセス頻度がある場合はセッションが切れてしまうため、頻繁に再ログインが求められるようになってしまいます。
頻繁に出てくる再ログイン画面
これを避けるには最小インスタンスを 1
以上に上げる (コンテナ インスタンスが複数になる場合はセッション アフィニティも有効にする) ことで改善する可能性はありますが 0
にできる長所も失われてしまいます。「コンテンツを編集する頻度があまり高くない CMS をコスト効率良く運用したい」といったユースケースが最もマッチすると思います。
複数ユーザー利用時の同時書き込み
Cloud Storage FUSE を使うと簡単に /wp-content
配下 (つまりユーザーの操作でファイルが作られるディレクトリ) をマウントできますが、POSIX に準拠していません。そのため複数ユーザーから同じファイルへの書き込みがあった場合は最後の書き込みが優先されるなどの制限事項があります。詳しくは こちら を確認してください。POSIX に準拠したネットワーク ファイル システムを使いたい場合は Filestore を採用するようにしましょう。
この構成は「同時編集などは行わず、1ユーザーごとに編集するコンテンツが分かれている」といったユースケースがマッチすると思います。
テーマ・プラグインへのアクセス頻度
Cloud Storage FUSE は読み書きによって費用が発生します。そのためテーマやプラグインを多用するようなページを作る場合、アクセスごとに読み書きが発生します。通常はキャッシュを効かせれば PV に比例して増える形にはなりませんが、キャッシュヒットが低いような使い方をする場合はコストが高くなってしまう可能性があります。また Cloud Storage FUSE は NFS などと比べると I/O 性能は出ないので、オリジン アクセス時のレイテンシは比較的長くなると思います。よりパフォーマンスを上げたい場合は Filestore を採用した方が良いかもしれません。
したがって「1度作成したページはキャッシュ期間を長くする」といったようなユースケースがコスト効率的にはマッチすると思います。
まとめ
WordPress on Cloud Run を通して、WordPress を使った CMS の開発や運用でよくある課題に使えそうなソリューションをいくつか紹介しました。他にもアイデアがありましたら、ぜひコメントでお寄せください!
Discussion