揭秘云原生混布资源调度器Koordinator (七)QOSManager 决策引擎

24 阅读19分钟

核心使命与设计理念

7.1 What - QOSManager 是什么?

QOSManager 是 Koordlet 中的资源协调决策引擎,负责根据节点资源状态和 Pod QoS 等级,动态调整资源分配和隔离策略。

核心职责

  1. 监控节点资源压力(CPU、内存、I/O)
  2. 根据 Pod QoS 等级制定差异化策略
  3. 决策资源抑制、驱逐、隔离操作
  4. 协调多个 QoS 策略插件的执行顺序
  5. 保障 LSE/LSR/LS Pod 的 SLO 不受侵犯

QoS 等级回顾

┌─────────────────────────────────────────────┐
│         Koordinator QoS 等级体系            │
├─────────────────────────────────────────────┤
│                                             │
│ LSE (Latency Sensitive Exclusive)          │
│ ├─ 最高优先级                              │
│ ├─ 独占 CPU 核心                           │
│ ├─ 不被干扰                                │
│ └─ 例:交易系统、实时计算                  │
│                                             │
│ LSR (Latency Sensitive Reserved)           │
│ ├─ 高优先级                                │
│ ├─ 预留资源保证                            │
│ ├─ 可轻微共享                              │
│ └─ 例:在线服务、API 网关                  │
│                                             │
│ LS (Latency Sensitive)                     │
│ ├─ 中等优先级                              │
│ ├─ 有一定资源保障                          │
│ ├─ 可共享 CPU                              │
│ └─ 例:Web 服务、后台任务                  │
│                                             │
│ BE (Best Effort)                           │
│ ├─ 低优先级                                │
│ ├─ 可被抑制和驱逐                          │
│ ├─ 使用剩余资源                            │
│ └─ 例:离线计算、数据分析                  │
│                                             │
│ SYSTEM                                     │
│ ├─ 系统保留                                │
│ ├─ 不受混部管理                            │
│ └─ 例:kubelet、系统守护进程               │
│                                             │
└─────────────────────────────────────────────┘

7.2 Why - 为什么需要决策引擎?

问题 1:混部环境下资源竞争无法自动协调

场景:同节点运行 LS 和 BE Pod

不使用 QOSManager 的问题:
┌─────────────────────────────────────┐
│ 节点资源: 8 CPU                     │
├─────────────────────────────────────┤
│ LS Pod (在线服务)                   │
│ ├─ request: 2 CPU                  │
│ ├─ limit: 4 CPU                    │
│ ├─ 实际使用: 波动 1-3.5 CPU        │
│                                     │
│ BE Pod (离线计算)                   │
│ ├─ request: 0 CPU                  │
│ ├─ limit: 未设置                    │
│ ├─ 实际使用: 尽量多用(贪婪)       │
│                                     │
│ 问题时刻:                           │
│ T=10:00 LS 流量激增                 │
│ ├─ LS 需要: 3.5 CPU                │
│ ├─ BE 已占用: 5 CPU                │
│ ├─ 总需求: 8.5 CPU > 8 CPU        │
│ └─ 结果: LS 延迟增加,SLO 违反     │
│                                     │
│ Kubernetes 默认行为:               │
│ ├─ CPU shares 机制:按比例分配     │
│ ├─ LS 和 BE 竞争,无优先级         │
│ └─ 无法主动抑制 BE                 │
└─────────────────────────────────────┘

使用 QOSManager 的解决方案:
┌─────────────────────────────────────┐
│ QOSManager 实时监控                 │
├─────────────────────────────────────┤
│ T=10:00:00 检测到资源压力           │
│ ├─ LS CPU 使用: 3.5 CPU            │
│ ├─ BE CPU 使用: 5 CPU              │
│ ├─ 总使用: 8.5 > 8 (超载)         │
│                                     │
│ T=10:00:01 决策                     │
│ ├─ 策略: 抑制 BE Pod               │
│ ├─ 目标: BE CPU 限制为 3 CPU       │
│ ├─ 保障: LS 可用 4.5 CPU           │
│                                     │
│ T=10:00:02 执行                     │
│ ├─ 调用 ResourceExecutor           │
│ ├─ 更新 BE 的 CGroup quota         │
│ └─ BE CPU 被限制                   │
│                                     │
│ T=10:00:05 验证                     │
│ ├─ LS CPU: 3.5 CPU (正常)          │
│ ├─ LS 延迟: < 100ms (达标)         │
│ └─ SLO 保持 99.9%                  │
└─────────────────────────────────────┘

问题 2:静态配置无法应对动态负载

Kubernetes 的局限性:

静态配置:
├─ Pod request/limit 是固定值
├─ 创建后不会根据实际情况调整
└─ 无法应对突发流量

例:在线服务的负载特征
┌─────────────────────────────────────┐
│ 时间段        | CPU 需求            │
├─────────────────────────────────────┤
│ 00:00-06:00  | 0.5 CPU (低谷)      │
│ 06:00-09:00  | 1.5 CPU (上升)      │
│ 09:00-12:00  | 3.5 CPU (高峰)      │
│ 12:00-14:00  | 2.0 CPU (午间)      │
│ 14:00-18:00  | 3.0 CPU (下午峰)    │
│ 18:00-00:00  | 1.0 CPU (夜间)      │
└─────────────────────────────────────┘

静态 limit=4 CPU 的问题:
├─ 高峰期 (3.5 CPU): 勉强够用
├─ 低谷期 (0.5 CPU): 浪费 3.5 CPU
└─ 平均利用率: 只有 50%

QOSManager 的动态策略:
├─ 低谷期 (00:00-06:00):
│  └─ 允许 BE 使用更多 CPU (5-6 CPU)
│  └─ 提高整体利用率到 75%
│
├─ 高峰期 (09:00-12:00):
│  └─ 提前抑制 BE (限制到 2 CPU)
│  └─ 为 LS 预留充足资源
│
└─ 突发事件:
   └─ LS 突然需要 3.8 CPU
   └─ 立即驱逐部分 BE Pod
   └─ 保障 LS 延迟 < 100ms

问题 3:需要多维度综合决策

复杂的决策场景:

单一指标决策的问题:
├─ 只看 CPU: 内存可能已经爆满
├─ 只看内存: CPU 可能已经过载
└─ 只看单个 Pod: 整体节点可能失衡

QOSManager 的多维决策矩阵:

维度 1: CPU 压力
    ├─ 当前使用率
    ├─ 增长趋势
    └─ 历史峰值

维度 2: 内存压力
    ├─ RSS 占用
    ├─ 内存泄漏检测
    └─ OOM 风险评估

维度 3: I/O 压力
    ├─ 磁盘吞吐量
    ├─ I/O 等待时间
    └─ 网络带宽

维度 4: QoS 等级优先级
    ├─ LSE > LSR > LS > BE
    ├─ 同等级 Pod 按优先级 Priority
    └─ 关键业务标签

维度 5: 节点整体健康
    ├─ 系统负载 (load average)
    ├─ 上下文切换频率
    └─ 中断处理延迟

综合决策流程:
    if CPU > 80% AND Memory > 85% AND LS_latency_increasing:
        priority = CRITICAL
        action = EVICT_BE_IMMEDIATELY
    elif CPU > 70% AND trend > 5%/min:
        priority = HIGH
        action = THROTTLE_BE_AGGRESSIVE
    elif CPU > 60%:
        priority = MEDIUM
        action = THROTTLE_BE_MODERATE
    else:
        priority = LOW
        action = RELAX_BE_LIMIT

7.3 How - 决策引擎的核心机制

QOSManager 的架构设计

┌──────────────────────────────────────────────┐
│         QOSManager 决策引擎架构             │
├──────────────────────────────────────────────┤
│                                              │
│ ┌──────────────────────────────────────┐   │
│ │     1. 数据采集层                     │   │
│ │  ┌────────────┬────────────┐         │   │
│ │  │ StatesInformer │ MetricCache │      │   │
│ │  └────────────┴────────────┘         │   │
│ │  获取 Pod 信息 + 历史指标数据         │   │
│ └──────────────────────────────────────┘   │
│                   ↓                         │
│ ┌──────────────────────────────────────┐   │
│ │     2. 分析评估层                     │   │
│ │  ┌────────────┬────────────┐         │   │
│ │  │  压力检测   │  趋势分析  │         │   │
│ │  └────────────┴────────────┘         │   │
│ │  计算资源压力分数、预测未来状态       │   │
│ └──────────────────────────────────────┘   │
│                   ↓                         │
│ ┌──────────────────────────────────────┐   │
│ │     3. 策略决策层                     │   │
│ │  ┌──────────────────────────────┐   │   │
│ │  │  6 大策略插件                 │   │   │
│ │  │  1. CPUSuppress              │   │   │
│ │  │  2. CPUEvict                 │   │   │
│ │  │  3. MemoryEvict              │   │   │
│ │  │  4. CPUBurst                 │   │   │
│ │  │  5. NodeQOSResource          │   │   │
│ │  │  6. SystemQOSResource        │   │   │
│ │  └──────────────────────────────┘   │   │
│ │  根据压力和 QoS 等级制定具体策略      │   │
│ └──────────────────────────────────────┘   │
│                   ↓                         │
│ ┌──────────────────────────────────────┐   │
│ │     4. 执行协调层                     │   │
│ │  ┌────────────────────────────┐     │   │
│ │  │  ResourceExecutor 接口     │     │   │
│ │  └────────────────────────────┘     │   │
│ │  将决策转化为 CGroup 操作指令         │   │
│ └──────────────────────────────────────┘   │
│                   ↓                         │
│ ┌──────────────────────────────────────┐   │
│ │     5. 反馈验证层                     │   │
│ │  监控执行效果,调整下一轮决策         │   │
│ └──────────────────────────────────────┘   │
│                                              │
└──────────────────────────────────────────────┘

工作周期:每秒执行一次(可配置)
决策延迟:< 100ms(从检测到决策完成)

QOSManager 的完整实现

7.4 压力检测与评分机制

CPU 压力检测

// CPU 压力评分算法
func calculateCPUPressure(node *NodeMetrics) float64 {
    // 因子 1: 当前 CPU 使用率(权重 40%)
    currentUsage := node.CPUUsagePercent
    score1 := currentUsage * 0.4
    
    // 因子 2: LS Pod 的实际使用 vs request(权重 30%)
    lsUsage := node.LSPodCPUUsage
    lsRequest := node.LSPodCPURequest
    lsRatio := lsUsage / lsRequest
    score2 := min(lsRatio, 2.0) * 0.3 * 100
    
    // 因子 3: CPU 使用增长趋势(权重 20%)
    trend := calculateTrend(node.CPUHistory5Min)
    // trend > 0: 增长中, < 0: 下降中
    // 归一化到 0-100
    score3 := max(0, trend * 10) * 0.2 * 100
    
    // 因子 4: 系统负载 (load average)(权重 10%)
    loadAvg := node.LoadAverage1Min
    cpuNum := node.CPUNum
    loadRatio := loadAvg / float64(cpuNum)
    score4 := min(loadRatio, 2.0) * 0.1 * 100
    
    totalScore := score1 + score2 + score3 + score4
    return min(totalScore, 100)
}

压力等级分类

CPU 压力分数          等级        策略
──────────────────────────────────────────
0-40                LOW         放松限制
40-60               MEDIUM      轻微抑制
60-75               HIGH        积极抑制
75-90               CRITICAL    驱逐 BE
90-100              EMERGENCY   紧急驱逐

示例计算:

场景 1: 正常负载
├─ 当前使用率: 50% → score1 = 50 * 0.4 = 20
├─ LS ratio: 0.8 → score2 = 0.8 * 0.3 * 100 = 24
├─ 趋势: 0.5%/min → score3 = 0.5 * 10 * 0.2 * 100 = 10
├─ 负载: 4.0 / 8 CPU = 0.5 → score4 = 0.5 * 0.1 * 100 = 5
└─ 总分: 59 → MEDIUM(轻微抑制)

场景 2: 高负载
├─ 当前使用率: 85% → score1 = 85 * 0.4 = 34
├─ LS ratio: 1.5 → score2 = 1.5 * 0.3 * 100 = 45
├─ 趋势: 2%/min → score3 = 2 * 10 * 0.2 * 100 = 40 (cap at 20)
├─ 负载: 12.0 / 8 = 1.5 → score4 = 1.5 * 0.1 * 100 = 15
└─ 总分: 94 → EMERGENCY(紧急驱逐)

内存压力检测

// 内存压力评分算法
func calculateMemoryPressure(node *NodeMetrics) float64 {
    // 因子 1: 当前内存使用率(权重 35%)
    memUsagePercent := (node.MemTotal - node.MemAvailable) / 
                       node.MemTotal * 100
    score1 := memUsagePercent * 0.35
    
    // 因子 2: LS Pod 的 RSS 增长(权重 25%)
    lsMemGrowth := calculateMemoryGrowthRate(node.LSPodMemHistory)
    // > 5%/min 认为异常
    score2 := min(lsMemGrowth * 5, 100) * 0.25
    
    // 因子 3: 内存回收压力(权重 20%)
    // 检查 kswapd 活跃度、direct reclaim
    reclaimPressure := node.PageScanDirect / node.PageScanKswapd
    score3 := min(reclaimPressure * 10, 100) * 0.2
    
    // 因子 4: Swap 使用(权重 20%)
    swapUsagePercent := node.SwapUsed / node.SwapTotal * 100
    score4 := swapUsagePercent * 0.2
    
    totalScore := score1 + score2 + score3 + score4
    return min(totalScore, 100)
}

7.5 六大策略插件详解

7.5.1 CPUSuppress - CPU 抑制策略

工作原理

CPUSuppress 的目标:
└─ 动态调整 BE Pod 的 CPU quota
└─ 保障 LS Pod 的 CPU 资源

抑制算法:

输入:
├─ CPU 压力分数 (0-100)
├─ LS Pod 的 CPU 需求
├─ BE Pod 当前的 CPU 使用
└─ 节点总 CPU 数

输出:
└─ BE Pod 的新 CPU quota(限制值)

公式:

available_cpu = total_cpu - ls_request - system_reserved
be_cpu_limit = available_cpu * suppress_ratio

suppress_ratio 的计算:
├─ 压力 0-40:   ratio = 1.0 (不抑制)
├─ 压力 40-60:  ratio = 0.8 (轻微抑制)
├─ 压力 60-75:  ratio = 0.5 (中度抑制)
├─ 压力 75-90:  ratio = 0.3 (重度抑制)
└─ 压力 90-100: ratio = 0.1 (极限抑制)

示例:

节点配置:
├─ 总 CPU: 16 核
├─ LS request: 6 CPU
├─ 系统预留: 1 CPU
└─ 可用于 BE: 16 - 6 - 1 = 9 CPU

场景 1: 压力分数 50(MEDIUM)
├─ suppress_ratio = 0.8
├─ BE limit = 9 * 0.8 = 7.2 CPU
└─ 允许 BE 使用最多 7.2 CPU

场景 2: 压力分数 80(CRITICAL)
├─ suppress_ratio = 0.3
├─ BE limit = 9 * 0.3 = 2.7 CPU
└─ 大幅限制 BE,为 LS 留出缓冲

生产案例:在线服务 + 离线计算混部

场景配置:
├─ 节点: 32 CPU, 128 GB 内存
├─ LS Pod (在线 API): 8 个 Pod, 每个 request 2 CPU
├─ BE Pod (数据分析): 4 个 Pod, 无 request
└─ 目标: LS 延迟 < 50ms, BE 尽量多跑

时间线:

T=08:00  低谷期
         ├─ LS 实际使用: 4 CPU (50% request)
         ├─ CPU 压力: 25 (LOW)
         ├─ suppress_ratio: 1.0
         ├─ BE limit: (32 - 16 - 2) * 1.0 = 14 CPU
         └─ BE 实际使用: 12 CPU
         └─ 节点利用率: (4 + 12) / 32 = 50%

T=10:00  流量上升
         ├─ LS 实际使用: 12 CPU (75% request)
         ├─ CPU 压力: 55 (MEDIUM)
         ├─ suppress_ratio: 0.8
         ├─ BE limit: 14 * 0.8 = 11.2 CPU
         └─ BE 被动减少到 11 CPU
         └─ 节点利用率: (12 + 11) / 32 = 72%

T=11:00  流量高峰
         ├─ LS 实际使用: 18 CPU (112% request,突发)
         ├─ CPU 压力: 78 (CRITICAL)
         ├─ suppress_ratio: 0.3
         ├─ BE limit: 14 * 0.3 = 4.2 CPU
         └─ BE 被大幅抑制
         └─ 节点利用率: (18 + 4) / 32 = 69%

T=12:00  流量回落
         ├─ LS 实际使用: 10 CPU
         ├─ CPU 压力: 48 (MEDIUM)
         ├─ suppress_ratio: 0.8
         ├─ BE limit 逐步放松: 11.2 CPU
         └─ BE 恢复处理能力

观察指标:
├─ LS P99 延迟: 全天 < 50ms ✅
├─ LS SLO: 99.95% ✅
├─ BE 吞吐量: 高峰期下降 60%,但非高峰期充分利用
└─ 整体利用率: 平均 65%(相比无混部的 40% 提升显著)

7.5.2 CPUEvict - CPU 驱逐策略

工作原理

CPUEvict 的触发条件:
├─ CPU 压力 > 90(EMERGENCY)
├─ 或 LS Pod 延迟 > SLO 阈值
├─ 且 CPUSuppress 已经执行但不够
└─ 需要彻底释放资源

驱逐决策流程:

步骤 1: 确认需要驱逐
    if cpu_pressure > 90 and ls_latency > slo_threshold:
        need_evict = true
    else:
        need_evict = false

步骤 2: 计算需要释放的 CPU
    ls_shortage = ls_actual_usage - ls_available
    # ls_available = total - other_ls - system
    
    target_free_cpu = ls_shortage * 1.2  # 留 20% 缓冲

步骤 3: 选择驱逐目标(按优先级)
    candidates = get_be_pods()
    sort(candidates, key=lambda p: (
        -p.priority,           # 优先级低的先驱逐
        -p.cpu_usage,          # 占用多的先驱逐
        p.creation_time        # 新创建的先驱逐
    ))

步骤 4: 逐个驱逐直到满足目标
    freed_cpu = 0
    evicted_pods = []
    for pod in candidates:
        if freed_cpu >= target_free_cpu:
            break
        evict(pod)
        freed_cpu += pod.cpu_usage
        evicted_pods.append(pod)

步骤 5: 记录和通知
    log("Evicted %d BE pods, freed %.2f CPU", 
        len(evicted_pods), freed_cpu)
    emit_event("CPUEviction", evicted_pods)

生产案例:突发流量导致紧急驱逐

场景:电商大促活动,流量突然激增

初始状态:
├─ 节点: 64 CPU
├─ LS Pod (购物车服务): 30 个 Pod, request 1 CPU/pod
├─ BE Pod (推荐计算): 10 个 Pod, 平均 3 CPU/pod
└─ 正常流量: LS 使用 20 CPU, BE 使用 30 CPU

时间线:

T=20:00:00  大促开始前
            ├─ CPU 压力: 55 (MEDIUM)
            ├─ LS 延迟: 30ms ✅
            ├─ BE 被轻微抑制到 25 CPU

T=20:00:05  流量激增(10 倍)
            ├─ LS 请求数从 1000 QPS → 10000 QPS
            ├─ LS CPU 需求暴涨: 20 → 50 CPU
            ├─ CPU 压力: 快速上升

T=20:00:10  CPUSuppress 响应
            ├─ CPU 压力: 85 (CRITICAL)
            ├─ BE limit 收紧: 25 → 8 CPU
            ├─ 但 LS 仍然不够,延迟开始增加

T=20:00:15  延迟违反 SLO
            ├─ LS 延迟: 150ms ❌ (SLO < 100ms)
            ├─ CPU 压力: 92 (EMERGENCY)
            ├─ LS 实际需求: 52 CPU
            ├─ LS 可用: 64 - 8 (BE) - 4 (system) = 52 CPU
            └─ 仍不够!(因为 BE 仍在使用 8 CPU)

T=20:00:16  触发 CPUEvict
            ├─ 决策: 需要释放 8 CPU
            ├─ 选择: 驱逐 3 个最低优先级的 BE Pod
            ├─ 释放: 3 * 3 = 9 CPU
            └─ 执行驱逐

T=20:00:20  驱逐完成
            ├─ LS 可用: 64 - 4 = 60 CPU
            ├─ LS 实际使用: 52 CPU
            ├─ 缓冲: 8 CPU
            └─ LS 延迟恢复: 50ms ✅

T=20:30:00  流量回落
            ├─ LS 需求降到 35 CPU
            ├─ CPU 压力降到 60
            ├─ 允许被驱逐的 BE Pod 重新调度回来
            └─ 恢复正常混部

驱逐效果:
├─ SLO 违反时间: 仅 5 秒(从 T=20:00:15 到 T=20:00:20)
├─ LS 可用性: 99.998%(5s / 30min)
├─ BE 影响: 3 个 Pod 被驱逐,10 分钟后重新调度
└─ 业务影响: LS 服务未受明显影响,用户体验良好

7.5.3 MemoryEvict - 内存驱逐策略

工作原理

MemoryEvict 的触发条件:
├─ 内存压力 > 80
├─ 或检测到 OOM 即将发生
├─ 或 LS Pod 内存增长异常
└─ 需要快速释放内存

内存驱逐的特殊性:
├─ 不能像 CPU 一样"抑制"(内存已分配无法收回)
├─ 只能通过驱逐 Pod 来释放内存
└─ 需要更谨慎,避免误杀

驱逐算法:

步骤 1: 计算需要释放的内存
    mem_shortage = ls_memory_request - mem_available
    target_free_mem = mem_shortage * 1.3  # 留 30% 缓冲

步骤 2: 选择驱逐目标
    candidates = get_be_pods()
    
    # 优先驱逐内存泄漏的 Pod
    leaking_pods = filter(lambda p: 
        p.memory_growth_rate > 5MB/min, candidates)
    
    # 其次驱逐内存占用大的 Pod
    sort(candidates, key=lambda p: -p.memory_rss)

步骤 3: 驱逐并验证
    for pod in candidates:
        if freed_mem >= target_free_mem:
            break
        evict(pod)
        wait_for_termination(pod, timeout=30s)
        freed_mem += pod.memory_rss

生产案例:内存泄漏检测与驱逐

场景:某 BE Pod 存在内存泄漏

时间线:

T=Day 0 00:00  Pod 创建
               ├─ 内存: 1 GB
               ├─ 内存压力: 40 (LOW)

T=Day 0 12:00  检测到异常增长
               ├─ 内存: 6 GB
               ├─ 增长速度: 500 MB/hour
               ├─ MetricAdvisor 标记为"疑似泄漏"

T=Day 1 00:00  内存继续增长
               ├─ 内存: 12 GB
               ├─ 节点可用内存: 20 GB(从 120 GB 降到 20 GB)
               ├─ 内存压力: 75 (HIGH)

T=Day 1 08:00  触发告警
               ├─ 内存: 16 GB
               ├─ 内存压力: 85 (CRITICAL)
               ├─ QOSManager 决策: 准备驱逐

T=Day 1 08:10  LS Pod 开始受影响
               ├─ LS Pod 内存分配失败(cgroup limit)
               ├─ LS 延迟增加(内存回收导致)
               ├─ 内存压力: 90 (EMERGENCY)

T=Day 1 08:11  触发 MemoryEvict
               ├─ 识别: BE Pod A 内存 16 GB(异常)
               ├─ 决策: 驱逐 Pod A
               ├─ 执行: 发送 SIGTERM

T=Day 1 08:11:30  Pod 优雅关闭(30秒)
                  ├─ 释放内存: 16 GB
                  ├─ 节点可用: 36 GB
                  ├─ 内存压力: 55 (MEDIUM)

T=Day 1 08:12  LS 恢复正常
               ├─ LS 延迟: 恢复到 < 100ms
               ├─ 内存分配正常
               └─ SLO 恢复

后续处理:
├─ Pod A 被标记为"不可重新调度"
├─ 通知开发团队修复内存泄漏
├─ 修复后的新版本重新部署
└─ 避免再次出现问题

效果评估:
├─ LS 影响时间: 2 分钟(从检测到恢复)
├─ BE 影响: 1 个 Pod 被驱逐,修复后重新上线
└─ 防止了更严重的节点 OOM(可能导致 LS Pod 被杀)

7.5.4 CPUBurst - CPU 突发策略

工作原理

CPUBurst 的目标:
└─ 允许 LS Pod 在短时间内超过 limit 使用 CPU
└─ 提升突发性能,但不影响整体稳定性

Kubernetes 原生 CPU limit 的问题:
├─ limit=2 CPU: 严格限制,即使节点空闲也不能超
├─ 导致: 突发流量时,CPU 充足但 Pod 被限流
└─ 结果: 延迟增加,用户体验变差

CPUBurst 的解决方案:
├─ 检测节点 CPU 是否有富余
├─ 如果有富余,临时提高 LS Pod 的 CPU quota
└─ 允许短时间超过 limit(burst)

算法:

输入:
├─ LS Pod 的 limit: 2 CPU
├─ LS Pod 当前使用: 1.8 CPU(接近 limit)
├─ 节点剩余 CPU: 8 CPU(充足)
└─ LS Pod 的请求队列: 积压 100 个请求

输出:
└─ 临时提升 limit 到 3 CPU(允许 burst 50%)

burst_ratio 的计算:
├─ 如果节点 CPU 空闲 > 30%: burst_ratio = 1.5 (允许 50% burst)
├─ 如果节点 CPU 空闲 20-30%: burst_ratio = 1.3
├─ 如果节点 CPU 空闲 < 20%: burst_ratio = 1.0 (不 burst)

new_limit = original_limit * burst_ratio

限制条件:
├─ burst 持续时间: 最多 10 秒
├─ burst 后冷却: 至少 30 秒
└─ 防止持续 burst 导致节点过载

生产案例:API 服务的突发响应

场景:在线 API 服务处理突发请求

Pod 配置:
├─ request: 1 CPU
├─ limit: 2 CPU
├─ 正常 QPS: 500
├─ 突发 QPS: 2000

时间线:

T=10:00:00  正常流量
            ├─ QPS: 500
            ├─ CPU 使用: 1.2 CPU
            ├─ P99 延迟: 20ms ✅

T=10:00:10  突发流量到达
            ├─ QPS: 500 → 2000(4 倍)
            ├─ CPU 需求: 1.2 → 4.8 CPU(理论)
            ├─ 但 limit=2 CPU,被限流

T=10:00:11  没有 CPUBurst 的情况
            ├─ CPU 被限制在 2 CPU
            ├─ 请求队列积压
            ├─ P99 延迟: 200ms ❌
            ├─ 部分请求超时(> 500ms)

T=10:00:11  使用 CPUBurst 的情况
            ├─ 检测: 节点剩余 CPU = 10 CPU(充足)
            ├─ 决策: 允许 burst,ratio = 1.5
            ├─ 新 limit: 2 * 1.5 = 3 CPU
            ├─ CPU 使用: 2.8 CPU(burst 中)
            ├─ P99 延迟: 50ms ✅(可接受)

T=10:00:20  突发结束
            ├─ QPS 降回 500
            ├─ CPU 使用: 1.2 CPU
            ├─ 恢复原 limit: 2 CPU
            ├─ 冷却 30 秒

T=10:00:50  冷却完成
            ├─ 允许下一次 burst

效果对比:

不使用 CPUBurst:
├─ P99 延迟: 200ms ❌
├─ 超时率: 5%
├─ 用户投诉: 较多

使用 CPUBurst:
├─ P99 延迟: 50ms ✅
├─ 超时率: 0.1%
├─ 用户体验: 良好

关键优势:
└─ 在不改变 Pod 配置的情况下,动态应对突发
└─ 充分利用节点空闲资源
└─ 提升服务质量

7.6 策略执行的优先级和协调

多策略的执行顺序

QOSManager 的插件执行顺序:

1. NodeQOSResource(节点级资源计算)
   └─ 计算节点的总可用资源
   └─ 为后续策略提供基础数据

2. SystemQOSResource(系统级资源预留)
   └─ 为系统组件预留资源
   └─ 防止系统组件被混部影响

3. CPUBurst(LS 突发优先)
   └─ 优先满足 LS 的突发需求
   └─ 如果有资源,先给 LS burst

4. CPUSuppress(BE 抑制)
   └─ 根据压力抑制 BE
   └─ 释放资源给 LS

5. CPUEvict(紧急驱逐)
   └─ 如果抑制不够,进行驱逐
   └─ 确保 LS 的 SLO

6. MemoryEvict(内存驱逐)
   └─ 处理内存问题
   └─ 独立于 CPU 策略

执行协调机制:

冲突检测:
├─ 如果 CPUBurst 给 LS 增加了 CPU
├─ 同时 CPUSuppress 要减少 BE 的 CPU
├─ 需要确保: LS burst + BE suppress <= 节点总 CPU

协调规则:
├─ LS 优先: 先满足 LS 的 burst
├─ 再抑制 BE: 根据剩余资源抑制 BE
└─ 最后驱逐: 如果仍不够,驱逐 BE

示例:

节点: 16 CPU
LS Pod: limit 4 CPU, 当前 3.8 CPU(接近满)
BE Pod: 当前 10 CPU

步骤 1: NodeQOSResource
    └─ 可用 CPU: 16 - 1 (system) = 15 CPU

步骤 2: CPUBurst
    └─ LS 需要 burst,检测到剩余 2 CPU
    └─ 允许 LS burst 到 5 CPU
    └─ 剩余: 15 - 5 = 10 CPU

步骤 3: CPUSuppress
    └─ BE 可用: 10 CPU
    └─ 当前 BE 使用 10 CPU,压力 MEDIUM
    └─ 抑制到: 10 * 0.8 = 8 CPU

步骤 4: 验证
    └─ LS: 5 CPU ✅
    └─ BE: 8 CPU ✅
    └─ 总: 13 CPU < 15 CPU ✅

生产调优指南

7.7 QOSManager 配置参数

# QOSManager 配置示例
apiVersion: v1
kind: ConfigMap
metadata:
  name: koordlet-config
  namespace: koordinator-system
data:
  koordlet-config.yaml: |
    qosManager:
      # 执行周期
      syncPeriod: 1s  # 每秒执行一次决策
      
      # CPU 压力阈值
      cpuPressure:
        low: 40       # 低压力
        medium: 60    # 中压力
        high: 75      # 高压力
        critical: 90  # 紧急压力
      
      # 内存压力阈值
      memoryPressure:
        low: 50
        medium: 70
        high: 80
        critical: 90
      
      # CPUSuppress 配置
      cpuSuppress:
        enabled: true
        suppressRatio:
          low: 1.0      # 不抑制
          medium: 0.8   # 轻微抑制
          high: 0.5     # 中度抑制
          critical: 0.3 # 重度抑制
          emergency: 0.1 # 极限抑制
      
      # CPUEvict 配置
      cpuEvict:
        enabled: true
        evictThreshold: 90  # 压力超过 90 触发驱逐
        evictCooldown: 60s  # 驱逐后冷却 60 秒
      
      # MemoryEvict 配置
      memoryEvict:
        enabled: true
        evictThreshold: 85
        memoryGrowthThreshold: 5  # 5 MB/min
        oomScoreAdjust: 1000      # BE Pod 的 OOM 分数
      
      # CPUBurst 配置
      cpuBurst:
        enabled: true
        burstRatio: 1.5           # 最多 burst 50%
        burstDuration: 10s        # burst 持续最多 10 秒
        burstCooldown: 30s        # burst 后冷却 30 秒
        minNodeCPUIdle: 0.2       # 节点至少 20% 空闲才允许 burst
      
      # 策略插件启用列表
      plugins:
        - NodeQOSResource
        - SystemQOSResource
        - CPUBurst
        - CPUSuppress
        - CPUEvict
        - MemoryEvict

7.8 调参指南

场景 1:在线服务为主(延迟敏感)

# 优化目标:保障 LS 延迟,BE 可牺牲
qosManager:
  cpuPressure:
    medium: 50      # 降低阈值,提前响应
    high: 65
    critical: 80
  
  cpuSuppress:
    suppressRatio:
      medium: 0.7   # 更激进的抑制
      high: 0.4
      critical: 0.2
  
  cpuBurst:
    enabled: true
    burstRatio: 2.0  # 允许更大的 burst
    minNodeCPUIdle: 0.15  # 降低要求,更容易 burst

场景 2:离线计算为主(吞吐优先)

# 优化目标:充分利用资源,LS 要求不高
qosManager:
  cpuPressure:
    medium: 70      # 提高阈值,减少干预
    high: 85
    critical: 95
  
  cpuSuppress:
    suppressRatio:
      medium: 0.9   # 轻微抑制
      high: 0.7
      critical: 0.5
  
  cpuEvict:
    enabled: false  # 禁用驱逐,避免影响 BE 吞吐

场景 3:混部比例 1:1(均衡)

# 优化目标:LS 和 BE 的平衡
qosManager:
  cpuPressure:
    medium: 60
    high: 75
    critical: 90
  
  cpuSuppress:
    suppressRatio:
      medium: 0.8
      high: 0.5
      critical: 0.3

7.9 监控指标

# QOSManager 关键监控指标

# CPU 压力分数
koordlet_qos_cpu_pressure_score

# 内存压力分数
koordlet_qos_memory_pressure_score

# CPUSuppress 执行次数
koordlet_qos_cpu_suppress_total

# CPUEvict 驱逐次数
koordlet_qos_cpu_evict_total{reason="cpu_pressure"}

# MemoryEvict 驱逐次数
koordlet_qos_memory_evict_total{reason="oom_risk"}

# CPUBurst 触发次数
koordlet_qos_cpu_burst_total

# BE Pod CPU 限制值
koordlet_qos_be_cpu_limit_cores

# 决策延迟
koordlet_qos_decision_latency_milliseconds

7.10 常见问题排查

问题 1:LS Pod 延迟仍然过高

诊断步骤:

1. 检查 CPU 压力分数
   $ kubectl logs koordlet-xxx | grep cpu_pressure
   
   如果压力分数 < 60,但延迟高:
   └─ 可能不是 CPU 问题,检查网络、磁盘

2. 检查 BE 是否被充分抑制
   $ kubectl exec koordlet-xxx -- cat /sys/fs/cgroup/.../cpu.cfs_quota_us
   
   如果 quota 仍然很高:
   └─ cpuSuppress 可能未生效,检查配置

3. 检查是否有驱逐发生
   $ kubectl get events | grep Evicted
   
   如果没有驱逐:
   └─ evictThreshold 可能设置过高,降低阈值

解决方案:
├─ 降低 cpuPressure.medium 到 50
├─ 增加 suppressRatio 的激进程度
└─ 启用 CPUEvict,设置 evictThreshold=85

问题 2:BE Pod 频繁被驱逐

诊断步骤:

1. 检查驱逐频率
   $ kubectl logs koordlet-xxx | grep "CPUEvict"
   
   如果每分钟多次:
   └─ evictThreshold 设置过低

2. 检查 CPU 压力波动
   $ promql: koordlet_qos_cpu_pressure_score
   
   如果压力波动频繁跨越阈值:
   └─ 需要增加平滑或冷却时间

解决方案:
├─ 提高 evictThreshold 到 95
├─ 增加 evictCooldown 到 120s
└─ 启用压力分数的滑动平均(5 分钟窗口)

问题 3:CPUBurst 未生效

诊断步骤:

1. 检查是否启用
   $ kubectl get cm koordlet-config -o yaml | grep cpuBurst
   
   如果 enabled: false:
   └─ 启用该功能

2. 检查节点空闲 CPU
   $ kubectl exec koordlet-xxx -- top
   
   如果空闲 < 20%:
   └─ minNodeCPUIdle 条件不满足

解决方案:
├─ 降低 minNodeCPUIdle 到 0.1
├─ 或增加节点资源
└─ 或减少 BE Pod 数量

总结 - 章节要点汇总

7.11 关键概念速查

概念含义触发条件
CPUSuppress动态抑制 BE CPU压力 > 40
CPUEvict驱逐 BE Pod压力 > 90
MemoryEvict驱逐内存异常 Pod压力 > 85 或 OOM 风险
CPUBurstLS 临时超 limit节点空闲 > 20%
压力分数0-100 的资源压力评估多维度计算

7.12 决策流程总结

QOSManager 的完整决策流程:

1. 数据采集(1ms)
   └─ 从 StatesInformer 和 MetricCache 获取数据

2. 压力评估(5ms)
   └─ 计算 CPU、内存、I/O 压力分数

3. 策略决策(10ms)
   └─ 执行 6 大插件,生成操作指令

4. 执行协调(50ms)
   └─ 调用 ResourceExecutor 更新 CGroup

5. 效果验证(下一轮)
   └─ 监控执行效果,调整策略

总耗时:< 100ms(从检测到执行)

本章要点

  • 理解 QOSManager 的决策引擎架构和工作流程
  • 掌握 CPU 和内存压力评分的计算方法
  • 学会 6 大策略插件的工作原理和应用场景
  • 理解多策略的执行顺序和冲突协调机制
  • 掌握不同业务场景下的调参方法