Kubernetes 环境 NFS 卡死问题排查与解决纪要

Kubernetes 环境 NFS 卡死问题排查与解决纪要

一、事件背景

在 Kubernetes 集群运行过程中,xxxx 命名空间内多个业务 Pod 出现启动异常,部分 Pod 长时间处于 CreateContainerErrorContainerCreating 状态,重建 Pod、重启业务均无法恢复。

异常具有以下明显特征:

  • 同一 Deployment 中,新创建的 Pod 启动失败,而旧 Pod 仍可运行
  • 问题集中出现在 涉及 NFS 持久化存储的 Pod
  • 重启 NFS 服务器后,所有异常 Pod 立即恢复正常

初步判断:

该问题并非应用自身故障,而是 底层 NFS 存储服务在高并发挂载场景下发生阻塞


二、现象描述

1. Kubernetes 侧表现

  • 同一 Deployment 中,部分 Pod 正常运行,部分 Pod 启动失败

  • 失败 Pod 状态为 CreateContainerError

  • kubectl describe pod 的 Events 中出现:

    • MountVolume.SetUp failed
    • timed out waiting for the condition

2. NFS 侧表现

  • NFS 服务未完全崩溃,但客户端挂载请求阻塞
  • 重启 NFS 服务后,K8s Pod 可立即恢复

三、环境信息

NFS 服务器硬件规格

  • CPU:2 vCPU(KVM 虚拟机)
  • 内存:3 GB
  • 架构:x86_64
  • 用途:Kubernetes 集群共享存储(PVC)

NFS 协议

  • NFS v4

四、排查过程(时间顺序)

1. Kubernetes 侧初步排查

  • 使用 kubectl get pod 发现多个 Pod 状态为 CreateContainerError

  • 通过 kubectl describe pod 查看 Events,发现大量:

    • MountVolume.SetUp failed
    • timed out waiting for the condition

结论:

Pod 在 Volume 挂载阶段阻塞,问题指向存储层。


2. 确认问题与 NFS 存储相关

  • 排查失败 Pod 所使用的 PVC,确认均使用 NFS 后端
  • 在 Kubernetes 节点执行 df -Thls 等命令,发现 命令本身卡死无返回

结论:

NFS 挂载已在内核态阻塞,影响整个节点的 VFS 操作。


3. NFS 服务器侧日志排查

3.1 systemd / 服务日志
bash 复制代码
journalctl -u nfs-server -u rpcbind -u nfs-idmapd
  • 未发现启动失败类错误
  • 出现 subtree_check 相关 warning(非致命)
3.2 内核日志分析(关键)

/var/log/messages 中发现:

text 复制代码
rpc-srv/tcp: nfsd: got error -32 when sending ... shutting down socket
nfs-mountd.service: Main process exited, code=killed, status=9/KILL

结论:

NFS 服务在运行过程中出现 RPC 连接异常及 mountd 被强制终止 的情况,符合线程/资源耗尽导致的"假死"特征。


4. NFS 内核运行状态确认

bash 复制代码
cat /proc/net/rpc/nfsd

关键输出:

text 复制代码
th 8
proc4 ... 5701

分析:

  • nfsd 工作线程仅 8 个(默认值)
  • NFSv4 请求数量大
  • 在 Kubernetes 并发 Pod 启动、同时挂载 NFS 的场景下,线程极易被打满

至此,基本锁定问题根因。


2. 查看系统日志(/var/log/messages)

发现关键错误:

text 复制代码
rpc-srv/tcp: nfsd: got error -32 when sending ... shutting down socket
nfs-mountd.service: Main process exited, code=killed, status=9/KILL

含义说明:

  • error -32:Broken pipe,客户端连接异常
  • status=9/KILLnfs-mountd 被强制杀死(疑似阻塞/资源问题)

3. 查看 NFS 内核运行状态

bash 复制代码
cat /proc/net/rpc/nfsd

关键输出:

text 复制代码
th 8 ...
proc4 2 0 5701

分析:

  • nfsd 线程数只有 8(默认值)
  • 大量 NFS v4 请求
  • 在 K8s 并发 Pod 启动/挂载场景下极易被打满

4. 确认 systemd 服务配置

bash 复制代码
systemctl cat nfs-server

发现:

  • ExecStart=/usr/sbin/rpc.nfsd
  • 未配置线程数参数
  • 未引用 /etc/sysconfig/nfs

结论:

每次重启 nfs-server,线程数都会被重置为默认 8。


五、根因分析(最终结论)

根本原因

NFS 服务器规格较低(2C3G) + nfsd 默认线程数过小(8) + Kubernetes 并发挂载行为 → NFS 服务线程耗尽,导致挂载阻塞

佐证证据链

  1. Pod 卡在 Volume Mount 阶段
  2. NFS 日志出现 socket error / mountd 被 kill
  3. /proc/net/rpc/nfsd 显示线程数仅 8
  4. 重启 NFS 服务后立即恢复

六、处理过程与解决方案

1. 临时止血措施

  • 重启 NFS 服务:
bash 复制代码
systemctl restart nfs-server

效果:

  • 阻塞的 RPC 请求被清空
  • Kubernetes 节点可重新完成 NFS 挂载
  • Pod 启动恢复正常

说明:

该措施只能临时恢复服务,无法根治问题


2. 根因修复:提升 nfsd 线程数

2.1 确认 systemd 未配置线程数
bash 复制代码
systemctl cat nfs-server

发现:

  • ExecStart=/usr/sbin/rpc.nfsd
  • 未指定线程数参数,服务每次重启都会回退到默认 8 线程

2.2 使用 systemd override 固化线程数(最终方案)
bash 复制代码
mkdir -p /etc/systemd/system/nfs-server.service.d
vim /etc/systemd/system/nfs-server.service.d/override.conf

内容:

ini 复制代码
[Service]
ExecStart=
ExecStart=/usr/sbin/rpc.nfsd 32

生效:

bash 复制代码
systemctl daemon-reload
systemctl restart nfs-server

验证:

bash 复制代码
cat /proc/net/rpc/nfsd | grep '^th'

结果:

nfsd 线程数固定为 32,不再随服务重启回退。


3. /etc/exports 配置优化(配合性修复)

  • 明确指定 no_subtree_check
  • 移除无必要的 insecure
  • 统一 K8s 场景导出参数

该调整进一步降低了 NFS 在高并发挂载时的风险。


2. 优化 /etc/exports(K8s 推荐)

bash 复制代码
/data/mnt/k8s-storageclass *(rw,sync,no_root_squash,no_subtree_check)

应用配置:

bash 复制代码
exportfs -rv

七、效果验证

  • NFS 线程数固定为 32,不再被重启重置
  • Kubernetes Pod 启动、扩容、重启时不再出现 NFS 挂载卡死
  • 未再出现 CreateContainerError 相关 NFS 错误

八、经验总结

  1. Kubernetes + NFS 默认配置不可直接用于并发场景
  2. nfsd 线程数必须根据 CPU 核数和并发量调整
  3. 重启可恢复的问题,通常是资源/线程耗尽而非功能性错误
  4. systemd override 是比临时 echo 更可靠的生产做法

九、Kubernetes 挂载 NFS 的建议与最佳实践(补充)

为避免类似问题再次发生,在 Kubernetes 场景中使用 NFS 时,建议从 挂载参数、使用方式、发布策略 三个层面进行规范。


1. NFS 挂载参数建议(非常关键)

1.1 推荐使用 NFS v4

在 Kubernetes 中,强烈建议统一使用 NFS v4

  • 减少对 rpcbind / mountd / statd 的依赖
  • 状态管理更清晰,锁问题更少
  • 更适合容器化与动态调度场景

PVC / PV 中推荐配置:

yaml 复制代码
mountOptions:
  - vers=4
  - noatime
  - hard
  - timeo=600
  - retrans=5

说明:retrans 必须设置上限,避免无限阻塞导致节点被拖死。


1.2 hard / soft 的选择建议
场景 建议 说明
关键业务数据 hard + retrans 保证一致性,同时避免无限等待
日志、缓存、可重试业务 soft 避免 I/O 阻塞拖垮节点

2. Kubernetes 使用方式建议

2.1 优先使用 PV / PVC,而非直接 nfs volume

推荐:

  • 使用 PersistentVolume / PersistentVolumeClaim
  • 统一挂载参数,便于集中治理

不推荐:

  • 在 Pod 中直接定义 nfs volume(不利于维护与审计)

2.2 避免大量 Pod 同时挂载同一 NFS
  • Deployment / StatefulSet 发布时控制并发

  • 合理设置:

    • maxUnavailable
    • maxSurge
  • 避免一次性 scale 大量 Pod

大量 Pod 同时 mount NFS,是触发 nfsd 线程耗尽的高风险行为。


3. 节点与运维层面的防护建议

3.1 避免运维命令被 NFS 拖死
  • 查看磁盘时优先使用:
bash 复制代码
df -Th -x nfs -x nfs4
  • 或:
bash 复制代码
findmnt -t nfs,nfs4

3.2 挂载异常时的处置建议
  • 单节点出现 NFS 阻塞时,优先考虑:

    • Pod 迁移
    • Node 重启
  • 避免在阻塞节点上反复执行 df / ls / du 等命令


4. 架构层面的长期建议

  • 若业务规模持续扩大,不建议长期依赖单点 NFS

  • 可考虑:

    • 升级 NFS 服务器规格(≥4C8G)
    • 引入分布式存储(Ceph / Longhorn 等)

十、/etc/exports 配置问题分析与优化方案(补充)

1. 事发时原始配置

bash 复制代码
/data 192.142.8.89(rw,sync,no_root_squash)
/data 192.142.8.88(rw,sync,no_root_squash)
/data 192.142.8.91(rw,sync,no_root_squash)
/data 192.142.8.92(rw,sync,no_root_squash)
/data 192.142.8.26(rw,sync,no_root_squash)
/data 192.142.8.27(rw,sync,no_root_squash)
/data 192.142.8.28(rw,sync,no_root_squash)
/data/mnt/k8s-storageclass/ *(insecure,rw,sync,no_root_squash)

2. 存在的问题分析

(1)未显式指定 no_subtree_check
  • NFS 默认行为依赖 nfs-utils 版本
  • K8s 场景下目录频繁变化(PVC 动态创建)
  • 容易导致 mount 阶段阻塞甚至假死
  • 日志中已出现多次相关 warning
(2)使用了 insecure 参数
  • 允许客户端使用高端口访问
  • 对 Kubernetes 节点无实际必要
  • 增加攻击面,不利于安全收敛
(3)导出策略不统一
  • /data 按 IP 精确控制
  • /data/mnt/k8s-storageclass 对所有主机开放
  • 在 NFSv4 状态管理下存在潜在一致性风险

3. 优化后的推荐配置(K8s 稳定版)

bash 复制代码
/data 192.142.8.89(rw,sync,no_root_squash,no_subtree_check)
/data 192.142.8.88(rw,sync,no_root_squash,no_subtree_check)
/data 192.142.8.91(rw,sync,no_root_squash,no_subtree_check)
/data 192.142.8.92(rw,sync,no_root_squash,no_subtree_check)
/data 192.142.8.26(rw,sync,no_root_squash,no_subtree_check)
/data 192.142.8.27(rw,sync,no_root_squash,no_subtree_check)
/data 192.142.8.28(rw,sync,no_root_squash,no_subtree_check)

/data/mnt/k8s-storageclass *(rw,sync,no_root_squash,no_subtree_check)

应用方式:

bash 复制代码
exportfs -rv

4. 参数选择说明

参数 说明 选择原因
rw 读写 K8s PVC 必需
sync 同步写入 稳定性优先,避免 IO 堆积
no_root_squash root 映射 容器内 root 访问需要
no_subtree_check 禁用子目录校验 K8s 官方推荐
insecure ❌ 不使用 K8s 不需要

5. 调整效果

  • NFS 启动日志中不再出现 subtree 相关警告
  • Kubernetes Pod 并发启动时 mount 行为明显更稳定
  • 配合 nfsd 线程数提升,未再复现卡死问题

相关推荐
阿里云云原生2 小时前
快速构建企业 AI 开放平台,HiMarket 重磅升级
云原生
谷隐凡二4 小时前
etcd在Kubernetes中的作用简单介绍
数据库·kubernetes·etcd
m0_569531014 小时前
《K8s 网络入门到进阶:Service 与 Ingress 原理、部署方案及核心区别对比》
网络·容器·kubernetes
眠りたいです6 小时前
Docker:容器虚拟化技术基础-namespace,cgroups,资源管理与LXC
运维·docker·中间件·容器
阿里云云原生6 小时前
AgentScope x RocketMQ:打造企业级高可靠 A2A 智能体通信基座
云原生·apache·rocketmq
新手小白*7 小时前
K8s 中的 CoreDNS 组件
云原生·容器·kubernetes
一周困⁸天.7 小时前
K8S-CoreDNS组件
网络·kubernetes
Selegant7 小时前
告别传统部署:用 GraalVM Native Image 构建秒级启动的 Java 微服务
java·开发语言·微服务·云原生·架构
speedoooo7 小时前
未来的App不再需要菜单栏?
前端·ui·容器·小程序·web app