クライアントとサーバの両面から、環境を注意深く分析することが、 NFS の性能を最適化する際の最初のステップになります。 前半のセクションでは、一般にクライアントの方で重要になる点を扱います。 後半 (Section 5.3 以降) では、サーバ側の点を議論します。 サーバ・クライアント両側での項目は、 互いに影響を及ぼしあうことがないわけではありませんが、 原因・結果をはっきりさせるためには、この 2 つを分けておくと便利かと思います。
十分なネットワーク容量、高速な NIC、全二重の設定にして衝突を減らす、 スイッチやハブでネットワークスピードを一致させる、 といったようなネットワーク関連の設定を除くと、 クライアントを最適化するために最も重要な設定は、 NFS データ転送のバッファサイズでしょう。 これは mount コマンドの rsize, wsize 各オプションで設定します。
# time dd if=/dev/zero of=/mnt/home/testfile bs=16k count=16384 |
# time dd if=/mnt/home/testfile of=/dev/null bs=16k |
最後に /etc/fstab を編集して、 決まった rsize/wsize の値を反映させるのを忘れないように。
Bonnie++ http://www.coker.com.au/bonnie++/
IOzone file system benchmark http://www.iozone.org/
公式の NFS ベンチマーク, SPECsfs97 http://www.spec.org/osg/sfs97/
非常にさまざまなファイルサイズや、 さまざまな IO 形式 (読み出し・書き込み、再読み出し・再書き込み、ランダムアクセスなどなど) など、最も広い範囲を扱えて、かつ最も簡単なベンチマークは、 IOzone のように思います。 推奨される IOzone の実行方法としては (これには root 権限が必要ですが)、 テストするディレクトリをアンマウント・再マウントして キャッシュが関与しないようにし、 ファイルクローズ時間の測定をテストに入れるようなやり方です。 すでにサーバ foo で /tmp を制限なしでエクスポートしており、 IOzone をローカルディレクトリにインストール済みであるとすると、 次のようなコマンド群になります。
# echo "foo:/tmp /mnt/foo nfs rw,hard,intr,rsize=8192,wsize=8192 0 0" >> /etc/fstab # mkdir /mnt/foo # mount /mnt/foo # ./iozone -a -R -c -U /mnt/foo -f /mnt/foo/testfile > logfile |
ベンチマークには最大で 2~3 時間かかります。 そして、もちろん対象の rsize や wsize を変更するたびに実行する必要があります。 web サイトにはパラメータを網羅した文書がありますが、 上記で用いたオプションについては以下で説明します。
-a 完全自動モード。 ファイルサイズ 64K から 512M までを、 レコードサイズ 4K から 16M まででテストします。
-R レポートを Excel のスプレッドシートで生成します (グラフには "surface plot" オプションを用いるのが良いでしょう)。
-c ファイルクローズ時間のテストを行う。 これは NFS version 3 の commit 時間を取得します。
-U 与えられたマウントポイントを テストごとにアンマウント/再マウントし、キャッシュをクリアする。
-f アンマウントを用いるときは、 マウントされたファイルシステムに置かれた テストファイルを指定する必要があります。
NFS ベンチマーク SPECsfs の実行結果がいくつか公開されていますが、それらでは [rw]mem_default と [rw]mem_max の両方にずっと大きな値を指定しています。 これらの値は、少なくとも 256k にまで増やすことを考えるべきです。 読み書きの上限値は、(例えば) proc ファイルシステムの /proc/sys/net/core/rmem_default と /proc/sys/net/core/rmem_max を用いて設定します。 rmem_default の値を増加させるには 3 つの段階を踏みます。以降に示す方法はちょっとあらっぽいですが、 ちゃんと動作しますし、問題を起こすこともないはずです。
これらのファイルに書かれているサイズを増加します:
# echo 262144 > /proc/sys/net/core/rmem_default # echo 262144 > /proc/sys/net/core/rmem_max |
NFS を再起動します。例えば RedHat システムなら次のようにします。
# /etc/rc.d/init.d/nfs restart |
サイズの上限値を通常の値に戻し、 他のカーネルシステムはこちらを使うようにします。
# echo 65536 > /proc/sys/net/core/rmem_default # echo 65536 > /proc/sys/net/core/rmem_max |
この最後のステップは不可欠で、 これらの値を長い間変えたままにしておくと、 マシンがクラッシュするというレポートも受けています。
2 つのファイルシステムを、少々異なるオプションで、 あらゆるクライアントに対してエクスポートします。
# /usr/sbin/exportfs -o rw,sync *:/usr/local # /usr/sbin/exportfs -o rw *:/tmp |
するとエクスポートされたファイルシステムのパラメータは 次のようになります。
# /usr/sbin/exportfs -v /usr/local *(rw) /tmp *(rw,async) |
RAID アレイを利用できる場合は、 RAID 1/0 を用いて書き込み速度と冗長度の両方を確保しましょう。 RAID 5 を用いると読み込みは速くなりますが、 書き込みはみじめなことになります。
ジャーナリングファイルシステムを用いれば、 システムがクラッシュしたときに再起動に要する時間が劇的に短くなります。 現時点では、 ext3 が NFS version 3 と一緒に正しく動作します。 また Reiserfs version 3.6 以降も、 2.4.7 以降のカーネルの NFS version 3 と一緒に正しく動作します (それ以前のカーネルに対するパッチもあります)。 それより前の Reiserfs では、生成番号 (generation number) の保管場所が inode に無いため、 サーバが再起動したときに検知不可能な形でデータが破壊する可能性があります。
また、ジャーナリングファイルシステムにおいて、 ジャーナルの更新はデータ保護のためにのみ必要である、 という事実を利用すれば、性能を最大化することが可能です。 例えば ext3 を例に取るなら、 data=journal を用いて、 更新をまずすべてジャーナルに対して行ない、 そしてその後でファイルシステム本体に対して行うようにします。 ジャーナルが更新されたら、 NFS サーバは安心してクライアントに対する応答を発行でき、 メインファイルシステムの更新はサーバが暇なときに行なえば良いのです。
ジャーナリングファイルシステムのジャーナルを フラッシュメモリカードなどの別のデバイスに置き、 ジャーナルの更新時にシーク動作を不要にすることもできます。 こうすれば回転待ちだけがコストになるので、 同期 IO の性能をかなり良くできます。 いまでは ext3 はジャーナルのリロケーションをサポートしており、 また ReiserFS も近々 (公式に) サポートするはずです。 ftp://ftp.namesys.com/pub/reiserfsprogs/reiserfsprogs-3.x.0k.tar.gz にある ReiserFS 用のツールパッケージには、 reiserfstune というツールがあり、 これを用いるとジャーナルのリロケーションを行なえます。 しかしこれにはカーネルパッチが必要で、 これはまだ 2002 年 1 月の段階では公式にはリリースされていません。
automounter (autofs や amd) を用いれば、 クロスマウントを (わざとでもうっかりでも) したマシンの どちらかが落ちたときでも、もう片方のハングアップを避けられます。 詳細は Automount Mini-HOWTO を見てください (JF に 日本語訳 があります)。
メーカーによっては、不揮発 RAM (NVRAM) を用いた NFS アクセレレータを提供しています (Network Appliance, Hewlett Packard など)。 NVRAM を用いれば、永続的なストレージへのアクセスが async 利用時と同じくらいにまで加速できます。