WordPress×Cloudflareキャッシュ戦略 — K8s運用で工夫したこと

GKE環境をPreemptible VM(24時間で強制シャットダウンされるノード)を使ってコスト削減しながら運用しています。ただ、その上で動かしているWordPressのダウンタイムやパフォーマンスが気になっていて、Cloudflareのキャッシュでどうにかできないかと考えていました。この記事では、Cloudflareを使いながらGKE上のWordPressをマルチインスタンスで提供するにあたって試行錯誤した記録をまとめます。

APO(月$5)を使わなかった理由

CloudflareにはWordPress向けの「Automatic Platform Optimization(APO)」があります。月$5でWorkers KVベースのキャッシュ配信が手に入りますが、「Super Page Cache for Cloudflare」という無料プラグインで同等のことができそうだったので、そちらを試しました。

APOはブラックボックス寄りで、トラブル時に自分で切り分けられる範囲が狭い。Super Page Cacheは動作がシンプルで、Cloudflare Rulesを1つ消費してキャッシュ制御をかけ、ログイン時は swcfpc=1 でキャッシュを回避します。PHPのソースも読めます。

$5とはいえランニングコストは削減したいですし、無料で同等のことができるならそちらを選びます。

cf-cache-status: DYNAMICとの戦い

プラグインを入れたものの、Cloudflare側のレスポンスヘッダが cf-cache-status: DYNAMIC のままでキャッシュが効かない問題に当たりました。HIT にするにはNginx側でレスポンスヘッダを適切に返す必要があります。

headers-more-nginx-module の出番です。Alpine版Nginxでapkインストールを試みましたが動かず、結局モジュールを自前ビルドしました。Dockerfileの ENABLE_MODULES 変数で組み込めるので運用上は問題ありませんが、冷静に考えるとWordPressのためにやる作業ではないです。

GKEでの共有ストレージ問題とgcsfuseの妥協

K8s上でWordPressをマルチインスタンス構成にすると、プラグインのキャッシュディレクトリをインスタンス間で共有する必要が出てきます。

GKEは標準で ReadWriteMany を提供していません。Filestoreなら解決しますが、当時は最低1TBからのプロビジョニングで、個人WordPressにはあまりにもコストが合いません。

結局、Cloud Storage FUSE(gcsfuse)でGCSバケットをファイルシステムとしてマウントするアプローチを取りました。Podのlifecycle hooksでマウント・アンマウントを制御し、securityContextSYS_ADMIN capabilityを付与する必要があります。

SYS_ADMIN をコンテナに与えるのはセキュリティ的に明らかな妥協です。K8sのベストプラクティスとは正反対。それでもFilestoreの月額コストと天秤にかけて、個人運用なら許容できると判断しました。業務システムでは絶対やらない選択です。

振り返り: SSG移行への伏線

一連の作業を通じて感じたのは、WordPressをK8sで運用するのは本来やらなくていい苦労が多すぎるということでした。

Nginxモジュールの自前ビルド、privileged container、lifecycle hooksでのマウント制御。やっていることの本質は「ブログの表示を速くしたい」だけです。

この経験が、後のHugo(SSG)移行の決め手になりました。静的サイトジェネレーターなら、HTMLをCDNに置くだけ。Nginxモジュールのビルドも、gcsfuseも要りません。

K8s上のWordPress運用で得た知見は引き出しにはなりましたが、個人サイトとしてはやりすぎでした。やり続ける覚悟がなかった、というのが正直なところです。

Related Posts