NFS:リモートファイルの一貫性、ステータス、およびリカバリ

このページの目次

クライアントがタイムアウトしても、サーバー側ではすでにファイルが作成されている可能性があります。WRITE が成功しても、内容がまだ永続化されていない可能性があります。NFS は、⁠リクエストの識別、キャッシュの検証、永続化の保証をそれぞれ追跡し、どのように再送するかを決定する必要があります。

説明すべき現象参照箇所
複数の操作を 1 つの RPC にまとめると、一括でロールバックされるか?第 1~2 節:バージョンと COMPOUND
他のクライアントはいつ変更を認識するか?第 3 節:キャッシュと delegation
クラッシュ後はデータを再送するか、それとも再確認するか?第 4 節:WRITE/COMMIT の推論
タイムアウト、リカバリ、セキュリティはどのように位置づけられるか?第 5~7 節:待機、レイアウト、読み取り専用観測

1. VFS、RPC、およびバックエンドファイルシステム

Linux クライアントの fs/nfs/ は VFS に接続し、net/sunrpc/ は ONC RPC 関連の機能を提供します。カーネルサーバー側の fs/nfsd/ はリモート操作を処理し、エクスポートされたバックエンドにアクセスします。ONC RPC はリモート呼び出しフレームワークであり、XDR はデータエンコーディングを定義します。TCP、UDP、または RPC-over-RDMA がその伝送手段であり、SUNRPC と TCP を同じ層の伝送プロトコルとして並列に扱うことはできません。

NFS の filehandle はサーバー側のファイルオブジェクトを識別するものであり、クライアントの fd でもなく、単なる絶対パス文字列でもありません。rename は名前を変更してもオブジェクトの識別子を保持します。削除と再作成、エクスポートの変更などは、古い handle を無効化し、ESTALE を返す可能性があります。ESTALE に遭遇した場合は、オブジェクトとエクスポートのライフサイクルを分析すべきであり、単に再送回数を増やすべきではありません。

バージョン主要なメカニズム境界
v3読み書き、属性などの基本操作;通常、補助的な MOUNT プロトコルを使用して初期 handle を取得コアプロトコルは比較的ステートレスであり、サーバーにキャッシュやロックサービス、リカバリ要件がないことを意味するものではない
v4.0OPEN/LOCK、リースと delegation、COMPOUND、統一されたエクスポートビューステートフルであり、リカバリと取り消しを処理する必要がある
v4.1session、slot/sequence、pNFS などリプレイ保護には具体的なセッションおよびキャッシュの境界がある
v4.2サーバー側コピー、スパースファイルなどの機能が増加バージョンのネゴシエーションが、すべてのオプション操作が利用可能であることを意味するものではない

v4 では通常、プロトコルのエクスポートルートから直接トラバースするため、v3 のような補助的な MOUNT プロトコルは不要です。クライアントに見えるパスはサーバーのエクスポートビューであり、サーバー側のローカルパスと一致するとは限りません。新しい設定では -t nfs を使用し、必要に応じて vers= を指定してください。古い nfs4 ファイルシステムタイプの表記は推奨されません。Linux nfs(5) を参照してください。

2. COMPOUND は往復を減らすものの、データベーストランザクションは提供しない

「オブジェクトの特定、オープン、属性の読み取り」などの複数の操作を COMPOUND にまとめると、ネットワークの往復を減らすことができます。サーバーはプロトコルに従って実行し、結果を報告しますが、後続の失敗が、前面で既に発生した効果の自動ロールバックを意味するわけではありません。したがって、1 つの RPC 内の複数の操作を、すべてまたは無しのビジネストランザクションとして扱うことはできません。

v4.1 の session は、slot および sequence などの情報を使用して再送を識別し、結果を再利用することで、重複実行のリスクを低減します。これは、新しいセッションで任意のリクエストを再送しても自動的に重複排除されるものではなく、ファイルシステム外のビジネスロジックにおける重複排除を代替するものでもありません。再起動、ステータスの喪失、および応答キャッシュの制限については、プロトコルに従って処理する必要があります。v4.1 の更新仕様については RFC 8881 を参照してください。これは RFC 5661 に取って代わるものです。

session による再送と新規リクエストの区別:1 回の応答喪失の場合

クライアントが有効な v4.1 session の slot 3 で sequence 8 を送信し、リクエスト R がファイルの作成を行うと仮定します。プロトコルに従って応答をキャッシュします。サーバーは正常に実行し、この slot に対応する結果を記録しますが、応答が途中で失われます。クライアントが直面する現象はタイムアウトですが、実際にはファイルはすでに作成されています。

クライアントの後の動作サーバーはどのように区別すべきか
同一 session、slot 3、sequence 8 で同じ R を再送再送を識別し、応答キャッシュの条件に従って以前の結果を返す
結果を受け取った後、slot 3 で sequence 9 に進めるその slot の次の作業
別の slot でリクエストを送信別のリクエスト位置を並行して占有可能

slot は並行処理の位置を提供し、sequence は各位置での前後のリクエストを区別します。これらは、すべてのビジネス ID を永久に保存する無限の重複排除データベースではありません。

クライアントがセッションを再構築する場合、または元の応答が所定の条件で保持されていない場合は、同じファイル名を以て上記のリプレイ保護を享受できるとは限りません。関連するエラーは、プロトコルで規定されたリカバリフローに入る必要があります。これが、「RPC には再送メカニズムがある」と「任意のビジネス操作を安全に再送できる」の間にはまだ距離がある理由を説明しています。

部分的な成功:作成完了後、属性の変更は依然として失敗する可能性がある

COMPOUND のもう一つの境界は、作成後の属性変更で理解できます。前面の OPEN で作成に成功したが、後続の SETATTR が権限エラーで失敗した場合、戻り値は項目ごとに解釈する必要があります。ファイルは既に存在している可能性があり、COMPOUND 全体の最後のエラーを単に「何も行われなかった」として翻訳することはできません。リプレイキャッシュはこの実行結果を保存しますが、前面で完了した操作をロールバックする能力とは異なります。RFC 8881 の Session と COMPOUND のセマンティクス を参照してください。

3. キャッシュ一貫性:誰が、いつ、何を検証したか

クライアントはファイルコンテンツ、属性、および名前解決の結果をキャッシュできます。属性キャッシュの有効期限は再検証のタイミングを制御し、データの有効性は変更属性などの判断に影響されます。close-to-open の典型的な意図は、書き込み側が閉じる前に変更を反映させ、その後、別のクライアントがオープンした際に変更をチェックすることです。これは、すべてのオープン済み fd および mmap マッピングがリアルタイムで同じメモリを共有することを意味するものではありません。

2 つのクライアント:再オープンと fd の継続的な保持は異なる

順序これに基づいて分析できること
A が書き込みし、必要な同期/クローズを完了A は変更を反映させた
B がその後再オープンし、通常のポリシーに従って検証B は変更の検出を通じて更新を確認する機会がある
B が fd を保持し続け、キャッシュを繰り返し読み取るA の write 成功だけでは、B が次回必ず更新を見ることを推論できない

複数の書き込み手が重複して書き込む場合、依然としてロックまたはアプリケーションプロトコルを共通で遵守する必要があります。NFS クライアントは O_APPEND をシミュレートする場合があり、並行して追記する場合、位置決めと書き込みの間に競合が発生する可能性があるため、ローカルな追記の直感をそのまま適用することはできません。

「すべての open に対して GETATTR が 1 回送信される」というのも絶対的ではありません。キャッシュ、delegation、プロトコルの組み合わせ、およびマウントオプションが実際のリクエストに影響を与えます。noac はすべてのページキャッシュを無効にする統一スイッチではありません。属性キャッシュの有効期限を短縮しても、マルチオペレーショントランザクションが凭空に生成されるわけではありません。

Delegation は回収可能な権限

サーバーは適切な条件下で読み取りまたは書き込みの delegation を付与し、クライアントが許可範囲内でより多くの操作をローカルで処理できるようにします。読み取り delegation がグローバルに唯一のリーダーを意味するわけではありません。書き込み delegation が、すべてのコンテンツがすでにキャッシュされており、ローカルでのすべてのアクセスが永遠に RPC 0 であることを意味するわけではありません。

競合が発生した場合、サーバーは通常まず recall を行い、クライアントが必要な書き戻しを完了して返還します。正常に返還できない場合にのみ、取り消しとリカバリが発生します。リース、コールバックチャネル、およびサーバー側のステータスがこのプロセスに関与しており、「別のクライアントがアクセスすると即座に revoke され、旧クライアントが自然に同期する」と単純化することはできません。

4. WRITE 成功後も安定レベルを確認する

NFS 書き込み:クライアント受領・サーバー受信・永続化は別 クライアント A NFS サーバー 安定したストレージ WRITE(UNSTABLE を要求可能) 応答:実際の安定レベル + verifier 必要に応じて COMMIT 必要な永続化を完了 安定を確認;verifier をチェック WRITE が安定している場合、COMMIT を送信する必要はありません。サーバー再起動により verifier が変更された場合、データの再送が必要になる可能性があります。

WRITE は異なる安定レベルを報告できます。UNSTABLE は、サーバー側が必要な安定したストレージの保証を行っていないことを示し、クライアントは通常、再送用にデータを保持し、その後 COMMIT を要求する必要があります。WRITE が要求通りに安定して完了している場合、余分な COMMIT を送信する必要はないかもしれません。したがって、fsync は固定の「COMMIT のみを実行する」別名ではなく、ダーティデータの送信、結果の待機、および状態の処理も必要になる場合があります。

write verifier は、サーバー側の関連する書き込みステータスが再起動をまたいで変化したかどうかを判断するために使用されます。サーバー側が未安定データを喪失した場合、クライアントは、データなしで COMMIT を再提出するだけで内容が回復したとみなすべきではなく、プロトコルに従ってデータを再送信する必要があります。安定した書き込みの定義については、RFC 1813 の WRITE/COMMIT および v4.1 仕様を参照してください。

クライアントは成功を得たとしても、サーバー側が保証を遵守することに依存しています。async でのエクスポート、バックエンドの同期ポリシー、デバイスの揮発性キャッシュなどは、実際の障害保証を変更する可能性があります。クライアントのマウントオプションとサーバー側の exports の同期オプションは、異なる制御ポイントです。NFS は、バックエンドが永続性に対して行った誤った保証を自動的に修復しません。

2 回のクラッシュの推論:なぜクライアントは未安定データを保持しなければならないか

サーバー側のディスク内のファイルコンテンツが旧バージョン O で、クライアントが特定の範囲を N に書き込む準備をしていると仮定します。他の書き込み手は無視し、サーバー側が安定したストレージの保証を厳格に遵守し、基盤デバイスに損傷がないと仮定します。

時刻クライアントが知っていることサーバー側の状態この時点でのリカバリに必要なもの
t0:WRITE 送信N はまだ確認されていないリクエストをまだ受信していない可能性があるクライアントは N を保持
t1:UNSTABLE、verifier V₁ を受信サーバーは N を受信したが、安定の保証は行っていないN は揮発性バッファのみにある可能性があり、ディスクは依然として Oクライアントは依然として N を再送可能でなければならない
t2:COMMIT 前にサーバーがクラッシュクライアントは一時的に応答なししか見ていないかもしれない再起動後、O のみ、書き込みステータスの verifier は V₂ に変更V₂ ≠ V₁ を検出し、N を再書き込み
t3:再送し、安定確認を完了N の回復保証がようやく成立N が安定対応する安定化責任を終了できる

重要なのは t2 です:COMMIT には完全な新内容が含まれていません。クライアントが t1 で唯一の N を捨ててしまうと、COMMIT を再送してもサーバー側が O から N を推測することはできません。verifier は、「まだ前の揮発性書き込み期間か」を判定可能な条件に変えます。クライアントがデータを保持することで、真のリカバリ材料が提供されます。

サーバー側の再起動と応答の喪失をこの書き込みに個別に適用します:前者はコンテンツの再送が必要になる可能性があり、後者は単にクライアントが安定化が完了していることをまだ知らないだけかもしれません。図では、クライアントは責任を負う必要があるデータを保持し続けます。

図を準備しています
COMMIT にデータが含まれていない場合、失われた書き込みをどのように取り戻すか?

UNSTABLE 応答後、クライアントは再送材料を保持し続ける必要がある。verifier が変更された場合、COMMIT の再送のみでは失われた内容は回復できず、データの再送信が必要。

対照:データはすでに安定しており、COMMIT 応答のみが喪失

別のクラッシュポイント:サーバー側は N を安定化したが、COMMIT 応答が喪失した。この場合、再問い合わせ/プロトコルに従ったリカバリは、クライアントの不確実性を解消するためのものであり、ディスクは O に戻らない。2 つのシナリオはクライアントにとってどちらもタイムアウトとして現れる可能性があるが、処理は「成功を受信していない」ことを以て書き込みが一度も発生しなかったと見なしてはならない。この差異は、fsync がエラーを返した後にアプリケーションが結果の不確実性を考慮する理由でもある。上記はプロトコルの推論であり、サーバーが意図的に安定保証を履行しない設定は含まれていない。

5. タイムアウト、hard/soft、およびタスク待機

リクエストのタイムアウトは、クライアントがタイムリーに利用可能な応答を取得できなかったことを示すだけで、サーバー側が実行しなかったことを証明するものではない。TCP 接続の信頼性のある伝送も、サーバー側が実行後に応答が喪失したり再起動したりするウィンドウを消除するものではない。

設定制御するもの誤解してはならないこと
hard通常、回復可能なリクエストに対して継続的に再送retrans はすべてのリクエストの総再送上限ではない
soft などのポリシーより早くアプリケーションにエラーを返す可能性があるエラーを返しても、サーバー側が部分的に実行しなかったことを証明するものではない
timeo1/10 秒単位で指定するタイムアウトパラメータシステムコール全体の完了期限を表すものではない

待機時間を短縮すると、アプリケーションが遭遇する失敗の形態が変化し、無条件に固まりを解消する最適化として扱うことはできません。

一部の待機はシグナルによって中断可能ですが、他の待機は非中断リソースの待機制約に従います。TASK_KILLABLE は 5.x よりも以前から存在しています。プロセスが D 状態と表示されているからといって、「永遠に殺せない、再起動するしかない」と断言することはできず、SIGKILL がすべての NFS クリーンアップを即座に完了する保証もありません。カーネルスタック、具体的な待機ポイント、およびサーバー側のリカバリ状態を結合して分析する必要があります。

サーバー側リカバリ:リースとロックの再調整

v4 サーバーの再起動は、クライアントが以前のオープンとロックを再主張できるようにする grace period に移行する可能性があります。永続的なクライアントがデータを追跡することは、リカバリの意思決定を支援しますが、すべてのランタイムロックオブジェクトを固定パスにそのまま書き込むことを意味するものではありません。nfsd worker の数は設定可能であり、各 CPU に正確に 1 つのスレッドを強制するものでもありません。

6. pNFS とセキュリティモデル

pNFS はメタデータサービスによってレイアウトなどの情報を提供し、クライアントは使用しているレイアウトに従ってデータサーバーまたは関連するストレージターゲットにアクセスし、データパスを分散させます。これはメタデータ調整を取消すものではなく、任意のディレクトリアクセスが MDS を迂回することを保証するものでもありません。レイアウトタイプ、撤回、フォールバック、およびデバイス能力が実際のパスを決定します。

セキュリティ方式提供するもの提供しないもの
AUTH_SYS / sec=sys権限判断のために UID/GID などの身元情報を伝達暗号化しない。また、自身のみでリモートホストのユーザー宣言の真実性を検証するものでもない
sec=krb5RPCSEC_GSS 認証krb5p と同じコンテンツのプライバシーを提供しない
sec=krb5i認証と完全性保護操作内容を暗号化しない
sec=krb5p認証、完全性、およびプライバシーエクスポート権限、ファイル権限、および鍵管理に取って代わるものではない

root_squash はクライアントの root 身份をマッピングするものであり、任意の信頼できないクライアントの身份偽造に対する完全な防護ではない。NFSv4 が、現在のマウントが必ず Kerberos を使用することを意味するものでもない。実際のネゴシエートされた sec= を確認すべきである。規範的な実装要件は、そのままランタイム設定の事実として扱うことはできない。

7. 読み取り専用で現在の NFS ビューを特定する

from pathlib import Path

found = False
for line in Path("/proc/self/mountinfo").read_text().splitlines():
    left, separator, right = line.partition(" - ")
    fields = right.split()
    if separator and fields and fields[0] in {"nfs", "nfs4"}:
        found = True
        print("マウント記録:", left)
        print("タイプ、ソース、およびスーパーブロックオプション:", right)
if not found:
    print("現在の mount namespace で NFS マウントが見つからない")

出力は mountinfo のエスケープ形式を保持し、スペースを含むパスを通常の文字列として誤って分割しないようにする。さらに /proc/self/mountstats、nfsstat -c、または nfsstat -s を読み込み、RPC 数、再送、およびサービス時間を区別する。これらの指標は、同じ時間窓の差分から取得すべきである。全量の rpcdebug はデバッグ状態を変更し、大量のログを生成する可能性があるため、デフォルトの読み取り専用観測ステップとして使用すべきではない。