Kubernetes节点高并发下报“too many open files”是因为kubelet、containerd、etcd等组件密集创建socket、日志和临时文件描述符,而默认系统级fs.file-max、用户ulimit及服务单元LimitNOFILE均偏低(如65536),无法支撑千级Pod或万级连接;必须同步调整系统全局(/etc/sysctl.conf设fs.file-max=2097152)、服务单元(kubelet/etcd systemd配置LimitNOFILE=1048576)和容器运行时(containerd config.toml启用SystemdCgroup)三层限制,缺一不可。

为什么 Kubernetes 节点在高并发下会报 too many open files
因为 kubelet、containerd、etcd 等组件在处理大量 Pod、容器、watch 请求时,会密集创建 socket、日志文件、临时文件描述符。默认系统级 fs.file-max 和用户级 ulimit -n 通常只有 65536 或更低,远不够支撑千级 Pod 或万级连接的生产集群。
必须改的三个层级:系统全局、服务单元、容器运行时
只改其中一层,Kubernetes 仍会 fallback 到低值——比如只调大 fs.file-max,但 kubelet 进程启动时没继承新 ulimit,实际打开数还是受限。
-
系统全局:写入
/etc/sysctl.conf,生效fs.file-max = 2097152(200 万),执行sysctl -p -
服务单元:修改
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf,在[Service]下加两行:LimitNOFILE=1048576LimitNPROC=1048576;然后systemctl daemon-reload && systemctl restart kubelet -
容器运行时:containerd 需显式配置——编辑
/etc/containerd/config.toml,在[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]下加SystemdCgroup = true,并确保default_runtime_name = "runc";再重启containerd
etcd 和 apiserver 的 ulimit 容易被忽略
etcd 对文件句柄更敏感:每个 client 连接、每个 WAL 日志段、每个 snapshot 都占 fd。若用 systemd 启动 etcd(非 kubeadm 托管),需单独配 LimitNOFILE;若用 kubeadm 部署,etcd 是静态 Pod,其 manifest 在 /etc/kubernetes/manifests/etcd.yaml 中,不能直接加 ulimit,得靠宿主机的 systemd 单元覆盖——检查 systemctl cat etcd,若无对应单元,则需手动创建 /etc/systemd/system/etcd.service.d/limits.conf 并写入 LimitNOFILE=1048576。
apiserver 同理:kubeadm 默认不生成独立 service 单元,但若你用二进制部署或自定义清单,也要确认其 systemd unit 文件里有 LimitNOFILE。
验证是否真正生效的命令组合
别只信 ulimit -n 当前 shell 的输出——它和 kubelet 进程无关。正确验证方式:
- 查 kubelet 进程当前限制:
cat /proc/$(pgrep kubelet)/limits | grep "Max open files" - 查 containerd:
cat /proc/$(pgrep containerd)/limits | grep "Max open files" - 查 etcd(若为 systemd 管理):
systemctl show etcd | grep LimitNOFILE - 实时观察 fd 使用量:
ls /proc/$(pgrep kubelet)/fd | wc -l(注意这个值应稳定低于 limit 值,否则仍有风险)
改完不验证等于没改;而验证时若发现某进程仍卡在 65536,基本就是对应服务单元没 reload 或配置位置错了。


















