NFSマウントした領域内でPHPのsession_startを実行すると異様に重い件 | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

PHPネタと思いきやLinuxサーバーネタで。

NFSマウントしたディレクトリ領域内でPHPのsession_startを実行すると30秒ほど待ち時間が発生するという状況が発生しました。

今回現象が出た環境はRedHat Enterprise Linux5で、ストレージ内のコンテンツ領域のディレクトリをマウントしています。

NFSの設定やマウントオプションが問題なのかなと思い、原因を調査してみました。



Linux上のflockの仕様が変わった?


結論から書くと、クライアント(NFSマウントする)側でnfslockデーモンを起動してあげることで状況は改善しました。


今回の現象は、そもそもNFSサーバー上のコンテンツ書き込みが遅いということではなかったため(vimで書き込んだり、touchコマンドでファイル作成しても一瞬で終わる)、セッションファイルを作成してそれをロックするのに時間がかかっているのかなと思い、ファイルロック周りの仕様を調べていました。

セッションファイルの出力先も下記のようにNFSマウントしているディレクトリ内に出力するようにしています。


session.save_path = "/path/to/nfs/dir"

session_startに限らず、PHPでファイルロックを取得するflock 関数を使っても同様に異常に重くなるという現象が発生します。

また、PHPのログばかりに気を取られていましたが、syslogに下記のようなエラーも出力されていました。


Feb  1 21:10:31 hoge kernel: statd: server localhost not responding, timed out
Feb  1 21:10:31 hoge kernel: lockd: cannot monitor 192.168.0.100
Feb  1 21:10:31 hoge kernel: lockd: failed to monitor 192.168.0.100

RHEL3の環境では同様のストレージをNFSマウントして使ってみても今回の現象は発生せず、そもそもnflockデーモンも起動していませんでした。

なので、NFS自体の設定やオプションが悪いのかなと思いかなりはまってしまいました。


で、そのflockについて調べまわっていたところ、下記の記事を見つけました。


シェルスクリプトでflock @ Stray Penguin


確かにRHEL5からflockがコマンドレベルで使えるようになっていますが、RHEL3では存在していません。

それがどうnfslockと絡んでるんだ、という点までについては調べきれてないのですが、ファイルロックの取得の仕様の変更により、NFS上のファイルロックの取得の仕様も変わったのかもしれません。


nfslockも内部的にはrpc.lockdとrpc.statdを利用していて、先のkernelのエラーのようにrpc.statdへのレスポンスが無くてタイムアウトしてますから、NFSへのファイルロックをkernelそのものから発行するんじゃなくて個々のデーモンに実装が移ったんでしょうかね。詳細わかりませんけど。



その他の回避方法


回避方法も何もnfslockデーモンを起動するだけなので、あまりその他のワークアラウンドを試す必要もないかと思いますが、調査していく中で3つこの現象を発生させない方法を見つけました。


1つ目は、session.save_pathをNFS上にはないディレクトリを指定してセッションを利用するというもの。

NFS上のファイルロックの取得が出来ないために今回の現象が発生していたので、その他のファイルシステム上であれば特に問題はでません。

ただし、今回のsession_start以外にも同様に内部的にファイルロックを取得しようとする挙動をする関数があるかもしれませんし、アプリケーションの仕様でファイルロックを用いるというものがあればこの回避方法は役に立たないかもしれません。

また、そもそもセッションファイルをNFS上に保存するというのは、複数台のアプリケーションサーバー構成の中でもセッションを維持させ続けるための対策として取られてたりもするので、ローカルディスクにセッションファイルを保存する事でその要件を満たせなくなる可能性は出てきます。


2つ目は、セッション利用時の読み込みや保存のロジックをユーザー定義してしまうというもの。

PHPでは、session_set_save_handler によりセッション関数を利用する際の各挙動をユーザー側で定義することができます。

ちょうどマニュアルにもサンプルが掲載されていますが、flockしない版のsession_startと同等の機能を作ってしまうことで問題を回避することも出来ます。

ただし、これもフレームワークを使っているなど、セッションの挙動をその他のシステムが握ってしまっているような環境の場合、思ったより適用することが複雑になったり、反って保守しにくい環境を生み出すことになってしまうかもしれません。

また、こちらもNFS上でファイルロックをするという仕様を満たしてはいないことになりますが。

3つ目も似たようにファイルロックをしないというものですが、NFSマウント時に下記のようにオプションを指定することでロックをしない仕様でNFSマウントをすることができます。


# mount -o nolock /path/to/nfsdir


こちらもNFS上でファイルロックをしないので要件を満たせていない場合がありますが。


後になって調べてみたら、結構似たような状況のことが下記のスレッドにまとまっていました。


【NFS】Network File System @ パソコン情報局


NFS上のファイルロックに関する信頼性の低さなども語られていますけど、複数台のサーバーに対して同一のコンテンツを補完しておきたいなんて場合は、NFSサーバーがあったほうが楽なんですよね。。。

別にNFSである必要は無いんですけど、簡易的な対策としてはポピュラーなのかなと個人的に思ったりします。

まぁ、システム全体の信頼性をあげるために、高価なディスク装置を用意するなど、コスト面では安価となるとは言えませんけどね。