LAST MODIFIED: 2011/07/24 07:02:00 UTC
不定期更新の日記です。ディスクスペースの関係上、 あまりに古くなったものは順次消していきます。 この日記の更新は、今野さんの *BSD Diary Links から取得することが可能です。
allbsd.org のファイルサーバが 22 日から不調に。 NFS export している ZFS 領域へのアクセスで panic が発生していることを確認。
システムを復旧させようと、とりあえず kernel dump をとって再起動をかけたところ、 ZFS のカーネルモジュールを読んでから、zpool を認識するところで同じ panic が繰り返される状態に。 panic 内容は、次のとおり。 システムは FreeBSD/amd64 の 8-STABLE。
panic: Solaris(panic): zfs: allocating allocated segment(offset=%llu size=%llu) cpuid = 1 KDB: stack backtrace: db_trace_self_wrapper() at db_trace_self_wrapper+0x2a kdb_backtrace() at kdb_backtrace+0x37 panic() at panic+0x187 vcmn_err() at vcmn_err+0xbe zfs_panic_recover() at zfs_panic_recover+0x7a space_map_add() at space_map_add+0xe4 space_map_load() at space_map_load+0x198 metaslab_activate() at metaslab_activate+0x11a metaslab_alloc() at metaslab_alloc+0x6e6 zio_dva_allocate() at zio_dva_allocate+0x70 zio_execute() at zio_execute+0xc3 taskqueue_run_locked() at taskqueue_run_locked+0x85 taskqueue_thread_loop() at taskqueue_thread_loop+0x4e fork_exit() at fork_exit+0x11f fork_trampoline() at fork_trampoline+0xe --- trap 0, rip = 0, rsp = 0xffffff8256feed00, rbp = 0 ---
どうもデータが損傷しているもよう。
zpool が認識できないとなると、5TB 近いミラーデータが全部ふっとんでしまうことになる。 正攻法ではどうにもならなそうなので、方針を変更。 ZFS はデータ完全性のチェックが厳しいので、 多少データが壊れてても、あとで検出くらいはできるだろう、 という発想で、無理矢理にでも認識させることを目標にする。
ファイルサーバのシステムディスクは UFS なので、 シングルユーザモードで起動。panic を発生させているコードを読むと、 vfs.zfs.recover=1 にすれば、データの不整合による panic を発生させないことが可能なようなので、とりあえずそれを定義してみる。
そうすると、別の panic が発生した。
panic: solaris assert: sm->sm_space == space (0xd01a60400 == 0xd01a61c00), file: /usr/src/sys/modules/zfs/../../cddl/contrib/opensolaris/uts/common/fs/zfs/space_map.c, line: 334
VERIFY3U という、データ構造に対するアサーションにひっかかったらしい。 これは loader tunable では無視できないので、 コメントアウトしてカーネルを再構築。
再起動させたところ、無事に zpool が認識された。 念のため、scrub を仕掛けておく。メーリングリストを検索してみると、 おなじ panic ではまっている人は他にもいるようだ。
OpenSolaris のコードでは、panic recovery の他に、 assertion を無視する aok という変数があり、 これを設定することで、緊急避難的な操作ができるようになっている。 この類の障害(zpool の認識時に panic してどうにもならん)が発生した時には、 panic_recover と aok の両方を 1 にするというのが最終手段。 FreeBSD のコードには後者がないので、そこでコケると、結構厳しい。
とりあえずシステムは復旧したようなので、様子見。 Solaris の knob があるなら、FreeBSD で対応しない理由もなかろう、ということで、 vfs.zfs.aok という名前で操作できるコードを FreeBSD に入れてみた。 あとでパッチを送っておこう。
panic その 2。
Fatal trap 12: page fault while in kernel mode cpuid = 1; apic id = 01 fault virtual address = 0x1698f010 fault code = supervisor read data, page not present instruction pointer = 0x20:0xffffffff8103356e stack pointer = 0x28:0xffffff825783af30 frame pointer = 0x28:0xffffff825783aff0 code segment = base 0x0, limit 0xfffff, type 0x1b = DPL 0, pres 1, long 1, def32 0, gran 1 processor eflags = interrupt enabled, resume, IOPL = 0 current process = 45077 (nfsd: service) [thread pid 45077 tid 100522 ] Stopped at dbuf_read+0xee: cmpq $0,0x50(%rax)
デバッガで追っかけてみると、コード的にはこの部分。
(kgdb) f 10 #10 0xffffffff8103356e in dbuf_read (db=0xffffff00398aa380, zio=0xffffff002f4376e0, flags=10) at /usr/src/sys/modules/zfs/../../cddl/contrib/opensolaris/uts/common/fs/zfs/dbuf.c:548 548 if (db->db_blkptr == NULL || BP_IS_HOLE(db->db_blkptr) ||
この panic は、上述の panic とは別に、 ある時期から頻発して発生するようになった。 どうも発生タイミングが掴めなかったのだけど、 nfsd(8) を起動した瞬間に発生するので、かなり困った事例。
複数回の panic ログをみながら backtrace を比較してみると、 どうも削除操作に起因しているらしいことに気がついた。
#21 0xffffffff8082cc80 in nfsrv_remove (nfsd=0xffffff825783b9c0, slp=0x0, mrq=0xffffff825783b9b0) at /usr/src/sys/nfsserver/nfs_serv.c:1819
大規模な削除を行なっている NFS クライアントのプロセスを止めてみると、 たしかに panic が発生しない。そのプロセスが削除していたファイル群を、 NFS サーバ上で別のディレクトリに移動させて、1個1個削除していくと、 あるファイルを削除しようとした瞬間、同じ panic が発生。
つまり、ファイルシステム上のデータがおかしくなっているということ。 他のプロセスが触らないように移動させると、panic 連発状態は回避できた。 しかし依然として、削除しようとすると panic が発生するのは変わらない。 scrub が検出してくれることを祈りつつ、結果待ち。
少しは文章を書かないといかんだろう、 ということで久々に更新。
FreeBSD 9.0R に向けたリリース作業がそろそろ本格的に動きはじめたので、 いろいろと作業中。現在は -BETA1 をつくる、という段階。 スケジュールはこのへんを参照のこと。
FreeBSD の日本語情報が最近少なくなったよね、 という話を良く聞くようになって久しいので、 少しは宣伝しなきゃという気持ちで 9.0 の話を書くことにしました。 第一弾は NFS です。
UNIX 系 OS の使用経験者ならおなじみの、 今はなき Sun Microsystems が 1984 年に開発した、 ネットワーク透過なファイルシステムを実現するプロトコル[1]です。 Sun は、NFS を実現するために VFS と Sun RPC を開発し、 1985 年に SunOS 2.0 に導入しました。どちらの実装も現在に至るまで大活躍しており、 VFS は BSD 系、System V 系の両方に取り込まれ、Sun RPC は ONC RPC (RFC 1831, 5531) としてオープンな技術になっています。
単純で明示的な TCP/IP 通信と違って RPC を使うことから、 管理者からは「分かりにくい面倒なもの」という印象を持たれていますが、 それは設計の主眼だった「ユーザからは、 とにかくローカルでの操作と区別がつかないように見えること」の裏返しでもあります。
NFS マウントしたディレクトリへの操作と、ローカルでの操作との間は、 ユーザランドプログラムから見て、ほとんど違いがありません。 今使っているプログラムを変更することなく、それを ネットワーク経由でリモートマシンにあるファイルに対しても適用できる、 というのは、今でこそいろいろな方法で実現されていますが、当時は非常に挑戦的なことだったわけです。
実装としては v2, v3, v4 のバージョンがあります。 違いはたくさんありますが、ざっくり書くと次のとおりです。
v2 | v3 | v4 | |
---|---|---|---|
トランスポート | UDP | TCP or UDP | TCP |
一回の通信における 最大転送データ量制限 | 8192 バイト | なし | なし |
ファイル名の長さ制限 | 255バイト | なし | なし |
パス名の長さ制限 | 1024バイト | なし | なし |
ファイルサイズ制限 | 32-bit (2GB) | 64-bit | 64-bit |
非同期書き込み | なし | あり | あり |
ファイルロック | なし(別プロトコルであり) | なし(別プロトコルであり) | 標準装備 |
ステート保持 | なし(別プロトコルであり) | なし(別プロトコルであり) | あり |
標準化 | RFC 1094 | RFC 1813 | RFC 3010, 3530, 5661 |
Linux 等の PC-UNIX でふつう使われているのは v3 です。 v2 と v3 は設定方法にほとんど違いがないので、 気づかずに v2 を使っているというケースもあるかも知れません。 2GB 以上のファイルを扱ったりすると、はまります。
基本的に、 実現できること(=リモートマシンのファイルシステムを、 ローカルのファイルシステムのように操作すること)は同じです。
FreeBSD の NFS 実装は、Sun が公開した NFS コードが 4.3BSD Reno に取り込まれた後、 BSD で発展してきたコードの流れを継承したものです。 NFSv2 と v3 に対応しており、 性能を向上させるための独自の改良がいくつか追加されています[2]。 しかし、長い間 NFS のコードは進化することなく、 メンテナンス担当不在のまま、バグ修正だけが細々と続けられる状況が続いていました。
一昨年、4.4BSD の NFS 実装の作業をしていた Rick Macklem 氏が FreeBSD committer として参加することが決まりました。 そして彼の作業をベースに、NFS 実装の大幅な刷新に入ります。 もちろん、もともと彼が 4.4BSD の時代に書いたコードが FreeBSD に入っていたわけですが、 それはもう古いものなので、大きく書き換えたものを作成しました。 9.0 に入っている NFS 実装は、古いものと、書き換えたものの両方が入っており、 新しいものがデフォルトになっています。
新しい実装は、v2/v3 は細かな不具合を修正した程度で、操作上の大きな違いはありません。 違いが大きいのは、v4 の実装が入ったことです。FreeBSD 6.X や 7.X にも NFSv4 の実装(ミシガン大学に由来するもの)が入ってはいたのですが、 満足に動作する状態に維持されておらず、事実上使えないままでした。 NFS の専門家が開発者として参画したことで、ようやく FreeBSD も、まともな NFSv4 を獲得することができた、というわけです。
FreeBSD 9.0 以降、古い実装は mount_oldnfs コマンドのように、oldnfs と呼ばれます。FreeBSD 8.X にも、実は新しい実装がすでに入っていて、mount_newnfs のように、newnfs と呼ばれています。ちょっと混乱するかも知れませんが、 8.X では nfs + newfs という組み合わせ、9.X 以降では oldnfs + nfs という組み合わせというように、名前だけが変わったのです。どちらも、「nfs」と呼んでいるのがデフォルトですが、中身が入れ替わりました。8.2-RELEASE にも newnfs という名前で入っていますので、新機能を試したい場合は、newnfs を使いましょう。
NFS の構造は複雑なので、使っているよ、という人でも 意味をちゃんと理解していないことが多かったりします。 技術的な部分を理解すれば、「どうしてこうなっているのか」が分かりやすいのですが、 それは大変なので、プロトコルの技術的な側面を最小限にしつつ、 管理者が知っておくべき知識を再整理してみましょう。
NFS プロトコルは、ファイルハンドルと呼ばれる数値でファイルを識別します。 v2 では 32 バイトの値、v3 では最大 64 バイトの値です。 FreeBSD ではファイルシステムタイプ、デバイス番号、 inode 番号などから生成される値を使っています。
クライアントがサーバにアクセスする時、このファイルハンドルを使ってアクセスするわけですが、 クライアントでは、サーバのファイルハンドルを知る方法がありません。 MOUNT プロトコルは「最初のファイルハンドルを、 サーバからクライアントに伝えるためのプロトコル」です。
クライアントはまず、MOUNT プロトコルを使ってサーバにアクセスし、 マウントポイント(ディレクトリ)のファイルハンドルを教えてもらいます。 それ以降のアクセスは、マウントポイントのファイルハンドルを基点にして、 別のファイルハンドルを知るわけです。(具体的には LOOKUP コールを、 マウントポイントのファイルハンドルに対して発行します)
mountd(8) は、/etc/exports ファイルを読み込んで、 マウントポイントに対応したファイルハンドルをクライアントに渡す役割を担っています。 当然ながら、サーバのみで必要になります。 これが起動していないと、NFS クライアントはマウント処理ができません。 RPC を使うので、rpcbind(8) のあとに起動しなければならない点に注意しましょう。
ちなみに、一度ファイルハンドルを受けとったクライアントは、それ以降は nfsd(8) と通信し、もう mountd(8) と通信する必要がありません。公開マウントポイントには後述するように IP アドレスの制限などをかけることができますが、一回マウントされてしまった後は、その制限を変更してもクライアントを締め出すことはできない、ということです。覚えておくと良いかも知れません。
NFS プロトコルの RPC コールを処理するデーモンです。サーバのみで必要になります。 ファイルハンドルを受けとり、ローカルのファイルシステムへのアクセスに変換して処理し、 結果をクライアントに送り返します。
これも RPC を使うので、rpcbind(8) のあとに起動しなければならない点に注意しましょう。
NFS プロトコルにはファイルロックの機能がありませんが、 需要が高かったため、別プロトコルとして NFS Lock Manager (NLM) というものが開発されました。 rpc.lockd(8) は、そのプロトコルを担当するものです。これが起動していないと、 アプリケーションが NFS 上のファイルにロックを要求する操作をした時に、 正常に動作しません。
このデーモンは、サーバとクライアントの両方で必要です。互いに通信を行ない、 ロック状態を管理します。
FreeBSD 8.X からは、NLM の機能がカーネルに統合されたため、 機能そのものはユーザランドデーモンではなく、カーネルで行なわれています。 起動しなければならないことには、変わりありませんが。
NLM には状態を保持する機能がないため、 NFS サーバがクラッシュして復帰すると、 サーバ上のロック情報が全部失われてしまうという欠点があります。 そこで、NSM という、情報を保持するプロトコルを別に用意して、連係させます。 NSM は、ロック状態をローカルファイルに保存することで、 サーバが再起動してもロック状態を引き継げるようにします。
rpc.statd(8) は、そのプロトコルを担当するものです。 状態は /var/db/statd.status に保存されます。rpc.lockd(8) を使う時は、 必ずこれもペアで起動する必要がある、と覚えましょう。
/a というディレクトリを 192.168.0.0/24 のネットワークに公開する NFSv3 サーバ (IP アドレスが 192.168.0.1) をつくりたい場合、 設定ファイルは次のようになります。
まずは /etc/rc.conf です。
nfs_server_enable="YES" rpcbind_enable="YES" nfsd_enable="YES" mountd_enable="YES" rpc_lockd_enable="YES" rpc_statd_enable="YES"
/etc/exports は次のとおりです。
/a -network 192.168.0.0 -mask 255.255.255.0
手動で起動させたい場合は、 rpcbind, statd, lockd, nfsd, mountd の順番で起動すると良いです。 rc.d スクリプトを直接実行してしまってください。
# /etc/rc.d/rpcbind start # /etc/rc.d/statd start # /etc/rc.d/lockd start # /etc/rc.d/nfsd start # /etc/rc.d/mountd start
クライアントでは、/etc/fstab に次のようなエントリを書きます。
192.168.0.1:/a /mnt nfs rw 0 0
これで終了です。
NFS v2/v3 を使うときにハマりがちな落し穴を知っておきましょう。
NFS v2/v3 では、サーバ上のファイルシステムの許可属性にある UID 番号/GID 番号と、 クライアントシステムの UID 番号/GID 番号が、分離していません。 なので、同じユーザ名で UID 番号が違う、という状況をつくると、 別のユーザのファイルにアクセスできてしまったりします。
これを防ぐために、NIS や LDAP など、ネットワークディレクトリサービスを使って UID/GID を共通にする、という手法が良く使われます。 小規模なら手動でデータベースを合わせてもやってやれないことはありませんが、 クライアントがたくさんになると、かなりきつい作業になります。
また、UID=0 のアクセスは、NFS サーバ上の root ユーザでのアクセスに対応するので、 特別な注意が必要です。/etc/exports のオプションに -maproot=UID番号:GID番号 というものがあり、 UID=0 でのアクセスを、NFS サーバ上のどの UID/GID として扱うかがコントロールできるようになっています。 何も付けなければ、nobody ユーザになるので、もし UID=0 でのアクセスを root ユーザのアクセスとしたい場合は、 -maproot=0:0 を付けなければならないことを覚えておきましょう。
NFSv2 は、2GB の大きさを超えるファイルを扱うことができません。 動画等、2GB を超えるファイルは現在では珍しくありません。 v2 を使う積極的な理由がなければ、v2 は捨てましょう。
すでに説明したとおり、NFS は複数のプロトコルを組み合わせた複雑なシステムです。 RPC を使うため、ポート番号が一定でないという特徴もあります。 セキュリティを確保しようと不要な通信を制限したい管理者にとっては頭の痛いものですし、 一部が通信できないと中途半端に動かなくなるので、原因の究明が厄介です。
まず、rpcbind は /etc/hosts.allow の影響を受けます。 nfsd, mountd, rpc.lockd, rpc.statd は /etc/hosts.allow が使えませんが、 ポート番号や bind する IP アドレスを指定することができます。 詳しくはマニュアルページを参照いただくとして、/etc/rc.conf には次のように書けることを覚えましょう。
nfsd_flags="-h 192.168.0.1" mountd_flags="-h 192.168.0.1 -p 996" rpc_statd_flags="-p 990" rpc_lockd_flags="-p 993"
nfsd のポート番号のデフォルトは 2049 です。nfsd は常にこのポート番号を使おうとするため、 -p オプションがありません。/etc/services を変えると変えられますが、変える必要はないでしょう。
複数のインタフェースがあるマシンでは、 bind する IP アドレスを -h オプションで制限するのがおすすめです。 rpcbind(8) にもあります。特に、インターネットに直接つながる場合は要注意です。
/etc/exports の文法は非常に分かりにくい制限があります。 ルールを一言で書くと、1) ローカルファイルシステム 1 個に対して 1 行、 2) 同じ属性を持つ公開マウントポイントに対して 1 行を割り当てる、ということになります。
たとえば、/a が NFS サーバ上のマウントポイントで、2 つのサブネットに /a を公開したい。 192.168.0.0/24 は読み書き可能、192.168.1.0/24 は読み出しのみとしたければ、次のようになります。
/a -network 192.168.0.0 -mask 255.255.255.0 /a -ro -network 192.168.1.0 -mask 255.255.255.0
/a には、/a/b と /a/c という 2 個のサブディレクトリがあったとしましょう。/a ではなく、公開マウントポイントを /a/b と /a/c にしたければ、次のように書く必要があります。
/a/b /a/c -network 192.168.0.0 -mask 255.255.255.0 /a/b /a/c -ro -network 192.168.1.0 -mask 255.255.255.0
ここからさらに、/a/b だけ両方のネットワークで読み書き可能にするには、次のように変えます。
/a/b /a/c -network 192.168.0.0 -mask 255.255.255.0 /a/c -ro -network 192.168.1.0 -mask 255.255.255.0 /a/b -network 192.168.1.0 -mask 255.255.255.0
さらに、NFS サーバ上に /b という別のマウントポイントがあって、/a/b と同じように両方のネットワークで読み書き可能にするには、次のように変えます。
/a/b /a/c -network 192.168.0.0 -mask 255.255.255.0 /a/c -ro -network 192.168.1.0 -mask 255.255.255.0 /a/b -network 192.168.1.0 -mask 255.255.255.0 /b -network 192.168.0.0 -mask 255.255.255.0 /b -network 192.168.1.0 -mask 255.255.255.0
要は、ローカルマウントポイントは、基本的にそれで独立した 1 行が必要です。そのマウントポイントの下のディレクトリは、1 行にまとめて並べます。そして、もし、公開したい IP アドレス範囲が違う、読み書き等の属性が違う、という場合には、行を分けます。
次のような書き方は、/a がローカルマウントポイントの場合(つまり /a/b と /a/c が同じファイルシステムにある)は許されていません。 /a/b, /a/c が独立したローカルマウントポイントなら、逆に、この書き方でないとエラーになります。
/a/b -network 192.168.0.0 -mask 255.255.255.0 /a/c -network 192.168.0.0 -mask 255.255.255.0
非常に混乱しやすいので、書き換えたあとは、ちゃんと読み込まれているかどうかチェックしましょう。 /etc/exports の担当は、前述したとおり mountd(8) です。SIGHUP を送ると /etc/exports を再読み込みしてくれます。/etc/rc.d/mountd スクリプトを reload という引数付きで呼び出しても、同じ効果になります。その後、エラーがあれば /var/log/messages にログが出ますので、 エラーが出なくなるまで調整してください。
# /etc/rc.d/mountd reload
公開マウントポイントは、showmount -e でもチェックできます。
ある NFS サーバが、他の NFS サーバのクライアントでもある場合、 公開マウントポイントの下に、他の NFS サーバからマウントしたディレクトリを配置することが可能です。 そうすると、複数の NFS サーバで公開している内容をマウントしてディレクトリツリーを構成し、 それを再公開する NFS サーバがつくれそうに思えますが、実際にやってみると、 それはうまく動きません。NFS マウントしたディレクトリは、 nfsd を使って再公開することができないようになっています。 空っぽのディレクトリが見えるだけなので、注意しましょう。
さて、NFSv3 の設定は目新しいものではないので、 9.0 からデフォルトになった新しい NFS 実装で使える NFSv4 の設定方法をみてみましょう。 8.2R を使っている人も、デフォルトになっていないだけで、実は使えます。 次のカーネルモジュールを読み込ませてください。
# kldload nfscl # kldload nfsd
NFSv4 の設定に入る前に、v4 の予備知識が必要です。
まず、v4 で大きく違うのは、MOUNT プロトコルです。 v2 や v3 では「最初のファイルハンドル」をサーバから得るために使われるもので、 /etc/exports に並べた公開マウントポイントを mountd が読みとって、 クライアントからの要求に応じてファイルハンドルを渡す、というものでした。
NFSv4 では、この公開マウントポイントが、サーバ 1 つに対して 1 個しか定義できません。 つまり、NFS サーバで公開したいディレクトリが複数あった場合、 それはすべてあるディレクトリの下に存在していなければならない、ということです。 NFSv4 では、サーバは仮想的なディレクトリツリーを 1 個持っていることを仮定しています。
次に、もともと NFS コアプロトコルとは別個のものだった NLM と NSM が、必須プロトコルになりました。サーバの状態は、/var/db/nfs-stablerestart に保存されます。rpc.lockd と rpc.statd は、必要ありません。
コアプロトコルにおける、UID/GID の取り扱いが変わりました。 v2, v3 では、クライアントが送った UID/GID を、そのままサーバ上の UID/GID に対応づける単純なものでしたが、NFSv4 ではユーザ名とグループ名を「user@domain」という文字列で扱います。 こうすることで、UID/GID がサーバとクライアントで共通になっていなくても、 名前が合っていれば同じユーザからのアクセスと認識されるわけです。
このユーザ情報と UID/GID との対応は、nfsuserd(8) というデーモンが担当します。 したがって、サーバとクライアントの両方で、このデーモンが動いている必要があります。 これがないと、ファイルの所有者等の情報が正しく扱えません。
対応関係は、前述したとおり user@domain という形です。user は、ログインユーザ名が使われます。domain は、デフォルトでは FQDN のドメインパートです。ただし、このドメインは DNS 名とは直接関係があるものではありませんので、サーバとクライアントで共通になっていれば、それで OK です。
ここまでをまとめましょう。必要なデーモンは次のとおりです。
v2, v3 と共存するには、rpc.lockd や rpc.statd を起動する必要がありますが、 v4 のみであれば、起動しなくて問題ありません。
/etc/exports の書き方は、次のようになります。192.168.0.2 は、NFS クライアントマシンの IP アドレスです。
V4: / -network 192.168.0.0 -mask 255.255.255.0 /a -maproot=0:0 192.168.0.2
V4: から始まる行が、NFSv4 で使われる公開マウントポイントです。 前述したとおり、NFSv4 では公開マウントポイントが 1 個しか持てず、 公開するディレクトリツリーは、すべてそのポイントの下位になければならない、という制約があります。
「/ って書いたら、ファイルシステム全体が公開されてしまうのでは」と心配になる書き方ですが、「マウントできること」と「アクセスできること」は、全く違う概念であることを思い出してください。繰り返しになりますが、/etc/exports と mountd(8) は、MOUNT プロトコルを担当するものです。MOUNT プロトコルは、クライアントがサーバにアクセスするための、「最初のファイルハンドル」を得るために使われるものでした。NFSv4 では、この最初のファイルハンドルに対応するものが 1 個になっています。
クライアントは、V4: の行で指定されたポイントより下位のディレクトリを、最初のファイルハンドルを使うことで知ることが可能です。しかし、マウントできる、あるいはそこにアクセスできるかどうかは、「最初のファイルハンドルを使って、アクセスしたいファイルハンドルが得られるかどうか」に依存します。/etc/exports の V4: 行は、あくまで最初のファイルハンドルへのアクセスを許可するだけで、それより下位のディレクトリのファイルハンドルへのアクセスは、無条件に許可しません。v2 や v3 の時と同様、公開マウントポイントは、別個の行として定義する必要があり、その定義の有無でアクセス許可が決まります。
つまり、NFSv4 を使いたければ、V4: 行を追加することが必要ですが、あとの個別のマウントポイントの設定は同一なのです。
V4: 行のアクセス制限は、NFS クライアント全体をカバーするように工夫する必要があります。個別の公開マウントポイント指定行でアクセス制限ができるので、極端に神経質になる必要はありませんが、ノーガードはやめましょう。
設定したら、マウントしてみましょう。サーバでの操作手順をまとめると、次のようになります。
まず、/etc/exports は次のとおりです。
V4: / -network 192.168.0.0 -mask 255.255.255.0 /a -maproot=0:0 192.168.0.2
/etc/rc.conf は次のようになります。
nfs_server_enable="YES" nfsv4_server_enable="YES" rpcbind_enable="YES" nfsd_enable="YES" nfsuserd_enable="YES" nfsuserd_flags="-domain example.com" mountd_enable="YES"
nfsuserd には、ドメイン名を指定するためのオプション -domain があります。ホスト名を変更したりすることを考えると、付けておいたほうが無難でしょう。
有効化するためのコマンドは、たとえば次のようになります。
# /etc/rc.d/rpcbind start # /etc/rc.d/nfsd start # /etc/rc.d/nfsuserd start # /etc/rc.d/mountd start # showmount -e Exports list on localhost: /a 192.168.0.0
最後の showmount -e は、mountd(8) がちゃんと /etc/exports を読み込んだかどうかのチェックです。
クライアントでは、次のようにします。
# mount -o nfsv4 192.168.0.1:/a /mnt
ここでは、サーバの IP アドレスが 192.168.0.1, ローカルのマウントポイントに /mnt を選択しています。-o nfsv4 は必須です。デフォルトでは、最初に NFSv3 を使ってマウントしようとしてしまうからです。
V4: に設定するディレクトリ階層は、「/」を使いましょう。「/」以外でも、もちろん使えるのですが、その場合は NFSv4 の基点が指定したディレクトリになってしまいます。クライアントからマウントする時は、基点からのパス名を指定するので、
V4: /a -network 192.168.0.0 -mask 255.255.255.0 /a/b -maproot=0:0 192.168.0.2
という設定をすると、マウントする側は
# mount -o nfsv4 192.168.0.1:/b
のように、指定する必要が生じます。v2, v3 では /a/b になるので、 共存させようとした場合は、管理者が混乱することでしょう。 「/」以外を選択することはセキュリティ的な利点が予想されますが、 実際のところ、そうすることで得られる効果は小さいです。
また、nfsd(8) が提供するサービスのバージョンを制限したい、 と思うケースがあると思います。たとえば、「NFSv2 は古いプロトコルなので、 クライアントが間違って使わないようにサーバ側で受け付けないようにしたい」というのが典型例でしょう。その場合は、サーバ側で sysctl 変数を設定します。
# sysctl vfs.nfsd.server_min_nfsvers=3
FreeBSD 8.X では、vfs.newnfs.server_min_nfsvers という名前になっています。受け付ける NFS プロトコルのバージョンの下限を指定するものです。デフォルトは 2 (NFSv2 という意味) になっています。これを 3 にすれば、v2 でのアクセスはできません。4 にすれば、NFSv4 専用サーバになります。max_nfsvers というのもあり、そちらは上限を設定します。デフォルトは 4 です。
NFS v2/v3/v4 を共存させたい場合には、次のことに注意してください。
NFSv4 には delegation という、性能を向上させるためのクライアントサイドキャッシュ機能が追加されています。FreeBSD の実装では nfscbd(8) が担当するものです。クライアントで起動させると一部の NFS 操作がクライアント側だけで完結するようになり、理論上は性能が向上します。ただ、現在の 9.0 では実装が一部しか行なわれていないため、効果が限定的です。クライアントで起動しておいたほうが良いものだ、と覚えておくと良いでしょう。
また、NFSv4 の実装では、デバイスをまたいだ export が可能なものがありますが、FreeBSD の実装はできません。このあたりの制限は NFSv2, v3 と同様です。
NFS のセキュリティは、RPC のセキュリティに頼っています。GSSAPI を使って認証や暗号化を行なうことができ、FreeBSD の実装も対応しています。基本的に Kerberos サーバが必要になり、ここで簡単い詳細を説明することは難しいですが、exports(5) マニュアルページに -sec= というオプションが載っています。
Kerberos も、国内ではあまり情報がなくて困る、という類のサービスです。昔 UNIX USER にまとめ記事を書きましたが、機会があれば、またまとめてみようかと考えています。
LAST MODIFIED: 2011/07/24 07:02:00 UTC