核心使命与设计理念
7.1 What - QOSManager 是什么?
QOSManager 是 Koordlet 中的资源协调决策引擎,负责根据节点资源状态和 Pod QoS 等级,动态调整资源分配和隔离策略。
核心职责:
- 监控节点资源压力(CPU、内存、I/O)
- 根据 Pod QoS 等级制定差异化策略
- 决策资源抑制、驱逐、隔离操作
- 协调多个 QoS 策略插件的执行顺序
- 保障 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 风险 |
| CPUBurst | LS 临时超 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 大策略插件的工作原理和应用场景
- 理解多策略的执行顺序和冲突协调机制
- 掌握不同业务场景下的调参方法