PHP 8.3 の FPM で opcache.validate_timestamps=0 にしたらデプロイが反映されない件

这篇内容暂时只有 日本語 版本,以下是原文。

PHP 8.3 + nginx + php-fpm の構成で、opcache.validate_timestamps=0 を入れたままデプロイして「古いコードが動き続ける」状態を一度作った。構成は php:8.3-fpm-alpine のコンテナ、opcache.memory_consumption=256、max_accelerated_files=20000、opcache.enable_cli=0。アプリは 400 ファイル程度、デプロイは rsync でコードを差し替え、FPM は再起動しない、という運用だった。現象は、デプロイ直後だけ古いクラスが動き、FPM を reload すると直る。つまりキャッシュ無効化のタイミングを運用側で握れていなかった。

まずやったのは、反映を「ファイル更新時刻」ではなく「明示的なキャッシュクリア」に寄せること。deploy の最後に以下を実行するようにした。

# rsync 完了後、FPM のマスタープロセスへ SIGUSR2 を送る
kill -USR2 $(cat /usr/local/php/var/run/php-fpm.pid)

SIGUSR2 は graceful reload で、ワーカーが順次入れ替わる。ダウンタイムなしで opcache も作り直される。ただし、リクエスト処理中のワーカーは処理を終えるまで残るので、長いリクエストがあると新旧混在の時間ができる。そこは許容するか、deploy を Blue/Green に寄せるかの判断になる。

もう一つの選択肢は opcache.file_cache を有効にして、キャッシュを共有ボリュームに置く方法。ただし、こちらは再起動時のコールドスタートを短くする効果が主で、コード差し替えの即時反映とは別問題だった。実際に試すと、file_cache だけでは古いエントリが残り、validate_timestamps=0 のままだと更新されない。

なぜ validate_timestamps=0 にするかというと、毎リクエストの stat 呼び出しを省けるから。特に NFS や overlayfs 上では stat のコストが効いてくる。ただし、その代償として「コード更新の検知」を完全に捨てることになる。ここを理解せずに入れると、今回のように「reload するまで直らない」状態になる。

効かない条件もはっきりしている。まず、PHP を CLI で動かすバッチは opcache.enable_cli=0 なら無関係。FPM 配下でも、opcache.restrict_api を設定していると、Web からの opcache_reset() は 403 になる。また、preload を使っている場合は、preload されたファイルは reload では消えず、FPM の再起動が必要になる。ここは別の記事の preload の話とも絡む。

最後に、自分の環境では「reload を deploy に組み込む」で落ち着いたが、reload のタイミングと rsync の完了が競合する可能性は残っている。rsync の途中で reload が走ると、半端な状態のファイルを読むことがある。rsync の –delay-updates を使うか、reload 前にマーカーファイルの存在を確認するなどの工夫が必要だった。

同じように validate_timestamps=0 で運用している人は、デプロイ時のキャッシュ無効化をどう担保している? rsync の完了検知や、reload の競合をどう避けているか知りたい。

0
赞同度 100% · 0 条评论 · 4 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。