节点混部与资源超卖并不是简单地在节点上多塞几个Pod。实际落地时,如果只是把request调低,一旦在线业务流量上涨,CPU和内存争抢会迅速恶化;如果超卖比例设置不当,节点压力驱逐可能把关键Pod杀掉。本文结合生产环境改造经验,梳理混部节点池设计、资源超卖参数、隔离策略和监控体系,给出可复制的配置方案。

一、混部与资源超卖解决什么问题
传统Kubernetes集群的资源利用率往往不高,原因主要有两个:业务方为了保险,会把requests设置得远高于实际平均使用量;同时,在线服务和离线批处理任务通常被划分到不同的节点池,导致在线节点在低峰期大量闲置,离线节点则因为资源不足而排队等待。这两种情况都造成了物理资源浪费,特别是在大规模集群中,账单金额与实际产出之间的差距非常明显。
资源超卖的本质是让调度器认为节点上可分配的资源多于物理资源。实现方式包括直接调低Pod的requests、设置节点超卖系数,或者引入更智能的动态资源预估组件。节点混部则是把在线服务(如Web API)和离线任务(如数据清洗、模型训练)调度到同一批节点上,利用在线业务的波峰波谷与离线任务的可中断特性形成互补。
混部与超卖组合使用可以显著提升节点平均利用率,从常见的20%至30%提升到50%甚至更高。但风险也随之而来:CPU抢占会导致在线请求延迟抖动,内存不足会触发OOM Killer,错误的驱逐顺序可能让核心业务中断。因此落地时必须建立一套完整的资源隔离与稳定性保障机制。
二、requests、limits与QoS:超卖的地基
Kubernetes调度器主要依据Pod的requests来决定其能否被调度到某个节点。如果所有Pod的requests之和小于节点可分配资源,调度器就认为节点有足够空间。而limits是Pod运行时资源使用的上限,CPU是可压缩资源,超限后会被内核限流;内存是不可压缩资源,超限后容器会被OOM Kill。
根据requests和limits的设置情况,Pod会被划分为三个QoS等级:Guaranteed、Burstable和BestEffort。等级越高,在节点资源紧张时越不容易被驱逐。Guaranteed要求每个容器都设置CPU和内存的requests且等于limits;Burstable至少有一个容器设置了requests但未完全等于limits;BestEffort则完全没有设置任何requests和limits。以下YAML展示了三种QoS的典型定义:
# Guaranteed QoS
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-demo
spec:
containers:
- name: app
image: nginx:1.24
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"
---
# Burstable QoS
apiVersion: v1
kind: Pod
metadata:
name: burstable-demo
spec:
containers:
- name: app
image: nginx:1.24
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "1Gi"
---
# BestEffort QoS
apiVersion: v1
kind: Pod
metadata:
name: besteffort-demo
spec:
containers:
- name: app
image: nginx:1.24
超卖策略基本上是围绕QoS展开的:将离线任务设置为BestEffort或低requests的Burstable,让它们尽量利用空闲资源;为在线服务设置Guaranteed或合理的Burstable,保证其基本资源不受抢占。当节点资源紧张时,kubelet会按照QoS顺序驱逐Pod,优先驱逐BestEffort,其次Burstable,最后才考虑Guaranteed。理解这一点,才能在超卖时合理设计Pod的资源声明。
三、节点混部落地的关键步骤
第一步是划分混部节点池。建议不要在原有在线节点上直接开启混部,而是新建一组带独立标签的节点,例如node-role.kubernetes.io/mixed=true。这样在线业务和离线任务都可以通过nodeSelector或亲和性调度到这些节点,同时保留纯在线节点池作为兜底,遇到突发流量时可以快速迁移。
第二步是引入扩展调度器或组件。原生调度器不感知资源超卖比例,也无法根据节点实时负载做动态调度。社区常用的方案包括Koordinator、Volcano和Crane。以Koordinator为例,它通过CRD定义NodeResourceTopology和PodMigrationJob,并在调度阶段根据节点实际可用资源(而非仅依据requests)来做决策。下面是一个简化的Koordinator配置示例,展示如何为混部节点设置资源预留和超卖系数:
apiVersion: scheduling.koordinator.sh/v1alpha1
kind: Reservation
metadata:
name: mixed-node-reservation
spec:
template:
spec:
containers:
- name: reserved-resource
resources:
requests:
cpu: "4000m"
memory: "8Gi"
nodeName: node-mixed-01
owners:
- object:
name: offline-job
第三步是设置超卖比例。超卖系数通常定义在调度器或节点资源管理组件中,例如Koordinator的NodeMetric CRD可以配置overcommitmentRatio字段。假设节点物理CPU为96核,预留系统组件和kubelet共8核,剩余88核可分配给Pod。如果设置超卖系数为1.5,那么调度器最多可以接受132核的requests。这个系数需要结合集群实际负载曲线逐步调整,初期建议从1.2开始,观察一到两周后再上调。
四、CPU与内存隔离策略
CPU是混部场景中最容易产生争抢的资源。Kubernetes默认使用CFS调度器,通过cpu.shares来按比例分配CPU时间片。但仅靠shares无法彻底避免在线服务被离线任务拖累,因为离线任务可能在一个CPU核上运行多个线程,频繁唤醒导致上下文切换开销增大。生产环境建议开启CPU Manager的静态策略,为Guaranteed QoS的Pod分配独占CPU核心,避免与其他Pod共享。kubelet配置如下:
apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cpuManagerPolicy: static cpuManagerReconcilePeriod: 5s topologyManagerPolicy: best-effort systemReserved: cpu: "2000m" memory: "2Gi" kubeReserved: cpu: "1000m" memory: "1Gi"
内存隔离比CPU更棘手,因为内存超卖稍有不慎就会触发整节点OOM。除了设置合理的limits,还要调整kubelet的驱逐阈值。例如将evictionHard中的memory.available设置为500Mi或节点总内存的5%,这样在内存低于阈值时kubelet会提前驱逐低QoS Pod,给系统留出缓冲。Linux内核的OOM Score也会影响哪个进程先被杀,kubelet通过oom_score_adj为不同QoS等级的容器设置不同分值。Guaranteed容器通常为-998,BestEffort为1000,因此OOM时优先杀掉离线任务。
此外,对于延迟敏感的在线服务,建议将离线任务的CPU限制在一定范围内,或者使用cgroup v2的cpu.weight设置更细粒度权重。如果节点支持cgroups v2,kubelet会自动调整部分参数,但混部时最好确认内核版本和cgroup驱动一致性,避免出现cgroup挂载失败导致的调度异常。
五、监控与动态调整
混部与超卖不是一次性配置就能高枕无忧,必须建立持续的监控和反馈机制。重点关注的指标包括:节点CPU利用率、内存使用率、容器实际CPU使用量与requests的比值、容器被限流的次数、驱逐事件数量以及在线服务P99延迟。当发现节点CPU利用率长期超过85%,或者BestEffort Pod被频繁驱逐,就说明超卖比例过高,需要下调或增加节点。
动态调整可以结合Prometheus和自定义控制器实现。例如每30分钟计算一次节点上所有在线Pod过去一小时的平均CPU使用量,如果平均使用量之和低于节点可分配资源的60%,则允许更多离线Pod调度上来;如果超过80%,则通过节点污点或Pod迁移来疏散部分离线任务。这样系统可以根据负载周期性自动伸缩,避免人工频繁修改配置。
最后需要强调的是,混部与超卖带来的是资源利用率的提升,但代价是系统复杂度增加。上线前务必在测试集群模拟高负载和OOM场景,验证驱逐顺序、隔离效果以及在线服务的延迟表现。只有经过充分验证的参数组合,才能安全地推广到生产环境,实现降本增效的目标。
Kubernetes资源超卖节点混部修改时间:2026-09-26 06:55:29