本当は怖いLinuxのNFS?

# はじめに
## 注意
この話はフィクションであり、現実に起こるかどうかウラはとっておりませんので、悪しからずご了承ください。
あと、多分フツーにNFSを使ってる人には関係ない話じゃないかな…? と思います。
※システム運用の描写が嘘っぽい可能性がありますが、悪しからずご容赦ください。

## 概要
特殊なファイルシステムのネットワーク公開用にNFSサーバを立てている場合、条件によって深刻なトラブルへ波及するかも…? というお話です。

# ストーリー
## プロローグ
朝、出社したA氏は愕然としていた。
先に出社している後輩たちが慌ただしく動いている。システムXで重大なトラブルが発生したのだと。
A氏はこのシステムの保守担当チーフエンジニアであった。聞けば、大容量ファイルシステムの全系ダウンが発生し、それに引きずられる形で連鎖的にサービス停止に至ったらしい。
「全部止まった…だと?? どういうことだ!?」居てもたってもいられず、A氏は現地へ飛ぶことにした。

## 1章: トラブル発生
システムXは、客先のマシン室におさめられたLinuxベースのシステムだ。
主に研究業務を目的としていることで膨大なデータを扱う必要性があり、高性能な大容量ファイルシステムが中心的な存在となっている。ここのデータは、他システムにもNFSやCIFSを通じて提供している。
研究業務ということから、24時間365日のミッションクリティカル性が求められているわけではない。それなりに大きなシステムゆえ、常駐の保守員を手配しているが、あくまで営業時間帯だけだ。とは言え、だからといって簡単に停止していいわけがない。
そのため、中核であるファイルシステムは、( 性能的なスケールもかねて ) 4重化を行い、一部がダウンしても縮退するだけでサービス継続するはずだった。

と、A氏が現地に到着すると、すでに保守員が復旧を進めていた。幸いにもハードウェア障害やファイルシステム破損があったわけではないらしい。OSも動いてはいたが念のため再起動を行い、それだけで復旧できそうな見通しだった。A氏はとりあえず胸をなでおろした。
しかし、客先担当者への報告と善後策の検討が待っている。まだまだ気の重さが解消されたわけではなかった。

どうやら、発端は前日深夜、1台のファイルサーバのサービス停止にあったらしい。それから大体1～2時間おき位で他のファイルサーバも順々にサービス停止となり、結果全系ダウンへ至ったようだ。
今後の対応方針としては、ひとまず動作試験をパスしていることからサービス再開で様子を見ることと、ストレージベンダーに送付した各種ログからの解析結果を待つこと、保守・運用の観点から原因を探ること、が決定された。

##2章: 深まる謎
さて、A氏の側でも状況の分析と原因の推測を始めていた。
何らかのトラブルがあったことはすぐにログから見て取れた。システムの監視を司り、障害時にサービスを他ノードに引き継ぐためのサブシステムが、NFSサービスの無応答を検出していたのだ。このサービスは、大容量ファイルシステム上のデータを他システムへ提供するためにあり、Linux kernelのnfsdをそのまま使用している。
ベンダーからの1次回答にも、「nfsdの不応答が原因と考えられるため、OSやネットワークの状況に問題がないか確認してほしい」とあった。
「ファイルシステムに問題があったからnfsdが巻き込まれたんであって、話が逆じゃないのか…?」とA氏は思ったが、そこがポイントの1つであることは確かではある。
NFSのサービスができないような状況であれば、ファイルシステムとしても機能できないということであろう。ファイルサーバがサービスダウンに至ったのは、この監視サブシステムの判断によるものと分かった。
不幸だったのは、ダウンしたサーバの復帰ポリシーを安全寄りにしていたことであろう。つまり、サーバが4台もあるわけだから、トラブルを抱えたままのサーバを自動で復帰させるよりは、後でチェックを終えてから手動で復帰させる想定になっていた。そのため今回のようなまさかの4台連続トラブルでは、逆にそれがあだとなったということだ。

しかし、サーバー上のログ(syslogやjournal)を見ても、sarによるシステム統計(`/var/log/sa/`以下のファイル)をあたっても原因が分からない。CPUもメモリも余裕があり、I/Oも軽いという程ではないが十分通常レベルだ。
とは言え1つ分からない点があった。それは、ロードアベレージが徐々に上昇していき、一定のラインに到達したところでサービスダウンとなっているように見えることだった。ロードアベレージとは実行待ち、ディスクI/O待ちのプロセス数の指標である。CPUやI/Oが逼迫していない状況で上がるのは不自然だ。
「…どのプロセスが待ちになっているのか分からないと判断しようがないか」A氏はベンダからの詳細な回答を待つことにした。「いっそ日中帯に再発すれば分かるかも知れないな」という考えがふと頭に浮かび、流石に不謹慎だなと振り払った。
今にして思えば、プロセスの状況を残すために、topコマンドをバッチモード(`-b`オプション)で実行させてログに残すという方法があったのだが。
そして、原因がつかめないまま、2回目のトラブルを迎えることになる。

## 3章: トラブル再発!
ベンダからの2次回答もなく、事態が進展しないまま数日過ぎたある日の日中。現地の保守員から緊急の連絡が入った。なんでも、ファイルサーバの1台がサービス停止し、フェイルオーバ ( 他サーバへの機能引継ぎ ) が発生したというのだ。
「まさか…。再発か!?」だとすると、今の内に確かめる必要がある。サービス停止した1台は、サービスを無効化した状態で再起動をかけ状態をチェックするよう指示し、A氏は別のサーバに次々とログインした。停止した分のサービスを引き継いだサーバの状態を見るためだ。
どのサーバが引き継いだかは、サービス用のフローティングIPアドレス ( 機器固有のアドレスとは別にサービス用に用意され、別の機器に引き継げる想定のアドレス ) が設定されているかどうか、ipコマンド(`ip a`)で見れば分かる。
「…これだ」A氏はフェイルオーバー先のサーバを突き止めると、すぐさま top コマンドでの出力のモニタを始めた。まずはロードアベレージ、これは直近1分、5分、15分での待ちプロセス数平均が分かる。この3種類の数値を見比べることで、待ちプロセスが増えているか減っているか傾向が分かることになる。果たして、待ちプロセスは増えつつある状況だった。
「やはりこの前と同じか。でも何が?」続いて メモリ使用量、CPU使用率も確認する。こちらはやはりまだまだ余裕があるようだ。I/O待ちの場合にはCPU使用率の wa ( iowait ) の数値が高くなるが、そういう傾向も見えない。もっとも、特定のCPUコアのみ iowait が上昇している場合は平均だけ見ても分からないので、CPUコア別の負荷状況表示で見るよう注意が必要だ。そんな中、A氏の目がある表示に吸い寄せられた。
「Dステータス…? nfsdが?? 幾つも???」A氏が着目したのは出力の S欄 ( プロセスステータス ) だった。この欄に現れるのは、通常はCPUを消費している実行中の R ( running )、一般的な待ちを表す S ( sleeping ) 位で、D はたまにしか見かけない。これは uninterruptible sleep、「シグナルでの割り込み不可の待ち」状態で、大抵はディスクI/O待ちが該当する。つまり、I/Oが厳しい状況でないと大量には現れないはずなのだ。
しかし現実には、D ステータスの nfsd プロセスが幾つもある。どうやらこれがロードアベレージを引き上げている原因ということは分かった。しかしCPU使用率はI/O待ちという状況を否定している。
「そうだ! 何で待っているか見ないと!」topコマンドの表示項目にWCHANを追加してみる。これは Linux の kernel レベルでの待ち処理(関数名)を表すものだ。SステータスのnfsdのWCHAN欄は svc_wait となっていて、これはNFSの仕事がない状況を表すものだから不思議はない。問題はDステータスのnfsdだ。「…なんだこの処理は?」そこには見慣れない関数名があった。「短く切られるから良く分かんないんだよなあ」とぼやきつつも、その関数名を控えておく。Dステータスのnfsdは全てそこで待っているようだった。

と、しばらく経ったところで妙なことが起こり始めた。急にDステータスのnfsdの数が減ったのだ。同時にロードアベレージも下がり始めた。しかし、A氏含め何か処置を試した覚えはない。A氏は訝しんだ。とは言え、前回のように全系ダウンへ突き進むよりは遥かにマシか、と思い直すことにした。
再発したトラブルは、結局このまま収束した。

## 4章: つながった線

よく分からないうちに収束したものの、事象再発中の状況がモニタできたことで、1つの仮説が浮かんでいた。少なくとも、ロードアベレージの高騰はDステータスのnfsdの状況を反映したもので、動作している全ての nfsd が Dステータスに至ったとき、新規のNFSリクエストに対応できなくなり、監視サブシステムのチェックに引っかかったということだ。問題は、なぜ次々Dステータスになったかというところだが…。意外なところから重要な情報が得られることとなる。

トラブル再発の少しあと、客先から「NFSでデータ利用しているユーザの1人から、特定のファイルのアクセスへの応答が全然返ってこなくなったという申告があった」と連絡が来たのだ。ファイルを読もうとして、コマンドが応答不能になってしまったと。アクセスを試みた時刻は分からなかったが、ダウンしたマシンのロードアベレージが上がり始めた時刻にほぼ近いようだ。
「まさか応答がないからと何度もアクセスを試みてDOS状態を引き起こしたのか…? いや、ユーザは放っておいたと言うから違うか…。いや、このファイルは…。そうか!」

応答のないNFSアクセス、徐々に上昇するロードアベレージ、幾つものDステータスのnfsd、その原因となったファイル…。A氏の中で全てがつながった。

## 5章: 謎解き

ユーザがアクセスしたというファイル、それは大容量ファイルシステムがディスクとテープの階層型であることと深いかかわりがあった。A氏は、そのファイルがある領域が、かつてディスクからテープに移行したものだったことを覚えていたのだった。
その領域にあるデータは、一旦テープから読みださないとアクセスできない。ところが、テープは単純なデータ転送速度こそ遅くはないものの、様々な要因で時間がかかるものだ。テープは、ライブラリに大量にあるカートリッジから該当するものをロボットが選んでロードする必要があり、しかも、ランダムアクセスはできないので、ファイルの収められている位置まで早送りする時間もかかり、更に心臓部であるテープドライブの数は限られるので、データアクセス要求が幾つもたまった場合、後続の要求に応答があるまでかなりの時間待たされることがある。
今回、NFSを通じての大容量ファイルシステムへのアクセスが、テープ領域への読み出しを引き起こし、そのテープのアクセス遅延がユーザに見えたのだろうとA氏は推測した。

しかし、高々1ファイルのアクセス遅延で幾つものnfsdがステータスDという待ち状態になるのは不自然に思えるかもしれない。1プロセスだけが待つのならともかくも、だ。
ここにNFSのプロトコルの特性が関係してくるとA氏は睨んだのである。

NFSは、その下層にRPC(Sun-RPC)というプロトコルが介在し、ネットワークを通じてのやり取りが発生するようになっている。
ここでもしネットワークの不調により応答が間に合わない場合どうするか。NFSクライアントは改めてRPCリクエストを生成し、サーバへ送信する。それでも応答がなければ、それはNFSマウント時のオプション hard,soft いずれかによって変わるが、通常は hard が選択されるので、応答があるまで延々リクエストの再送を行う。soft であれば適当なところで打ち切るが、その場合データの整合性に不安が出るからだ。
問題は、今回のようにNFSの下層のファイルシステムの応答遅延がある場合だ。別にネットワーク的に問題がないにも関わらず、nfsdが応答を返せないためにクライアントから次々にRPCリクエストが来る。しかしRPCリクエストはそれぞれ独立なものであるため、同じファイルへのアクセスであっても、LinuxのNFSサーバサブシステムは、RPCリクエストへの対処を新たに別の nfsd へ割り当てる。
つまり、NFSクライアントがRPCリクエストを再送する度、サーバ側のnfsdが次々と同じファイルへの対処に追われ、いつまでもデータが読み取れず Dステータスに変わることになる。そのペースは標準的なクライアントの設定であれば、1～2分毎に1プロセスになると推測できる。

厄介なことに、Dステータスであるから、外部からの干渉では決して状況を解消することはできない。たとえnfsdの再起動を行ったとしても、依然Dステータスを解消することはできない。あくまでファイルシステムからの応答がないとだめなのだ。もちろん、OS毎再起動するか、あるいはファイルシステムの方で何らか処理を止める方法があれば、それで解消することもできるだろうが。
前回のトラブルでは全系停止になるほどの長時間のアクセス遅延が発生していたのが、今回はたまたま適当なところでデータが読み出せ、nfsdのDステータスも解消したということなのだろう。

結果的に、ネットワーク的なトラブルに対処するためのNFSのリクエスト再送という仕組みと、RPC上同一の処理に対しても独立したリクエストになるという特性がかみ合わず、nfsdのプロセス枯渇へつながったということになる。

## エピローグ

A氏はベンダにモニタした状況を伝えた。そして返ってきた詳細な回答は、A氏の推測を裏付けるものだった。ちょうど、topコマンドで見た不明なWCHANの内容は、ファイルシステムモジュールの処理に合致するものであった。
ベンダからの回答には、ファイルシステム内での応答タイムアウトに関わるパラメータについての情報もあった。適切なタイムアウト値を設定すれば、クライアントからのリクエスト再送はそのままであるにしても、nfsd が一旦処理に区切りをつけ、Dステータスのまま蓄積していくことを防げるだろう。

いずれにせよ、テープへのアクセスが重なった場合を考慮する必要はあるだろうが、これで大分症状が緩和されるだろう。A氏はひとまず胸をなでおろしたのだった。

# あとがき

この話は、NFSに関するkernelのソースを眺めていた時に、ふと「NFSの下層のファイルシステムが大幅に応答遅延を起こしたらどうなるんだろう」と考え、そこから引き起こされるトラブルを想像して書いたものです。
ひょっとしたらこんな酷いことにならないように、色々対策が ( ファイルシステムであったりkenrelなりで ) 入ってるのかも知れません。kernelソースも全然読み切れていませんから、私が気付いてないだけという可能性もあります。
※なので、kernelに詳しい方に「こういう対策があるから大丈夫!」という指摘を是非頂きたいところです

あくまでフィクションなので、「こういうこともありえるのかな～」位の軽い気持ちで捉えて頂ければ幸いです。