Redis Object Cache有効時にPHP-FPMがクラッシュして503エラーが頻発する原因と対処法

Redis Object Cache有効時にPHP-FPMがクラッシュして503エラーが頻発する原因と対処法

Redis Object Cache有効時にPHP-FPMがクラッシュして503エラーが頻発する原因と対処法

Redis Object Cacheプラグインのドロップインを有効にしたWordPressサイトで、PHP-FPMのワーカープロセスがSIGSEGV(セグメンテーション違反)を起こし断続的に503エラーが発生する現象は、PHP本体のコア(Zend Engine)領域におけるメモリ破壊が根本原因だ。まずはオブジェクトキャッシュを無効化してサイトを安定させ、PHPのビルドバージョンやRedis拡張の状態を確認するのが最初の一手になる。

なぜRedis Object Cacheを使うとPHP-FPMが落ちるのか

なぜRedis Object Cacheを使うとPHP-FPMが落ちるのか

障害の直接的な引き金は、Zend Engine VM の命令ハンドラ内で発生するSIGSEGVシグナルだ。これはPHP本体のコア実行エンジンが、本来アクセスできないメモリ領域に誤って触れたときにOSがプロセスを強制終了させる。エラーが発生する場所は拡張機能のコードではなく、PHP 8.5.9が持つVMの基本命令を処理する部分であるため、PhpRedisのような特定の拡張機能が直接のバグを抱えている可能性は低い。

クラッシュはリクエスト処理の初期には起こらず、PHP-FPM のワーカーが起動してから700~1,600秒以上経過したタイミングで突如発生する。この時間差は、徐々に進行するヒープの破壊(メモリの二重解放、解放後の使用、境界外書き込みなど)を示唆している。リクエストの処理中にどこかでメモリが不正に操作され、その結果として破壊された領域を後続のVM命令が触れたときにSIGSEGVが起こる流れだ。

Redis Object Cacheのドロップインが有効な場合にのみ再現するのは、オブジェクトキャッシュが大量の一時データをPHPのメモリ空間に出し入れすることで、潜在的なメモリ破壊の発生確率を高めているからだと考えられる。また、キャッシュ操作自体の頻度やデータ構造の複雑さが、PHP 8.5.9固有のレアな境界条件を偶然踏み抜いている可能性もある。

クラッシュが発生した場合、PHP-FPM マスタープロセスは該当の子プロセスを再起動するが、その間は該当ワーカーが応答できないため、Webサーバー(nginxやApache)がバックエンドのFPMに接続できず、フロントエンドに503エラーが断続的に現れる。

実際のクラッシュ発生箇所は次のバックトレースで特定されており、coreで発生していることはほぼ間違いない。

0 ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_HANDLER (execute_data=…, opline=…)
at Zend/zend_vm_execute.h:6227
クラッシュ発生時
PHP-FPM 子プロセス
ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST_HANDLER 内
→ 解放済みメモリ領域にアクセス
→ SIGSEGV 発生 → プロセス強制終了
フロントエンド
Webサーバー(nginx / Apache)
バックエンドのFPMに接続不可
→ 503 Service Unavailable を返す

Redis Object Cacheを安全に無効化する手順

Redis Object Cacheを安全に無効化する手順

サイトの安定が最優先だ。根本原因の調査は時間がかかるため、まずはオブジェクトキャッシュのドロップインを無効化し、クラッシュが起きない状態に戻す。無効化してもフロントエンドの表示が著しく遅くなるわけではないケースが大半だが、ショートコードやDBクエリの多い動的ページでは表示速度が一時的に落ちる可能性があることは把握しておく。

管理画面にログインできる状態なら、以下の手順で無効化する。もし503エラーの影響で管理画面にアクセスできない場合は、FTPやSSH経由で直接ファイルを削除する手順に進む。

STEP 1 「設定」→「Redis」画面の「キャッシュを有効にする」をオフにする
STEP 2 「キャッシュをクリア」ボタンを押下してキャッシュを破棄
STEP 3 プラグイン一覧からRedis Object Cacheを「停止」する

プラグイン自体を停止するとドロップイン(wp-content/object-cache.php)も無効化される。プラグインを停止したあと、念のためFTPやSSHでwp-content/object-cache.phpが削除されていることを確認するほうが確実だ。このファイルが残っていると、プラグインが無効でもキャッシュ機構だけが動き続ける。

管理画面にアクセスできない場合は、以下のいずれかの方法でobject-cache.phpを直接削除する。

  • FTPクライアントでwp-content/object-cache.phpを削除(または拡張子を.bakに変更)
  • SSHでrm wp-content/object-cache.phpを実行
  • wp-cliが使えるならwp redis disableを実行

いずれの操作のあとも、サイトが正常に表示され503が発生しなくなったことを確認する。これで一時的な安定化は完了だ。

PHP 8.5.9でのSIGSEGVを根本的に調査する方法

PHP 8.5.9でのSIGSEGVを根本的に調査する方法

オブジェクトキャッシュを無効にしてサイトが安定したら、根本原因の調査に移る。クラッシュの位置がZend Engineコアである以上、PHPランタイムそのものの再ビルドや、利用しているPHPビルドの変更が最も確実な対策になる。

PHPのビルドを更新または再コンパイルする

この問題は特定のPleskビルド(plesk-php85-8.5.9-0redhat.9.260731.1229)で確認されている。Plesk環境であれば、PHPのコンポーネント更新を実行し、最新のマイナーパッチが適用されたビルドに差し替える。ビルド番号が異なるだけでZend Engineの挙動が変わる可能性があるからだ。Pleskの「ツールと設定」→「更新とアップグレード」から「PHP 8.5」のコンポーネント更新をチェックする。

ソースから自前でPHPをビルドしている場合は、configureオプションに--enable-debugを付けてビルドしたPHPをまずは検証環境にデプロイし、デバッグシンボル付きでクラッシュ時の詳細なコールスタックを取得する。商用サイトでデバッグビルドを使うとパフォーマンスが落ちるため、あくまでテスト用のステージング環境で行うこと。

PHPのセッションハンドラを確認する

Redis Object Cacheのドロップインが、意図せずセッションハンドラとしても機能しているケースがある。セッションデータがPHPの内部メモリ管理と衝突し、クラッシュを誘発している可能性も考えられるため、php.inisession.save_handlerfilesまたは意図した値になっているかを確認する。redisが指定されていた場合は、セッションとキャッシュの分離を検討する。

Redisサーバー側のチューニングと接続方式を再検討する

今回の環境ではRedisとの接続にUnixソケットが使われている。UnixソケットはTCP接続より高速だが、カーネルのソケットバッファやパーミッションの問題が間接的にPHP-FPMのメモリ状態を不安定にすることもありうる。いったんWP_REDIS_SCHEMEtcpに切り替えてしばらく様子を見るという切り分けも有効だ。

また、Redis側のmaxmemory-policynoevictionだと、メモリ上限に達したときに書き込みエラーが発生する。直接SIGSEGVを起こすわけではないが、エラー処理の過程でPHP内部の状態が破壊される可能性を完全には否定できない。allkeys-lruなど適切な追い出しポリシーを設定し、Redis自体のメモリ使用率にも注意を払う。

代替のオブジェクトキャッシュバックエンドを試す

Redis Object Cacheプラグインをどうしても使い続けたい場合は、PhpRedisとPredisのどちらのクライアントバックエンドを使ってもクラッシュが再現する以上、Redis以外のバックエンドをテストする価値がある。たとえばMemcachedをバックエンドとするObject Cache Proや、ファイルベースのキャッシュに一時的に切り替えて様子を見る。

とはいえ、Zend Engineコアでのクラッシュである以上、バックエンドを変えても根本解決には結びつかない可能性が高い。もしMemcachedやファイルキャッシュに切り替えても同じタイミングでクラッシュするなら、PHPビルドそのものの問題である確度がさらに上がる。

よくある質問

503エラーはRedis Object Cacheをやめれば必ず直るのか

PHP-FPMのワーカークラッシュが原因で起こっている503エラーであれば、ドロップインを無効化してクラッシュが発生しなくなれば解消する。ただしサーバーの同時接続数超過や他のPHP拡張の不具合など、別の原因で503が発生しているケースもあるため、エラーログを必ず確認する。

PHP 8.5系以外でも同じ現象は起こるのか

今回のクラッシュ位置(Zend Engineの特定の命令ハンドラ)がトリガーになっていることから、PHP 8.5の特定ビルドに限定される可能性が高い。ただしPHP 8.4や8.3の一部のビルドでも、メモリ安全性に関する潜在バグが残っていることはありうる。まずは該当のPHPマイナーバージョンにパッチが提供されていないか公式のバグトラッカーを確認する。

PHP-FPMのワーカー数を増やせば503は減るのか

クラッシュが断続的に発生しているだけなら、ワーカー数を一時的に増やすことで503の発生頻度を下げられる場合がある。ただし本質的には問題を隠しているだけであり、クラッシュが多発すると結局は全ワーカーが落ちてサイト全体が応答不能になるリスクは残る。根本対策にはならない点に注意が必要だ。

ドロップインを削除してもRedisは自動起動したままでいいのか

ドロップインを削除しただけではRedisサーバープロセスは稼働し続ける。メモリやCPUリソースをわずかに消費するが、WordPressが接続しなければ実害はほとんどない。必要に応じてRedis自体を停止しても構わないが、他のアプリケーションが同じRedisインスタンスを使っている場合は影響範囲を確認してから停止する。

PHPのバグ報告はどこに出せばいいのか

再現手順が明確で、デバッグシンボル付きのバックトレースが取得できているなら、PHPの公式バグトラッカー(bugs.php.net)に報告する。Plesk環境固有のビルドに問題がありそうな場合は、Pleskサポートにも並行してチケットを発行するとよい。報告時はPHPのビルド番号と再現手順をできるだけ具体的に記載する。

この記事のポイント

  • PHP 8.5.9の特定ビルドでRedis Object Cacheのドロップインが有効だと、Zend EngineコアでSIGSEGVが発生し503エラーが断続的に起こる
  • クラッシュはワーカー起動後700秒以上経過してから発生し、PhpRedisとPredisの両方で再現するため拡張機能固有のバグではない
  • 応急処置としてobject-cache.phpを削除しオブジェクトキャッシュを無効化すればサイトは安定する
  • PHPのビルド更新、セッションハンドラの確認、Redis接続方式の変更などでPHP本体の潜在バグを回避できる可能性がある
  • 根本解決にはPHPのバグ報告またはビルド差し替えが必要で、再発リスクを下げるにはステージング環境での検証が欠かせない
佐々木 太陽

・ Reddit、Stack Overflow、WordPress.org フォーラムを日々巡回し、現場の悩みを拾い上げて記事化 ・ WordPress、WooCommerce、Next.js などモダンWeb制作領域のトラブルシューティングが専門 ・ 「検索しても答えが見つからなかった」を一つでも減らすことが目標 ・ エラーメッセージから根本原因にたどり着く粘り強い調査が得意 ・ 初心者がつまずきやすい箇所を先回りで解決する記事作りを心がけている

メッセージを残す