Skip to content

NFS Client Mount 疑難排解與最佳實踐

Linux 主機作為 NFS client 時的完整排查流程:mount 驗證四維度、fstab 選項深度解析、umount/remount 正確操作,以及 HA failover 期間的行為說明。

概述

NFS client 掛載的常見誤判是「ls 看起來是空的就認為 mount 失敗」——實際上 NFSv4 剛建立 session 時存在短暫的 dentry cache race,幾秒後重試就正常。診斷 mount 是否成功,需要交叉驗證 mount table、stat device number、nfsstat 協商資訊,以及實際 IO 四個維度,任一單一訊號都可能誤判。

fstab 的選項設計同樣有許多細節:_netdev 告訴 systemd 等網路就緒後才 mount、nofail 讓開機不因 NFS 失敗而卡住、hard 確保資料安全(預設值,不應改為 soft)。timeo 的單位是 0.1 秒而非秒,許多人因此設了過於激進的值。NFS remount 不允許修改連線相關參數,套用新 fstab 必須走 umount + mount 流程。

本文件處理任何 Linux 主機作為 NFS client 時遇到的問題。NFS server 本身(HA cluster)的故障排查見 DRBD + Pacemaker NFS HA Cluster 完整復原 Runbook

核心內容

Mount 成功驗證四維度

單一訊號容易誤判,建議交叉比對:

bash
# 維度 1:mount table 有這筆
mount | grep /mnt/nfs_share
findmnt /mnt/nfs_share

# 維度 2:stat 的 device number 與 rootfs 不同
stat /mnt/nfs_share | grep Device
stat / | grep Device
# 兩個 Device 值必須不一樣

# 維度 3:NFS 層協商資訊
nfsstat -m | grep -A5 /mnt/nfs_share
# 應看到 vers=4.2, proto=tcp, addr=10.248.36.109

# 維度 4:實際 IO
touch /mnt/nfs_share/.probe_$$ && rm /mnt/nfs_share/.probe_$$
echo "IO works: $?"

四個都通過 = mount 百分百成功。三個通過一個可疑 = 可能是 dentry cache race,等幾秒再試。

Mount 後看起來是空的 — 診斷流程

START


mount | grep <mount-point>  +  findmnt <mount-point>

  ├── 都看不到 → mount 沒成功,查 dmesg + mount 指令的 exit code(見下節)

  └── 看得到 NFS 條目 → mount 成功,繼續


    stat <mount-point> vs stat /  比對 Device 欄位

      ├── Device 相同 → mount 雖在 table 但未真正生效(kernel bug / namespace 問題)

      └── Device 不同 → mount 生效,繼續


        ls 是空的?
          ├── a) NFSv4 dentry cache benign race → 等 5 秒重試
          ├── b) server 端本身就是空的 → 去 server 端 ls 確認
          └── c) NFSv4 pseudo-root 設定問題(本環境不適用,僅供參考)

Mount 真的沒成功的診斷

bash
# 重新執行 mount 並看 exit code
sudo mount -v 10.248.36.109:/mnt/nfs_share /mnt/nfs_share
echo "exit: $?"

# kernel 層錯誤
sudo dmesg -T | grep -iE 'nfs|rpc' | tail -20

# 網路可達性
ping -c 3 10.248.36.109
nc -zv 10.248.36.109 2049          # NFSv4 只需 tcp:2049

# server export 設定
showmount -e 10.248.36.109
ip -br addr show | grep UP

常見失敗原因

症狀根因修復
access denied by serverclient IP 不在 exportfs clientspecserver 端改 /etc/exports + exportfs -r
No such file or directoryserver 路徑寫錯,或 server 端 fs 未 mount確認 server 端 ls /mnt/nfs_share 有內容
Connection refusedVIP 未起來,或 nfs-server 未啟動回 HA cluster runbook
operation not permitted缺 sudosudo mount ...
卡住不回應firewall 擋、或 VIP 正在 failovertcpdump -i any host 10.248.36.109 看封包

fstab 選項深度解析

_netdev(極重要)

告訴 systemd 等 network-online.target 完成後才 mount。沒加的話,systemd 可能在網路就緒前嘗試 mount,失敗後被 nofail 靜默蓋過,開機看起來正常但 mount 點實際是空的。

驗證:

bash
systemctl show mnt-nfs_share.mount -p After -p Wants | grep network
# 應看到 network-online.target

nofail

Mount 失敗時不阻塞 systemd boot process。幾乎一定要加:NFS server 暫時不可用時,沒有此選項會讓開機卡在 90 秒 timeout。

Trade-off:mount 失敗會被靜默藏起來,需靠監控才發現。

hard vs soft(預設 hard

選項語意適合場景
hardserver 不回應時無限重試,process 會 hang 住Production,資料不能丟
softretrans 次數用完後放棄,回 I/O error 給 application監控 / 非關鍵 read

強烈建議用 hard(也是預設值)。soft 在 server 短暫故障時會直接截斷正在寫入的檔案。

timeo(單位:0.1 秒,不是秒)

初次 RPC 等待回應的 timeout。預設 600(即 60 秒)

  • 值太小(如 30 = 3 秒):網路短暫抖動就會多送 RPC
  • 值太大(600,預設):容忍抖動,但 server 真死時要等較久

NFS 會指數退避:第一次 timeo、第二次 2×timeo…上限 60 秒。推薦 150(15 秒)

retrans

soft mount 下:用完後放棄並回 I/O error。hard mount 下:每過 retrans 次就在 syslog 印一次警告,無法讓 hard mount 放棄。預設 2。改大只是降低 warning 頻率。

noatime

不更新 file access time。每次讀檔省一個 write RPC,對 read-heavy 或大量小檔案場景效益顯著。建議加,除非應用確實依賴 atime(罕見)。

Server Failover 期間的行為

NFS server 是 HA cluster,VIP 在 master2/master3 間遷移。Failover 期間(約 30–60 秒):

  • Hard mount client 會卡住所有 IO(符合預期,資料安全)
  • VIP 遷移完畢後,IO 自動 resume,不需要 remount
  • 正在寫入的檔案可能看到瞬間 EIO,檔案狀態需 application 層保證
  • 不會有資料丟失,但 read-intensive workload 建議加 retry 邏輯

NFSv3 vs NFSv4 差異重點

本環境協商到 NFSv4.2,但舊版 K8s CSI driver 可能用 NFSv3:

項目NFSv3NFSv4
Ports2049 + portmap/111 + 動態 ports只有 tcp/2049
有狀態無狀態(stateless)有狀態(stateful)
Lock 協議NLM(獨立)內建
AuthAUTH_SYS(UID/GID 直接送)支援 Kerberos
Delegation有(client 可更積極快取)

關鍵要點

  • 判斷 mount 成功要用四維度交叉驗證,不要只靠 ls 的輸出
  • timeo 單位是 0.1 秒,不是秒;預設 600 = 60 秒,不是 600 秒
  • NFS remount 無法修改連線相關參數(timeo、retrans、vers 等),必須 umount + mount
  • _netdev + nofail 是每個網路 mount 的基本配備,缺一不可
  • hard mount 在 HA failover 期間表現為 IO hang(正確的安全行為),不是故障

部署設定參考

環境參數

項目
NFS server(HA cluster VIP)10.248.36.109
Export 路徑/mnt/nfs_share
Export policyclientspec=10.248.36.0/24, mountpoint, no_root_squash, sync
協商版本NFSv4.2(server 支援,預設協商最高版本)
NFS porttcp/2049(NFSv4 only)

推薦 fstab 配置

fstab
10.248.36.109:/mnt/nfs_share  /mnt/nfs_share  nfs  _netdev,nofail,noatime,timeo=150,retrans=3  0 0
  • _netdev + nofail:開機安全
  • noatime:省流量(每次 read 不更新 atime)
  • timeo=150(15 秒):比預設 60 秒敏感,但不激進
  • retrans=3:hard mount 下只影響 syslog 頻率,適度即可
  • 不指定 vers / proto:讓協商決定(目前會是 NFSv4.2 / tcp)

套用新 fstab 配置的正確流程

NFS remount 不允許修改連線相關參數,套用新 fstab 必須走完整的 umount + mount:

bash
# 第 0 步:先 cd 離開 mount point(否則 umount 會失敗)
cd ~

# 第 1 步:確認沒有 process 在用
sudo fuser -vm /mnt/nfs_share
# 若有列出 PID,決定要 kill 還是等它結束

# 第 2 步:umount
sudo umount /mnt/nfs_share
# 若出現 "target is busy",用 lazy umount:
# sudo umount -l /mnt/nfs_share

# 第 3 步:重新 mount(會讀取 fstab 最新內容)
sudo mount /mnt/nfs_share

# 第 4 步:驗證
findmnt /mnt/nfs_share
nfsstat -m | grep -A5 nfs_share    # 確認 timeo/retrans 是新值

Busy 時的處理

bash
# 選項 A:查出誰在用,kill 或讓它放掉
sudo fuser -vm /mnt/nfs_share
sudo lsof +D /mnt/nfs_share 2>/dev/null | head

# 選項 B:Lazy umount(通常夠用,對 NFS 安全)
sudo umount -l /mnt/nfs_share
# 現有 process 繼續用舊 mount 直到關檔,新存取走新 mount

# 選項 C:Force(最後手段,可能造成正在寫入的資料不一致)
sudo umount -f /mnt/nfs_share

完整驗證 Checklist

套用新 fstab 後逐項確認:

  • [ ] findmnt /mnt/nfs_share 有輸出
  • [ ] stat /mnt/nfs_share 的 Device 與 stat / 不同
  • [ ] nfsstat -m | grep -A5 nfs_share 顯示新選項(timeo=150, retrans=3, noatime)
  • [ ] systemctl show mnt-nfs_share.mount -p After 輸出包含 network-online.target
  • [ ] ls /mnt/nfs_share | head 看得到內容
  • [ ] touch /mnt/nfs_share/.probe_$$ && rm /mnt/nfs_share/.probe_$$ IO 可寫入
  • [ ] (可選)重開機一次,確認開機後 mount 自動生效

指令速查

bash
# 查狀態
mount | grep <path>
findmnt <path>                             # 結構化輸出
nfsstat -m                                 # NFS client 協商資訊
nfsstat -c                                 # NFS client RPC 統計
stat <path>                                # Device number 對比
systemctl list-units --type=mount
systemctl show <unit>.mount

# 操作
sudo mount <path>                          # 根據 fstab mount
sudo umount <path>
sudo umount -l <path>                      # Lazy umount
sudo mount -a                              # mount 所有 fstab 中 auto 項目
sudo mount --fake <path>                   # 不實際 mount,只驗證語法

# 除錯
sudo dmesg -T | grep -iE 'nfs|rpc' | tail
sudo fuser -vm <path>                      # 誰在用這個 mount
sudo lsof +D <path>
showmount -e <server>                      # Server export 清單
nc -zv <server> 2049                       # 測試 tcp/2049 通不通

常見陷阱

fstab mount unit 名稱規則

systemd 根據 mount point 產生 unit 名稱,規則是把路徑的 / 換成 -

Mount PointUnit Name
/mnt/nfs_sharemnt-nfs_share.mount
/data/nfsdata-nfs.mount

systemctl 指令要用 unit name,不是路徑。

寫入 /mnt/nfs_share 根目錄的風險

根目錄下有 K8s PV 目錄(swiss_simulationwimap_projectsbom_alignment_* 等 production 資料)。需要在 client 上寫東西時,請建立專屬 scratch 目錄:

bash
sudo mkdir -p /mnt/nfs_share/master1_scratch
sudo chown beit:beit /mnt/nfs_share/master1_scratch

NFSv4 clientaddr

nfsstat -m 會看到 clientaddr=10.248.36.248(NFSv4 callback 用的本機 IP,kernel 自動選)。多 NIC 且路由複雜時,可能選錯導致 delegation callback 失敗,可手動指定:

fstab
nfs _netdev,nofail,noatime,clientaddr=10.248.36.248,...

相關概念

來源