現象
表題の通りですが、現象を噛み砕いて説明します。
筆者は Symlink ベースのデプロイツール ( Ansistrano ) を使用してデプロイをしています。
※ Symlink ベースのデプロイツールは Capistrano や、Deployer なども含まれます。
デプロイが完了したサーバ側のディレクトリ構造は以下のようになります。
.
├── current -> ./releases/20160530055905
├── releases
│ ├── 20160530055905
│ ├── 20160530055658
│ └── 20160530050508
└── shared
デプロイされると、 current
ディレクトリはシンボリックリンクとなっており、 releases
以下に、デプロイされた最新のアプリケーションコードを指します。
ウェブサーバやアプリケーションサーバの設定ではドキュメントルートに current
ディレクトリを指してます。
この時、 OPcache が有効で且つ PHP-FPM を使用した Web サーバーではコードが反映されないという現象が発生しました。
原因
PHP5.5 以降のコードキャッシュは APC から OPcache を使うようになりました。
APC は、ファイルをキャッシュするために、 inode を用います。 inode はシンボリックを解決し、参照するように働きます。しかし OpCache は inode ベースではありません。 OpCache は、キャッシュする前に絶対ファイルパスを見つけるためにシンボリックリンクを評価するのでデプロイする前のパスを覚えています。
つまり、 PHP がデプロイされた最新のアプリケーションコードへシンボリックリンクを参照せず、その一つ前の世代のアプリケーションコードを参照している ということです。
解決方法
- Web サーバーのドキュメントルートの設定を直接書き換える
- OPcache をリセットする
- PHP-FPM を再起動する
ウェブサーバに nginx を利用している場合の解決方法
nginx は、シンボリックリンクを解決できるように設定することが可能です。
nginx の構成では、おそらく以下のような設定となってます。
以下のような設定の場合、 $document_root
は current
ディレクトリのシンボリックリンクを解決せずに参照します。
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
以下のように $document_root
から $realpath_root
を指定するように設定を変更します。
そうすることで current
ディレクトリのシンボリックリンクを解決し、 release
ディレクトリ配下のアプリケーションコードの絶対パスを参照できるようになります。
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;