nfs環境の覚書

正直あまり使いたくないNFSを使わなければいけない状態に陥ってしまったので覚書。

# 前提環境（サーバ・クライアントとも）
+ CentOS7
+ Linux 3.10.0-957.el7.x86_64 #1 SMP Thu Nov 8 23:39:32 UTC 2018
+ NFSv4

# サーバ側設定
/etc/exportsに書くだけ。または、ドロップインディレクトリ`/etc/exports.d`配下にファイルを追加する。

```ini:/etc/exports
/work 192.168.3.0/24(rw)
```

一つのディレクトリを複数のネットワークに公開するには、スペース区切りのリストにする。

```ini:/etc/exports
/work 192.168.3.0/24(rw) 10.1.1.0/24(ro)
```

スペースは公開対象リストのデリミタとしての意味を持つため、例えば以下のように書き間違えると大変なことになる。

```ini:/etc/exports
/work 192.168.3.0/24 (rw)
/work 192.168.3.0/24() *(rw)
```

上の2行は同じ意味。つまり、192.168.3.0/24のネットワークに対してデフォルト設定で公開し、全世界"&lt;world&gt;"に向けて"rw"その他デフォルト設定で公開するということになる。
公開先とオプションの"()"の間にスペースを入れてはいけない。まったく別の意味になる（と、マニュアルにも注意書きがある）。

# 主なオプション＜サーバ側＞（`man exports`から抜粋）
+ `secure`: 1024より小さいポート番号からのリクエストしか受け付けない。これがデフォルト。無効にするには`insecure`を明示。
+ `rw`: Read/Writeを可能にする。デフォルトは`ro`（read only）。
+ `async`: NFSプロトコルでは同期が標準だがこれをあえて非同期にする（パフォーマンス向上、信頼性・一貫性犠牲にしたいとき）。デフォルトは`sync`。
+ `root_squash`: uid/gidが0、つまりrootユーザからのリクエストをanonymousに格下げ（squash）する。デフォルト有効で無効にするには`no_root_squash`を指定する。
+ `all_squash`: root以外のユーザからのリクエストもanonymousに格下げ（squash）する。デフォルトは`no_all_squash`。
+ `anonuid`/`anongid`: 格下げ（squash）する対象のuid/gidをそれぞれ指定する。

# 主なオプション＜クライアント側＞（`man nfs`から抜粋）
+ `nfsvers=n`: NFSプロトコルバージョンを指定する。
+ `soft`/`hard`: デフォルトは`hard`で、NFSサーバダウン時に無限リトライ。`soft`にするとリトライアウトあり。
+ `timeo=n`: タイムアウト値を指定する。単位は1/10秒。TCP使用時のデフォルトは600（60秒）。
+ `retrans=n`: `soft`マウント時のリトライ回数を指定する。
+ `intr`/`nointr`: 下位互換性のため保持（無視）。
+ `bg`/`fg`: マウントをバックグラウンドかフォアグラウンドで実行するかを指定する。
+ `retry=n`: マウントのリトライ時間を分単位で指定する。デフォルトはフォアグラウンドで2分、バックグラウンドでは10,000分（ほぼ1週間）。0に指定するとリトライなし。

# 主なコマンド
+ `exportfs -a`: 設定されたディレクトリをすべて公開する。
+ `exportfs -au`: 公開されたディレクトリをすべて非公開にする。
+ `exportfs -v`: 公開されているディレクトリの詳細を表示する。
+ `nfsstat -l`: NFS統計情報をリスト形式で出力する。
+ `nfsstat -m`: NFSマウントの情報（オプション等）を出力する（クライアント側）。

# NFSサーバダウン時の影響（fstab編）
NFSはいわゆる蜜結合なソリューションであり、常時マウントすべく`/etc/fstab`にNFSマウントのエントリを`defaults`で記載しておくと、NFSサーバのダウン時に非常に困る。そして現場SEを以下のようなシチュエーションで苦労させることになる。
※昔のOS（CentOSだと6まで）では`fstab`ではなく`netfs`というサービスでNFSマウントさせていたが、現行バージョンでは必要なくなった模様。

```ini:/etc/fstab
db1:/work /tmp/work nfs defaults 0 0
```

## OS起動時
マウント時の`retry`オプション（デフォルト2分）でタイムアウトする（通常よりもサーバ起動にかかる時間が長くなる）。__復旧後に手動で`mount`しなおさなければならない__。

## マウント中
`hard`マウントしていれば無限リトライし、復旧後はなにごともなかったかのように処理される。NFSファイルシステムにアクセスするプロセスはフリーズしたような状態となる（IOエラーにならず、典型的には`stat`関数が実行中のままになる）。このため、多数のプロセスが起動したままとなり、クライアント側リソースが食いつぶされていくことになる（例えば`df`コマンドもフリーズする）。
対応として`soft`マウントすればIOエラーで異常終了させられるが、推奨はされていない（ファイルシステムの一貫性が大事）。どちらがよいかはアプリケーション設計も含めた決断になる。

## アンマウント時
`stat`がフリーズするので、`umount`もフリーズする。`umount --force`でタイムアウトまで待つか、リブートする前提で`umount -l`で即時lazyアンマウントするしかない。

## OS停止時
マウント・アンマウントもsystemd制御なので`umount`が90秒でタイムアウトしてそのままパワーオフすると想定される（少し時間はかかるがパワーオフはできる）。

# NFSサーバダウン時の影響（autofs編）
特にOS起動時にNFSサーバがダウンしていた場合、手動での回復が運用的にNGであれば、`autofs`サービスを使うのが一つの解となり得る（`yum install autofs`）。

## セットアップ
`autofs`の構成はマスターとマップと呼ばれる2つのファイルからなる。
デフォルトでは"/etc/auto.master.d"がドロップインディレクトリなので、ここにファイルを追加する。

```ini:/etc/auto.master.d/nfs.autofs
/-      /etc/auto.master.d/auto.nfs -t 10 rw
```

書式は、`マウントポイント (マップタイプ,フォーマット)マップファイル オプション`となる。マップタイプはデフォルトが"file"で、ほかには"program"や"ldap"なども指定できる。オプションにはマウントオプション（"rw"など）等を指定できる（"-"つきオプションはautofsに対するもので、"-"なしは`mount -o`に渡される）。
ダイレクトマッピング（個別のディレクトリをフルパスで管理）する場合、マウントポイントには"/-"を指定する。そうでない場合、当該ディレクトリ配下は"autofs"の管理下となる（マウントされない限り配下には何も見えない状態）。

```ini:/etc/auto.master.d/auto.nfs
/tmp/work       db1:/work
```

マップファイルの書式はマウントポイントディレクトリとマウントオプション（上記例では10秒アクセスがなければアンマウントし、書き込み可能）、マウントするデバイスを指定する。
`systemctl start autofs`, `systemctl enable autofs`しておく。

## OS起動時
起動直後に`df`しても、指定したNFSファイルシステムはマウントされていない。当該ディレクトリにアクセスした際にオンデマンドでマウントされ、一定時間（デフォルト300秒で`-t|--timeout`オプションでオーバーライド）アクセスがなければリソース解放（アンマウント）してくれる。
というわけで、これを使えばOS起動時にNFSサーバがダウンしていたとしても、対象ディレクトリにアクセスしない限りは問題ない。

## マウント中
fstabでマウントしたときと同様になる（アクセス中プロセスは無限リトライでフリーズし、復旧後に継続処理）。

## アンマウント（タイムアウト）時
fstabでマウントしたときと同様になる（NFSサーバ復旧後にアンマウント）。

## OS停止時
これが厄介で、シャットダウンシーケンスが止まってしまう（autofs自体はデフォルトユニットファイル定義180秒タイムアウト、そのあとのumountで止まると推測）。これを避けるために、以下のようにNFSマウントを強制的に外す処理を入れておくのが吉。

```ini:/etc/systemd/system/umountnfs.service
[Unit]
Before=shutdown.target
[Service]
ExecStart=/usr/bin/umount -lat nfs4
[Install]
WantedBy=shutdown.target
```

これで一応はきれいにシャットダウン可能。
とにかく、NFSを使う以上、いろいろな意味でそれなりの覚悟が必要だということ。
NFSサーバ自体に高可用性を確保するか、必要な時に`mount`して使い終わったら`umount`するか、`autofs`を使うか、、、できれば使わなくて済むのであればそれに越したことはない気もする。


# 参考
+ [RHEL公式ドキュメント](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/storage_administration_guide/nfs-autofs)
+ `man nfsstat`
+ `man nfs`
+ `man exports`
+ `man exportfs`
+ `man auto.master`
+ `man automount`