一个 ingress-nginx 的 geoip 文件,揭开 CSI 插件与 Alinux4 的兼容性问题
背景
将新采买的节点 alinux-node 加入到集群后,ingress-nginx-controller Pod 一直处于 ContainerCreating 状态。查看事件,发现挂载 NAS 卷失败。
查看 CSI 插件日志:
E0528 10:16:41.907584 136527 utils.go:101] GRPC error:Nas, Mount Nfs error: Failed to run cmd: mount -t nfs -o vers=3,nolock,tcp,noresvport<nas-server>.cn-beijing.nas.aliyuncs.com:/ingress/geoip/var/lib/kubelet/pods/.../mount,with out: , with error: exit status 255
在继续排查之前,先简单了解下相关组件的作用。
CSI 插件架构速览
集群中与存储相关的组件分为两大块:
| csi-plugin | ||
| csi-provisioner |
一个 csi-plugin Pod 包含 4 个容器:
csi-plugin Pod(DaemonSet,每节点一个)├── csi-plugin(主容器) ← 核心 CSI 驱动,负责 mount/umount├── disk-driver-registrar ← 向 kubelet 注册 disk 插件├── nas-driver-registrar ← 向 kubelet 注册 nas 插件└── oss-driver-registrar ← 向 kubelet 注册 oss 插件
csi-plugin 主容器 的核心职责:
NodePublishVolume:当 Pod 要使用 PV 时,负责在节点上执行实际的挂载操作 NAS: mount -t nfs ...云盘:attach 云盘 → 格式化 → mount OSS:通过 ossfs 挂载 NodeUnpublishVolume:Pod 删除时,卸载对应挂载点 健康检查:监听 /healthz端口初始化:部署 csiplugin-connector到宿主机、安装 nfs-utils 等依赖
这次报错就是 csi-plugin 容器在执行 NAS 挂载时失败的日志。
回到问题本身,挂载 NFS 返回了 exit status 255,stderr 为空。更诡异的是:
宿主机上手动 mount 正常 其他节点的 CSI 插件正常 只有新采买的节点 alinux-node(使用阿里云最新镜像)有问题
看起来是一个"薛定谔的 Bug"——在宿主机上能挂,进了容器就不行。
第一阶段:常规排查
1. 确认容器环境
$ kubectl exec -it csi-plugin-xxx -n kube-system -c csi-plugin -- sh# mount.nfs 存在吗?ls -la /sbin/mount.nfs-rwsr-xr-x 1 root root 196560 Aug 3 2022 /sbin/mount.nfs ✅# 内核支持 NFS 吗?cat /proc/filesystems | grep nfsnodev nfsnodev nfs4 ✅# NFS 内核模块加载了吗?lsmod | grep nfsnfsv3 69632 0nfs 610304 1 nfsv3 ✅# SELinux 开启了吗?cat /sys/fs/selinux/enforce(文件不存在,SELinux 关闭) ✅# Pod 特权模式?securityContext:privileged: trueallowPrivilegeEscalation: true ✅
看起来一切正常,那为什么 mount 会失败?
2. 在容器内手动测试
sh-4.4# mount -t nfs -o vers=3,nolock,tcp,noresvport \<nas-server>.cn-beijing.nas.aliyuncs.com:/ingress/geoip /tmpsh-4.4# echo $?255
exit 255,没有任何错误输出,就像被黑洞吞掉了一样。
第二阶段:关键线索
上面 mount -t nfs 是把参数传给 mount 命令,mount 再去找 /sbin/mount.nfs 来干活。我们绕过 mount,直接调用 mount.nfs:
sh-4.4# /sbin/mount.nfs -o vers=3,nolock,tcp,noresvport \<nas-server>.cn-beijing.nas.aliyuncs.com:/ingress/geoip /tmp/nfs-testsh: line 2: 1253486 Killed /sbin/mount.nfs ...echo $?137
exit 137!Killed!
这个信息极其重要:
mount |
进程是被 SIGKILL 杀死的,而不是正常返回错误。在容器环境里,被 SIGKILL 只有一个可能——OOM Killer。
第三阶段:确认 OOM
dmesg 查看内核日志:
$ dmesg | grep -i "oom\|killed"mount.nfs invoked oom-killer:gfp_mask=0xcc0(GFP_KERNEL), order=0, oom_score_adj=-997Memory cgroup out of memory:Killed process 1256913 (mount.nfs)total-vm:54535296kB (虚拟内存 52GB)anon-rss:2084140kB (物理内存 ~2GB)
破案了!mount.nfs 进程被 cgroup OOM Killer 杀死。
检查 Pod 的内存限制:
resources:limits:memory: 1024Mi # 1GB
把内存限制调到 2Gi 后重试,仍然 OOM,只是 RSS 涨到了 ~2GB:
Memory cgroup out of memory: Killed process mount.nfsanon-rss:2084140kB (2Gi 限制下涨到了 ~2GB)
这说明 mount.nfs 的内存分配是无上限的——给多少内存它就涨到多少,并不是"恰好需要 2GB"的问题。
第四阶段:为什么 mount.nfs 内存会无限上涨?
在容器内执行 mount.nfs -V 可以看到版本是 nfs-utils 2.3.3。当 mount.nfs 执行挂载时:
mount -t nfs -o vers=3 ...└─ mount.nfs└─ rpcinfo -p <NAS服务器> ← 查询 RPC 服务注册信息└─ 服务端返回了异常的 RPC 记录标记└─ 客户端 RPC 库不断分配内存读取数据└─ 虚拟内存暴涨到 52GB,RSS 无限上涨直到 OOM
即使你指定了 vers=3,mount.nfs 仍然会先执行 RPC 探测再应用版本约束,在这个探测过程中就已经 OOM 了。
第五阶段:真正的根因
进一步排查发现:alinux-node 采用了最新的阿里云系统版本 Alibaba Cloud Linux 4(Alinux4)。而集群中的 CSI 插件版本是 v1.24.5,通过 docker inspect 可以看到该镜像基于 Alinux3 构建(org.label-schema.name: "Alinux3 Base Image")。宿主机是 Alinux4,容器内却是 Alinux3 版本的 nfs-utils,两个大版本的内核/RPC 栈接口存在差异,触发兼容性问题。
Alinux4(宿主机)+ 容器内 Alinux3 的 nfs-utils → mount.nfs OOM ❌其他节点没有升级系统,所以一直正常。alinux-node 新采买回来就带着 Alinux4,成为了第一个踩坑的节点。
最终解决方案
节点重装系统降级回 Alibaba Cloud Linux 3.2104 LTS 后重新加入集群,问题彻底解决。
根本上是因为:
操作系统版本与 CSI 插件版本需要匹配:旧版 CSI 插件(Alinux3 基础镜像)的 nfs-utils 与 Alinux4 的新内核/RPC 栈交互时触发内存泄露式分配 升级 CSI 插件到新版(v1.36.1,Debian 基础镜像)也可以解决,因为它使用宿主机 connector 执行 mount,不依赖容器内的 nfs-utils
经验总结
exit code 137 = SIGKILL 一定是进程被杀了,在容器里先查 OOM OOM 查 dmesg,用户态日志看不到内核的 Kill 记录 直接调用子进程( /sbin/mount.nfs)而不是父命令(mount -t nfs),能暴露被包装的错误码操作系统升级后,旧的 CSI 插件可能不兼容,升级 CSI 前先确认版本匹配 有时候不是"哪个节点坏了",而是"节点上的系统版本和组件版本配不上"
后记:重装系统降级版本后,业务恢复。那无限膨胀的内存,就是 mount.nfs 为了一次 RPC 探测付出的代价。
夜雨聆风