オープンソースこねこね

Webプログラミングなどについてあれこれ。

PHP5.5+OPcacheでシンボリックリンクでデプロイするとキャッシュが消えない

という問題にぶち当たりました。 どういうことかを話す前にPHPアプリケーションの個人的なデプロイ構成について説明します。 要はCapistranoのデプロイと同様なのですが、デプロイ先は以下のようなディレクトリ構成になっています。

.
|-- current -> /path/to/releases/20141011000001
|-- releases
|   |-- 20141011000000
|   `-- 20141011000001

currentアプリケーションサーバ(apachephp-fpm)から参照される箇所で、releases配下の最新のアプリケーションへのシンボリックリンクになっている。新しくアプリをデプロイするとreleases配下に20141011000002のようなタイムスタンプで新しくディレクトリが作成され、そこにアプリケーションのコードが配置される。その後currentシンボリックリンクが新しいアプリケーションへと切り替えられる。これによってダウンタイムなしでPHPアプリのコードをデプロイできます。

OPcacheのキャッシュ

ところが、PHP5.5+php-fpm+OPcacheという環境でこの構成のデプロイをためしたところ、実行中のアプリケーションにデプロイコードが反映されないという問題が生じました。 (※OPcacheはPHP5.5の新しいコードキャッシュシステムでPHP5.4以前のAPCに替わるものです)

調査していたら以下の解説記事を発見しました。

OpCache and symlink-based deployments

要約すると

  • OPcacheはAPCとはファイルのキャッシュの仕方が違う。
  • OPcacheはシンボリックを解決して、実ファイルパスの状態でキャッシュする。
  • よってシンボリックリンクを更新しても、実ファイルパスのキャッシュが保持されてしまう。

というわけだそうです。(このへんをちゃんとPHPのソース読んで、自分で裏付けとれるといいと思うのですが。。。技術力の低さが露呈しますね。。。(ー_ー;))

上記の解説記事では対応方法もいくつか提示されていて

  • webサーバの設定を直接書き換える(シンボリックリンクの代わりにweb(AP)サーバが参照するパスを直接書き換える)
  • OPcacheをリセットする
  • php-fpmを再起動する(多少のダウンタイムあり)

ansibleやpuppetを使っていればデプロイでwebサーバの設定を書き換えるのは簡単かも、と言及されています。 ただ、シンボリックリンクを使い続けたいならキャッシュをリセットするか、php-fpmを再起動する。 OPcacheはキャッシュをリセットする関数opcache_resetをもっているので、 これを使用した対応方法も(Laravelを使った例で)示されています。

さて、自分はどーしようか悩み中。 デプロイタスクがちょっとだけど複雑になるのと、コードキャッシュはアプリケーションより一段下の技術レイヤと考えているので、 それのリセット処理をアプリケーションのコードに直接入れるような対応はやりたくない。

この際、最近盛り上がってきているdockerでBlue-Green Deployment的なことをやってしまうのもアリかなと考えています。