如果你们公司的 K8s 节点不是裸金属,而是跑在 ESXi 上的虚拟机,大概率踩过这样一类玄学问题:监控面板上节点 CPU / 内存水位明明不高,Pod 却突然卡顿、CPU 被节流、甚至 OOM;HPA、VPA 一切正常,流量一上来直接雪崩;扩容逻辑本身没毛病,可底层 ESXi 主机资源的挤兑,会让整批 Node 集体“内伤”。
本质原因一句话就能说清:这条链路里存在两层独立超卖,两套完全隔离、互不感知的乐观调度器——kube-scheduler(K8s 层)+ ESXi Hypervisor 调度器(虚拟化层)。两层都在“赌业务不会同时打满峰值”,各自做着乐观假设,彼此之间信息完全不通。
先定义本文所说的「乐观调度器」:调度决策时,不要求资源实时独占预留,基于“大部分时间业务用不到配置上限”这个假设,允许分配总和大于物理真实容量;调度成功只代表“当前账面够”,不代表峰值一定能扛住。
第一层:K8s 侧乐观调度器 kube-scheduler
K8s 调度决策只认 resources.requests,limits 是运行时硬上限,调度阶段完全不看 limits 总和。
- 调度逻辑:累加所有 Pod 的 request,只要小于节点 allocatable,就可以调度 Pod 上来。这里的“账面统计”只按 request 走,调度逻辑:累加所有Pod的request,只要小于节点allocatable,就可以调度Pod上来 这个机制本身就埋下了超卖的空间。
- 超卖来源:sum(limits) ≫ sum(requests)。Burstable 类型 Pod 天然就是 K8s 层面的超卖模型:调度器只按 request 做资源账面统计,业务低负载时占用很低,突发繁忙时最多可以吃到 limit 上限。kube-scheduler 的乐观假设就是:所有 Pod 不会同时打到 limit。
兜底机制:kubelet、cgroup 节流、内存 OOM、节点压力驱逐。
举个例子:一台 VM Node,allocatable 16C 32G。我们跑一堆 Pod:每个 Pod request=0.5C/1G,limit=2C/4G。调度器可以塞很多 Pod,sum(request) 不超 16C;但 sum(limit) 轻轻松松就跑到 16C 以上,这就是 K8s 层的超卖。kube-scheduler 乐观地相信:业务不会同时爆到 limit。
关键点:kube-scheduler 眼里,Node 就是一台拥有固定 allocatable 的“真实服务器”。它完全不知道自己底层是 VM,不知道 ESXi 还会再做一层超分配。
第二层:ESXi 宿主机乐观调度器
ESXi 对 VM 做 vCPU、内存超分配(Overcommit),这是虚拟化经典能力。
- 调度逻辑:给 VM 分配的 vCPU 总数,可以大于宿主机物理 CPU 核数;分配给所有 VM 的内存总和,可以大于物理内存。
- CPU:时间片轮转,VM 看到多个 vCPU,hypervisor 在物理核上分时调度;vCPU 越多,调度切换开销、ready 队列排队延迟越高。
- 内存:ESXi 有透明页共享 TPS、内存气球 balloon、交换 swapping 等机制做动态回收。
乐观假设:所有 VM 不会同时把 vCPU 跑满、不会同时占满全部分配内存。
兜底机制:CPU ready 飙升、balloon、主机 swap、VM 卡顿、甚至 VM 卡死。
举例:一台 ESXi 物理主机 32C 256G,开 10 台 VM,每台 VM 给 8vCPU/32G。sum(vCPU)=80,远大于物理 32C;sum(内存)=320G,大于物理 256G。ESXi 调度器乐观地赌:这些 VM 不会同时拉满。
关键点:ESXi 调度器眼里,VM 就是一个独立客户。它不知道 VM 里面跑的是 K8s,不知道 VM 内部还会再放一堆 Pod、还有一层 request/limits 超卖。
致命问题:两套乐观,互相隔离,没有全局视图
这是整个架构最容易被忽略的地方:
- kube-scheduler:调度单元是 Pod,输入是 VM Node 的 allocatable,对下层 ESXi 负载无感知。
- ESXi 调度器:调度单元是 VM,输入是 VM 配置的 vCPU / 内存,对 VM 内部的 Pod、request / limits、QoS 完全无感知。
两者是两套独立、互不通信、独立做乐观假设的调度系统。风险会叠加放大:
- 平时流量低:两边都很稳,监控一切正常,甚至会觉得超卖很香,资源利用率拉满。
- 突发峰值:一批 Pod 同时冲到 limit → Node(VM)瞬间拉高资源占用 → 触发 ESXi 层面的资源争抢。
- 连锁反应:ESXi CPU ready 暴涨、内存气球启动、swap,K8s Node 开始出现内核态卡顿、容器 cgroup 节流、延迟抖动。
- 更糟:K8s 驱逐、Pod 重建,重建后的 Pod 又调度到同一台 ESXi 主机上的其他 VM 里,进一步加剧宿主机压力,形成雪崩。
很多运维遇到的疑难故障:Node 指标看着还有余量,但业务 RT 飙升、Pod 丢包、随机 OOM,根因往往不在 K8s 层,而是下层 ESXi 超卖击穿——VM 底层资源已经被抢爆,但 K8s 监控采集的是 VM 内部视角,根本看不到宿主机的排队开销。
一张极简风险模型
单层超卖:风险可控,有明确的边界和观测指标。
两层叠加超卖:概率乘法效应。K8s 赌 Pod 不同时打满,ESXi 赌 VM 不同时打满;两层假设同时失效的概率,远高于单层。
容易踩的几个认知误区
误区 1:只要 K8s 按 request 调度,底层就安全
错。request 只是 K8s 调度的账面预留,limit 才是峰值上限。当 Pod 冲到 limit,对 VM 来说就是瞬时高负载,直接打向 ESXi 的超卖池。request ≠ VM 的资源保障。
只有给 VM 在 ESXi 上设置资源预留(Reservation),才是给 K8s Node 保底。不设预留,VM 本身就是一个可被超分的“弹性容器”。
误区 2:ESXi 只要不超内存,vCPU 超一点没事
错。vCPU 超配带来的 CPU ready 是隐形杀手。很多平台只看使用率,不看 ready 时间,业务延迟莫名其妙恶化。
误区 3:监控看 Node 的 CPU 使用率就足够
错。你必须双层观测:
- 上层:K8s 侧 Pod request / limit 使用率、QoS、节流、驱逐事件。
- 下层:ESXi 侧 VM CPU ready、balloon、swap、主机内存压力。
只看其中一层,等于盲人摸象。
工程上怎么控住两层超卖的风险?
1. 隔离调度域,打通信息(最理想,但成本高)
让 K8s 调度器感知 ESXi 主机资源:
- 借助 vSphere CSI、vSphere Cloud Provider、自定义调度插件。
- 调度 Pod 时,不仅看 VM 内 allocatable,还要看 VM 所在 ESXi 主机的水位。
缺点:架构复杂,耦合虚拟化平台,很多私有云、信创环境不易落地。
2. 分层设置保守水位,做硬上限(落地最广)
K8s 层:控制 sum(limits)/allocatable 的超卖倍率,比如限制在 1.5~2 倍,在线业务尽量低;离线批处理可以放宽;做好 QoS 分级,Guaranteed 业务尽量不参与超卖。
ESXi 层:对承载核心 K8s Node 的 VM,设置资源预留 Reservation,优先保障;核心业务宿主机压低 vCPU 超配倍率,禁止激进内存超分配;给 ESXi 主机设置安全水位,比如 CPU 大于 80%、balloon 激活就禁止新 VM 部署。
3. 流量与扩缩容层面做熔断,避免叠加冲击
HPA、VPA 扩容策略增加冷却、分批扩容;高优先级在线业务和离线任务打散在不同 ESXi 主机池,HPA、VPA扩容策略增加冷却、分批扩容 避免离线打爆宿主机连带在线 K8s 节点遭殃。
4. 指标体系必须双层对齐,统一告警
K8s 侧:CPU throttle、OOM、驱逐事件、request 水位、limit 水位。
ESXi 侧:%Ready、balloon 目标、swap in/out、主机内存压力。
告警不能只看使用率,排队指标优先级高于使用率。
5. 架构备选方案:裸金属 Node,砍掉一层乐观调度
如果业务延迟敏感、支付、核心交易类,直接让 K8s 跑在裸金属上,只保留 kube-scheduler 一层调度,去掉 ESXi 这一层超卖,风险模型会简单很多。代价是资源利用率下降。
什么时候适合两层超卖架构?
适合:负载波动大、有明显波谷、延迟容忍度尚可、离线 / 内部业务、测试环境。
非常不适合:低延迟核心链路、强一致性交易、流量尖峰极陡的在线业务——两层乐观叠加,一次峰值就可能引发事故。
结尾总结
kube-scheduler 和 ESXi hypervisor,本质上是两套理念同源、但信息隔离的乐观调度器。K8s 把 Node(VM)当成物理机去做 Pod 超卖;ESXi 把 VM 当成负载去做虚拟化超分配。它们各自的逻辑单独看都自洽,合在一起就是一个高风险的双层彩票系统:平时很香,利用率拉满;一旦两层的乐观假设同时被打破,故障就是非线性的雪崩。
理解这个模型,再回头看很多莫名其妙的 K8s 抖动、随机 OOM、扩容翻车,答案就清晰多了:不是 K8s 错了,也不是 ESXi 错了,是两套独立乐观调度叠加在一起,缺少全局的约束和感知。
架构取舍没有银弹:要么接受利用率换风险,要么牺牲利用率换确定性;最可怕的是,你根本不知道这里存在两层超卖。