Docker 挂载 NFS 卷卡死排查:stat、df 与 nfsstat 定位失联

    发布时间:2026-09-26 04:31 更新时间:2026-09-26 04:31 阅读量:0

    把 NFS 卷挂进 Docker 容器是很多站长的常规操作:日志、上传目录、备份文件放到共享存储,多台服务器都能读。但 NFS 有个特点——它是网络文件系统,一旦服务端或链路出问题,客户端上的文件操作不会立刻报错,而是一直等。表现出来就是容器里业务线程全部阻塞,页面转圈,日志不再输出,重启容器也不一定管用。这篇文章按现场排查顺序,讲清怎么用 stat、df、nfsstat 把失联的挂载点揪出来,以及事后怎么配置避免再次卡死。

    先确认现象:是业务慢,还是卡在内核里

    发现接口大面积超时后,第一步不是重启容器,而是进容器看进程状态。如果进程处于 D 状态(不可中断睡眠),说明它正卡在内核的系统调用里,常见的就是 NFS 的 read/write/stat。可以执行:

    # 进入容器(用容器名或 ID)
    docker exec -it app-web bash
    
    

    查看进程状态,关注 STAT 列是否有 D

    ps -eo pid,stat,wchan:24,cmd | grep -v grep | head -20

    如果看到大量进程 STAT 是 D,wchan 显示 rpc_wait_bit_killable 或 nfs_ 相关字样,基本可以确定卡在 NFS 上。此时容器内执行 ls 挂载目录也会一直挂着不返回,按 Ctrl+C 都未必能中断——因为这是内核态等待,信号要等系统调用返回才处理。

    另一个快速判断点:容器内 cat /proc/mounts 找到 NFS 挂载项,记下服务端地址和挂载点路径,后续排查都围绕它展开。

    用 stat 与 df 判断挂载点是否还活着

    在宿主机上(NFS 挂载通常由宿主机做,再 bind 或 volume 给容器),先对挂载点做一次带超时的 stat 测试。注意不要直接在业务目录上跑不带超时的命令,否则排查终端也会卡住:

    # 带 5 秒超时,避免排查命令自身卡死
    timeout 5 stat /mnt/nfs
    
    

    超时返回 124 说明该挂载点无响应

    echo $?

    查看挂载信息与来源

    timeout 5 df -hT /mnt/nfs

    如果 stat 返回 124(timeout 命令的超时退出码),说明对这个挂载点的元数据请求已经没有回应;如果 df 卡住或报 “Stale file handle”,则说明服务端可能重启过、导出目录变了,或者客户端与服务端的连接状态已经失效。这两个命令的区别值得注意:stat 测的是单个路径的元数据可达性,df 会遍历挂载点统计容量,对失联挂载点更敏感,有时 df 卡住而 stat 能返回缓存结果。

    同时可以在宿主机上抓一下 RPC 通信状态:

    # 查看 NFS 客户端与 mountd 的 RPC 统计
    nfsstat -c
    
    

    只显示挂载相关的 RPC 信息

    nfsstat -m

    nfsstat -c 的 retrans、timeout 计数持续上涨,说明请求在不断重传却得不到响应,属于链路或服务端问题;nfsstat -m 会列出每个挂载点的参数,重点看是 hard 还是 soft、timeo 和 retrans 是多少,这决定了失联时客户端是无限等待还是有限重试。

    hard 与 soft:卡死的根源往往在这里

    NFS 默认是 hard 挂载,含义是请求失败后无限重试,直到服务端恢复。这个设计对数据一致性友好,但对业务是灾难——服务端不回,进程就一直 D 住,容器健康检查失败、负载飙升,重启容器也解不开,因为等待发生在内核里。

    对日志、缓存、可重试的临时文件这类场景,可以考虑 soft 挂载配合合理的 timeo/retrans,让请求在超时后返回错误,业务层能捕获并降级。但要注意:soft 挂载在写操作超时后可能返回错误,存在数据不一致风险,数据库文件、关键业务数据不要用 soft。具体参数取舍以 NFS 官方文档和实际环境为准,不同内核版本行为也有差异。

    下面是一个相对稳妥的挂载示例,用 soft 加较长超时,给业务留出重试空间:

    # 挂载 NFS,soft 模式,超时 30 秒(3 秒 x 10 次重试)
    mount -t nfs -o soft,timeo=30,retrans=10,noac,vers=4.2 \
      192.0.2.10:/data/nfs /mnt/nfs
    
    

    写入 /etc/fstab 持久化,注意 _netdev 让网络就绪后再挂载

    192.0.2.10:/data/nfs /mnt/nfs nfs soft,timeo=30,retrans=10,_netdev 0 0

    如果用的是 Docker volume 或 compose 挂载,driver 层的挂载参数同样要写进去,容器内再挂一次并不会改变宿主机已有的挂载行为。容器里看到的卡顿,本质是宿主机这个挂载点卡了。

    现场处置与后续加固

    确认挂载点失联后,处置顺序建议是:先恢复 NFS 服务端或网络,让请求能返回;如果服务端短时间无法恢复,在宿主机上对失联挂载点做 umount -f 或 umount -l(lazy umount)解除关联,但要注意 lazy umount 只是把挂载点从命名空间摘掉,已有引用仍会等待,彻底清理可能需要重启相关容器。做完这一步再重启容器,通常能恢复业务。

    事后加固可以从三方面入手:一是把 NFS 挂载参数显式写进 fstab 或编排文件,别依赖默认 hard 无限等待;二是给容器配置健康检查,探测里带上对挂载目录的轻量访问,卡住时能尽早暴露;三是关键业务目录不要强依赖单点 NFS,本地缓存加异步同步往往比同步读写更抗故障。NFS 本身是成熟方案,卡死多数不是它坏了,而是失联时客户端选择了无限等待,把参数和监控配好,这类现场就能少很多。

    继续阅读

    📑 📅
    Linux服务器内存泄漏初判:RSS上涨是缓存还是泄漏 2026-09-26
    MySQL死锁日志定位实战:读懂SHOW ENGINE INNODB STATUS 2026-09-26
    Nginx 上游节点健康检查实战:max_fails、fail_timeout 与 backup 配置 2026-09-26
    MySQL主从复制中断后如何安全恢复 2026-09-25
    宿主机与容器时间不一致导致接口签名失败排查 2026-09-25
    Nginx与上游服务时间不同步导致签名校验失败排查 2026-09-25
    PostgreSQL WAL 堆积与复制槽不释放排查实战 2026-09-26
    Nginx proxy_pass 带 URI 与不带 URI:路径拼接错乱排查 2026-09-27
    Docker 容器 PID 1 与信号处理:docker stop 超时强制 kill 的成因与写法 2026-09-27
    PostgreSQL连接池选型与pgbouncer事务池踩坑 2026-09-27