← 返回文章列表

K8s 资源超卖方案:原理与实现

10 分钟阅读
字号

K8s 资源超卖方案:原理与实现

本文介绍 K8s 资源超卖的概念、解决的问题,以及四个核心组件的实现方法。


一、为什么需要资源超卖

Kubernetes 资源上报的默认逻辑

Kubelet 在创建 Pod 时,会根据 requests 值向 kube-apiserver 汇报 Pod 所需的资源量。调度器调度 Pod 时,依据的是 requests 值而非实际使用量。

resources:
  requests:
    cpu: "2"
    memory: "4Gi"
  limits:
    cpu: "4"
    memory: "8Gi"

默认逻辑的问题

假设一个节点有 32 核 CPU、128Gi 内存:

PodCPU RequestMemory Request
Pod A4核16Gi
Pod B4核16Gi
Pod C4核16Gi
Pod D4核16Gi

调度器认为已用 16核/64Gi,剩余 16核/64Gi,还可以再调度 4 个 Pod。

但实际上:这些 Pod 可能只用了 50% 的资源,节点已经快撑不住了。

资源超卖的核心思路

用真实使用量替代 requests 作为调度依据,从而在保证节点稳定的前提下,提高资源利用率。


二、核心概念

PDB(Pod Disruption Budget)

保障 Pod 最小可用数量的资源对象,防止一次性驱逐过多 Pod。

apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
  minAvailable: 3    # 最少保留 3 个 Pod
  # 或者
  maxUnavailable: 30% # 最多驱逐 30%

优先级与抢占

K8s 支持 Pod 优先级(PriorityClass),高优先级 Pod 可以抢占低优先级 Pod。


三、四个核心组件

组件总览

┌─────────────────────────────────────────────────────────┐
│                    资源超卖架构                            │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌──────────────────┐                                  │
│  │ kube-node 采集器  │ → 获取节点真实资源使用率            │
│  └────────┬─────────┘                                  │
│           ▼                                            │
│  ┌──────────────────┐                                  │
│  │ Oversold-Injector│ → 修正 kubelet 上报值             │
│  │    (Webhook)      │                                  │
│  └────────┬─────────┘                                  │
│           ▼                                            │
│  ┌──────────────────┐                                  │
│  │ Matrix 保护控制器 │ → 节点过载时驱逐低优先级 Pod        │
│  └────────┬─────────┘                                  │
│           ▼                                            │
│  ┌──────────────────┐                                  │
│  │ DW-Scheduler     │ → 调度时考虑真实利用率             │
│  │ 增强调度器        │                                  │
│  └──────────────────┘                                  │
│                                                         │
└─────────────────────────────────────────────────────────┘

1. kube-node 采集器

作用:定时调用 kubelet API 获取节点真实资源使用率。

实现方法

  1. 部署一个 DaemonSet 到每个节点
  2. 每隔一段时间(如 10 秒)调用 kubelet 的 Summary API
    GET /api/v1/nodes/{nodeName}/proxy/stats/summary
  3. 解析返回的 JSON,提取真实 CPU/内存使用量:
    {
      "node": {
        "cpu": { "usageNanoCores": 15000000000 },  // 15核
        "memory": { "usageBytes": 50000000000 }      // 约 50Gi
      }
    }
  4. 将数据上报到 Matrix 保护控制器

关键点

  • 调用 kubelet 不需要认证,因为 DaemonSet 和 kubelet 在同一节点
  • 采集频率要合适:太频繁增加负载,太稀疏数据不及时

2. Oversold-Injector(Webhook)

作用:拦截 kubelet 上报的 metrics,修正为真实使用量,让调度器看到更准确的数据。

实现方法

  1. 创建一个 MutatingWebhookConfiguration
  2. 监听 nodes/status 资源变更
  3. 当 kubelet 上报节点 allocatable 时,用真实使用量替换 requests
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: oversold-injector
webhooks:
  - name: oversold-injector.example.com
    clientConfig:
      service:
        name: oversold-injector
        namespace: default
        path: "/mutate-node"
    rules:
      - operations: ["UPDATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["nodes/status"]

修正逻辑

原始值修正值
node.status.allocatable.cpu节点真实剩余 CPU × 调整系数
node.status.allocatable.memory节点真实剩余 Memory × 调整系数

为什么不直接改 Pod 的 requests

  • Pod 的 requests 影响 Limit 计算
  • 直接改 Pod 可能导致已运行 Pod 的 Limit 收缩,引发不稳定
  • 改节点 allocatable 更干净,让新调度过来的 Pod 自然用更少资源

3. Matrix 保护控制器

作用:监控节点真实使用率,当超过阈值时,主动驱逐低优先级 Pod 释放资源。

实现方法

  1. 部署一个 Controller,持续监控所有节点的使用率
  2. 当节点使用率超过阈值(如 80%)时,触发保护逻辑:
    • 找出该节点上所有 低优先级 Pod
    • 计算需要驱逐多少 Pod 才能降到安全线
    • 结合 PDB 驱逐 Pod(保证 PDB 约束)
    • 触发 cgroup 限流 限制低优先级 Pod 资源使用

驱逐流程

节点使用率 > 80%


┌──────────────────┐
│ 1. 标记低优先级 Pod │  ← 找出所有低优先级 Pod
└────────┬─────────┘

┌──────────────────┐
│ 2. 计算需驱逐数量  │  ← 目标:降到 70% 以下
└────────┬─────────┘

┌──────────────────┐
│ 3. 检查 PDB 约束  │  ← 保证 PDB minAvailable
└────────┬─────────┘

┌──────────────────┐
│ 4. 执行驱逐        │  ← kubectl delete pod
└────────┬─────────┘

┌──────────────────┐
│ 5. 触发 cgroup    │  ← 限流保护
│    限流            │
└──────────────────┘

cgroup 限流

  • Linux cgroup 可以限制进程组的 CPU/内存使用
  • Controller 通过 systemd 或 docker config 动态调整低优先级 Pod 的 cgroup 限制
  • 快速降低资源争抢,无需等待 Pod 真正被驱逐

4. DW-Scheduler 增强调度器

作用:在调度阶段就考虑节点真实利用率,避免把 Pod 调度到即将过载的节点。

实现方法

修改 kube-scheduler 配置,在 FilterScore 阶段加入真实利用率考量。

Filter 阶段(预选)

过滤掉不符合条件的节点。增强后:

  • 原始条件:节点 allocatable >= Pod requests
  • 增强条件:节点 allocatable >= Pod requests 节点当前使用率 < 阈值(如 85%)
// Filter 增强伪代码
func OversoldFilter(pod *v1.Pod, node *v1.Node) bool {
    // 原条件:节点资源够不够
    if !originalFilter(pod, node) {
        return false
    }
 
    // 新条件:节点使用率是否在安全范围内
    realUsage := getNodeRealUsage(node.Name)
    if realUsage.CPUPercent > 85 || realUsage.MemoryPercent > 85 {
        return false  // 拒绝调度到过载节点
    }
 
    return true
}

Score 阶段(优选)

对符合条件的节点打分。增强后:

  • 原始分数:基于 allocatable 计算
  • 增强分数:真实使用率越低,分数越高(优先调度到资源更充裕的节点)
// Score 增强伪代码
func OversoldScore(pod *v1.Pod, nodes []*v1.Node) map[string]int {
    scores := originalScore(pod, nodes)
 
    for _, node := range nodes {
        realUsage := getNodeRealUsage(node.Name)
        // 真实使用率越低,分数加成越高
        usageBonus := 100 - realUsage.CPUPercent
        scores[node.Name] += usageBonus * 0.5  // 加权系数 0.5
    }
 
    return scores
}

四、完整流程

Pod 调度到运行的完整链路

1. 用户提交 Pod


2. kube-apiserver 接收请求


3. Scheduler Filter 阶段
   ├─ 检查节点 allocatable
   └─ 检查节点真实使用率 < 85%  ← 增强点


4. Scheduler Score 阶段
   └─ 真实使用率越低分数越高  ← 增强点


5. Pod 调度到目标节点


6. kubelet 创建 Pod


7. kube-node 采集器上报真实使用量  ← 持续运行


8. Oversold-Injector 修正节点 allocatable  ← 持续运行


9. Matrix 保护控制器监控
   ├─ 使用率 > 80% → 驱逐低优先级 Pod
   └─ 触发 cgroup 限流

Pod 抢占与驱逐

高优先级 Pod A 等待调度


Matrix 检测到节点紧张


┌──────────────────┐
│ 抢占低优先级 Pod B │ ← B 被标记为待驱逐
└────────┬─────────┘


PDB 保护检查
├─ B 所在 PDB minAvailable=2
└─ 当前有 3 个 B 类 Pod → 可以驱逐 1 个


Pod B 被驱逐


Pod A 调度成功

五、与传统超卖的区别

维度传统超卖K8s 资源超卖方案
依据固定比例真实使用率动态调整
保护PDB + cgroup 多层保护
调度无感知Scheduler 增强,提前规避
响应被动主动预防 + 被动拦截

六、总结

┌─────────────────────────────────────────────────────────┐
│                   K8s 资源超卖核心要点                    │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  四个组件:                                               │
│  ├─ kube-node 采集器      → 获取真实使用率                │
│  ├─ Oversold-Injector     → 修正 kubelet 上报值          │
│  ├─ Matrix 保护控制器      → 过载时驱逐低优先级 Pod        │
│  └─ DW-Scheduler 增强     → 调度时预判节点负载            │
│                                                          │
│  核心思想:                                               │
│  ├─ 用真实使用量替代 requests 作为调度依据               │
│  ├─ PDB 保护低优先级 Pod 最小可用数量                    │
│  └─ cgroup 限流快速降低资源争抢                          │
│                                                          │
│  效果:                                                   │
│  └─ 提高资源利用率的同时保障节点稳定性                    │
│                                                          │
└─────────────────────────────────────────────────────────┘
分享

// RELATED_POSTS

0%