核心使命与设计理念
9.1 What - 驱逐机制是什么?
驱逐(Eviction)是 Koordinator 在资源压力严重时,主动终止低优先级 Pod 以释放资源,保障高优先级 Pod 的 SLO 的最后手段。
核心目标:
- 紧急资源释放:在抑制无效时,快速释放资源
- 优先级保障:优先驱逐 BE Pod,保护 LS/LSR/LSE Pod
- 最小影响:选择影响最小的 Pod 进行驱逐
- 可控性:支持驱逐策略配置和驱逐速率控制
- 可恢复:被驱逐的 Pod 可以重新调度回来
驱逐触发条件:
┌─────────────────────────────────────────────┐
│ Koordinator 驱逐触发条件 │
├─────────────────────────────────────────────┤
│ │
│ CPU 驱逐触发: │
│ ├─ CPU 压力 > 90 (EMERGENCY) │
│ ├─ CPUSuppress 已执行但不够 │
│ ├─ LS Pod 延迟 > SLO 阈值 │
│ └─ 需要快速释放 CPU │
│ │
│ 内存驱逐触发: │
│ ├─ 内存压力 > 85 (CRITICAL) │
│ ├─ 检测到 OOM 即将发生 │
│ ├─ LS Pod 内存分配失败 │
│ └─ 需要快速释放内存 │
│ │
│ 主动驱逐(预测性): │
│ ├─ PredictServer 预测未来压力 > 阈值 │
│ ├─ 提前 10 分钟驱逐 BE Pod │
│ └─ 防患于未然 │
│ │
│ 故障驱逐: │
│ ├─ Pod 连续 OOM 3 次 │
│ ├─ Pod 内存泄漏持续 1 小时 │
│ └─ 驱逐故障 Pod,防止影响节点 │
│ │
└─────────────────────────────────────────────┘
9.2 Why - 为什么需要驱逐机制?
问题 1:抑制无法解决内存问题
CPU vs 内存的本质区别:
CPU 可以抑制:
├─ 降低 CPU quota
├─ 进程被限流,但继续运行
└─ 性能下降,但不会失败
内存无法抑制:
├─ 内存已分配,无法收回(除非回收 page cache)
├─ 如果内存不够,进程会 OOM
├─ 唯一方法:驱逐 Pod,释放内存
└─ 抑制对内存问题无效
场景示例:
节点: 128 GB 内存
├─ LS Pod: 使用 60 GB
├─ BE Pod: 使用 50 GB
└─ 总使用: 110 GB
T=10:00 BE Pod 内存泄漏
├─ BE 使用: 50 → 70 GB
├─ 总使用: 130 GB > 128 GB
├─ 内存压力: 严重
│
├─ CPUSuppress 无效(内存无法抑制)
├─ 内核开始回收 page cache
├─ LS Pod 性能下降
│
└─ 唯一解决方案:驱逐 BE Pod
问题 2:需要快速响应紧急情况
对比:抑制 vs 驱逐
CPUSuppress(渐进式):
├─ 响应时间: 1-5 秒
├─ 效果: 渐进释放 CPU
├─ 影响: BE 性能下降,但继续运行
└─ 适用: 中等压力(60-90)
CPUEvict(紧急式):
├─ 响应时间: < 1 秒
├─ 效果: 立即释放全部资源
├─ 影响: BE Pod 终止
└─ 适用: 紧急压力(> 90)
紧急场景:
T=10:00:00 电商大促,流量突然 10 倍
├─ LS 需求: 10 → 100 CPU
├─ 节点总: 64 CPU
├─ CPU 压力: 95 (EMERGENCY)
T=10:00:01 CPUSuppress 执行
├─ BE quota 降低到 0.1
├─ 但 BE 仍占用一定资源
├─ 释放的 CPU 不够
T=10:00:02 CPUEvict 触发
├─ 驱逐 5 个 BE Pod
├─ 立即释放 20 CPU
├─ LS 可用 CPU 增加
T=10:00:05 LS 延迟恢复正常
└─ 从 SLO 违反到恢复,仅 5 秒
问题 3:需要淘汰故障 Pod
故障 Pod 的危害:
场景 1: 内存泄漏 Pod
├─ BE Pod 每小时泄漏 1 GB
├─ 持续运行 10 小时,泄漏 10 GB
├─ 即使被 OOM 杀死,kubelet 会重启
├─ 重启后继续泄漏
└─ 需要:驱逐并标记为"不可调度"
场景 2: 频繁 OOM 的 Pod
├─ Pod 连续 OOM 3 次
├─ 说明 Pod 的 limit 配置不合理
├─ 或 Pod 本身有 bug
├─ 继续运行会影响节点稳定性
└─ 需要:驱逐并通知用户修复
场景 3: 异常 CPU 消耗 Pod
├─ BE Pod 持续使用 100% CPU
├─ 即使被抑制,仍不释放
├─ 可能是死循环或计算错误
├─ 浪费资源
└─ 需要:驱逐并分析原因
9.3 How - 驱逐机制的实现架构
┌──────────────────────────────────────────────┐
│ Koordinator 驱逐流程 │
├──────────────────────────────────────────────┤
│ │
│ 1. 触发检测 │
│ ├─ QOSManager 检测资源压力 │
│ ├─ 判断是否达到驱逐阈值 │
│ └─ 决定驱逐原因(CPU/内存/故障) │
│ │
│ 2. 目标选择 │
│ ├─ 获取候选 Pod 列表(BE Pod) │
│ ├─ 按优先级排序 │
│ ├─ 计算需要释放的资源量 │
│ └─ 选择最优驱逐目标 │
│ │
│ 3. 驱逐执行 │
│ ├─ 调用 Kubernetes Eviction API │
│ ├─ 记录驱逐事件 │
│ ├─ 等待 Pod 优雅关闭(最多 30s) │
│ └─ 验证资源释放 │
│ │
│ 4. 反馈验证 │
│ ├─ 监控资源压力变化 │
│ ├─ 验证 LS Pod SLO 是否恢复 │
│ ├─ 记录驱逐效果 │
│ └─ 调整下一轮策略 │
│ │
│ 5. 冷却和恢复 │
│ ├─ 驱逐后冷却 60 秒 │
│ ├─ 防止频繁驱逐 │
│ ├─ 压力降低后,允许 BE Pod 重新调度 │
│ └─ 恢复正常混部 │
│ │
└──────────────────────────────────────────────┘
驱逐策略详解
9.4 驱逐目标选择算法
优先级排序规则:
驱逐目标选择优先级(从高到低):
1. QoS 等级优先级
BE > LS > LSR > LSE
└─ 优先驱逐 BE Pod,绝不驱逐 LSE Pod
2. Pod 优先级(Priority)
同 QoS 等级内,按 Pod Priority 排序
└─ 优先驱逐低 Priority 的 Pod
3. 资源占用量
同 Priority 内,按资源占用排序
└─ 优先驱逐占用多的 Pod(释放更多资源)
4. 运行时长
同占用量时,按运行时长排序
└─ 优先驱逐新创建的 Pod(影响小)
5. 故障频率
同运行时长时,按故障频率排序
└─ 优先驱逐频繁 OOM 的 Pod
选择算法:
func SelectEvictionTarget(pods []Pod, targetResource float64) []Pod {
// 步骤 1: 过滤候选 Pod(只选 BE)
candidates := filterByQoS(pods, "BE")
// 步骤 2: 多维度排序
sort(candidates, func(a, b Pod) bool {
if a.Priority != b.Priority {
return a.Priority < b.Priority
}
if a.ResourceUsage != b.ResourceUsage {
return a.ResourceUsage > b.ResourceUsage
}
if a.RunningTime != b.RunningTime {
return a.RunningTime < b.RunningTime
}
return a.OOMCount > b.OOMCount
})
// 步骤 3: 逐个选择直到满足目标
selected := []Pod{}
freed := 0.0
for _, pod := range candidates {
if freed >= targetResource {
break
}
selected = append(selected, pod)
freed += pod.ResourceUsage
}
return selected
}
生产案例:CPU 驱逐的目标选择
场景:节点 CPU 压力 95,需要释放 10 CPU
候选 BE Pod 列表:
┌────────────────────────────────────────────────────┐
│ Pod | Priority | CPU使用 | 运行时长 | OOM次数 │
├────────────────────────────────────────────────────┤
│ BE-1 | 0 | 5 CPU | 2天 | 0 │
│ BE-2 | -10 | 8 CPU | 1天 | 1 │
│ BE-3 | 0 | 3 CPU | 5小时 | 0 │
│ BE-4 | -10 | 6 CPU | 3天 | 2 │
│ BE-5 | 10 | 4 CPU | 1小时 | 0 │
└────────────────────────────────────────────────────┘
排序过程:
步骤 1: 按 Priority 排序(低的先)
├─ Priority -10: BE-2, BE-4
├─ Priority 0: BE-1, BE-3
└─ Priority 10: BE-5
步骤 2: 同 Priority 内按 CPU 使用排序(高的先)
├─ Priority -10: BE-2 (8 CPU), BE-4 (6 CPU)
├─ Priority 0: BE-1 (5 CPU), BE-3 (3 CPU)
└─ Priority 10: BE-5 (4 CPU)
步骤 3: 同使用量按 OOM 次数(高的先)
├─ BE-4 的 OOM 次数 (2) > BE-2 的 OOM 次数 (1)
└─ BE-4 应优先于 BE-2
最终排序:
1. BE-4: Priority -10, CPU 6, OOM 2 ← 最优先
2. BE-2: Priority -10, CPU 8, OOM 1
3. BE-1: Priority 0, CPU 5
4. BE-3: Priority 0, CPU 3
5. BE-5: Priority 10, CPU 4 ← 最后
选择结果:
需要释放 10 CPU
├─ 选择 BE-4: 释放 6 CPU(累计 6)
├─ 选择 BE-2: 释放 8 CPU(累计 14)
└─ 满足目标 10 CPU,留有余量
驱逐执行:
├─ BE-4 和 BE-2 被驱逐
├─ 释放 14 CPU(超过目标,安全缓冲)
└─ BE-1, BE-3, BE-5 继续运行
优势:
├─ 优先驱逐低 Priority 的 Pod
├─ 优先驱逐频繁 OOM 的 Pod(淘汰故障)
├─ 释放资源超过目标(留缓冲)
└─ 高 Priority 的 BE-5 未受影响
9.5 驱逐速率控制
为什么需要速率控制?
无速率控制的风险:
场景:节点压力波动
T=10:00 压力 95 → 驱逐 5 个 BE Pod
T=10:01 压力降到 70
T=10:02 压力升到 92 → 再次驱逐 4 个 BE Pod
T=10:03 压力降到 65
T=10:05 压力升到 94 → 再次驱逐 3 个 BE Pod
问题:
├─ 10 分钟内驱逐了 12 个 Pod
├─ 频繁的驱逐和重新调度
├─ 集群调度器压力大
├─ Pod 启动开销浪费
└─ BE 作业无法完成,吞吐量为 0
速率控制目标:
├─ 防止频繁驱逐
├─ 给系统恢复时间
├─ 减少不必要的驱逐
└─ 平衡 LS 保护和 BE 可用性
速率控制策略:
Koordinator 的驱逐速率控制:
1. 驱逐冷却期(Cooldown)
├─ 每次驱逐后冷却 60 秒
├─ 冷却期内不再触发新的驱逐
└─ 给系统恢复的时间
2. 最大驱逐速率
├─ 每分钟最多驱逐 N 个 Pod
├─ 默认 N=5
└─ 防止大规模驱逐
3. 批量驱逐限制
├─ 单次驱逐最多 M 个 Pod
├─ 默认 M=3
└─ 避免一次性释放过多资源
4. 压力稳定性检测
├─ 压力持续 3 次检测周期(3秒)才触发
├─ 避免瞬时波动导致驱逐
└─ 确认压力是持续的
5. 自适应冷却
├─ 驱逐后压力快速恢复 → 缩短冷却期
├─ 驱逐后压力仍高 → 延长冷却期
└─ 动态调整策略
配置示例:
evictionConfig:
cooldownPeriod: 60s # 冷却期
maxEvictionPerMinute: 5 # 每分钟最多驱逐数
maxEvictionPerBatch: 3 # 单次最多驱逐数
pressureStableThreshold: 3 # 压力稳定检测次数
adaptiveCooldown: true # 启用自适应冷却
生产案例:速率控制防止过度驱逐
场景:节点压力波动
不使用速率控制:
┌─────────────────────────────────────┐
│ T=10:00 压力 95 → 驱逐 5 个 Pod │
│ T=10:01 压力 70 → 恢复 │
│ T=10:02 压力 93 → 驱逐 4 个 Pod │
│ T=10:03 压力 68 → 恢复 │
│ T=10:05 压力 91 → 驱逐 3 个 Pod │
│ T=10:06 压力 65 → 恢复 │
│ │
│ 总计: │
│ ├─ 10 分钟驱逐 12 个 Pod │
│ ├─ BE 作业无法完成 │
│ └─ 调度器压力大 │
└─────────────────────────────────────┘
使用速率控制:
┌─────────────────────────────────────┐
│ T=10:00 压力 95 │
│ ├─ 持续检测 3 秒 │
│ └─ 确认压力稳定 │
│ │
│ T=10:03 触发驱逐 │
│ ├─ 驱逐 3 个 Pod(批量限制)│
│ ├─ 释放 9 CPU │
│ └─ 进入冷却期 60 秒 │
│ │
│ T=10:04 压力降到 70 │
│ ├─ 但仍在冷却期 │
│ └─ 不触发新驱逐 │
│ │
│ T=10:05 压力升到 93 │
│ ├─ 仍在冷却期 │
│ └─ 不触发驱逐 │
│ │
│ T=10:06 压力降到 65 │
│ └─ 压力已恢复 │
│ │
│ T=11:00 冷却期结束 │
│ ├─ 压力保持在 60-70 │
│ ├─ 无需驱逐 │
│ └─ 被驱逐的 Pod 可重新调度 │
│ │
│ 总计: │
│ ├─ 仅驱逐 3 个 Pod │
│ ├─ BE 作业大部分完成 │
│ └─ 调度器压力小 │
└─────────────────────────────────────┘
效果对比:
指标 不使用速率控制 使用速率控制
────────────────────────────────────────────────
驱逐 Pod 数 12 3
BE 作业完成率 0% 75%
调度器 QPS 峰值 300 100
LS SLO 达成率 99.9% 99.9%
结论:
├─ 速率控制在保持 LS SLO 的同时
├─ 大幅减少了不必要的驱逐
└─ 提高了 BE 作业的完成率
9.6 优雅驱逐机制
Kubernetes 的优雅关闭流程:
Pod 优雅关闭流程:
1. Eviction API 调用
└─ Koordinator 调用 Kubernetes Eviction API
2. Pod 进入 Terminating 状态
├─ Pod.DeletionTimestamp 被设置
└─ Pod 从 Service Endpoints 中移除
3. 发送 SIGTERM 信号
├─ kubelet 向容器主进程发送 SIGTERM
├─ 容器有机会优雅关闭
└─ 超时时间: terminationGracePeriodSeconds
4. 等待容器退出
├─ 等待容器主动退出
├─ 最长等待 terminationGracePeriodSeconds(默认30s)
└─ 超时后强制杀死
5. 发送 SIGKILL 信号(超时后)
├─ 强制终止容器
└─ 释放资源
6. Pod 被删除
└─ 从 API Server 中移除
Koordinator 的优雅驱逐增强:
优雅驱逐的改进:
1. 驱逐前通知
├─ 在驱逐前发送 PreStop Hook
├─ 给应用准备时间(保存状态、关闭连接)
└─ 可选: preStopDelaySeconds=10
2. 分批驱逐
├─ 不一次性驱逐所有目标 Pod
├─ 每批驱逐 3 个,间隔 10 秒
└─ 观察压力变化,避免过度驱逐
3. 验证驱逐效果
├─ 驱逐后立即检查资源压力
├─ 如果压力已缓解,停止继续驱逐
└─ 动态调整驱逐数量
4. 驱逐原因记录
├─ 记录详细的驱逐原因
├─ CPU 压力 95 / 内存压力 90 / OOM 风险
└─ 方便故障排查和审计
配置示例:
evictionConfig:
gracefulEviction:
enabled: true
preStopDelaySeconds: 10 # 驱逐前延迟
batchSize: 3 # 每批驱逐数
batchInterval: 10s # 批次间隔
verifyAfterEviction: true # 驱逐后验证
生产案例:优雅驱逐保护数据完整性
场景:数据库 BE Pod 被驱逐
不使用优雅驱逐:
┌─────────────────────────────────────┐
│ T=10:00:00 触发驱逐 │
│ ├─ 立即调用 Eviction API│
│ └─ Pod 进入 Terminating │
│ │
│ T=10:00:01 SIGTERM 发送 │
│ ├─ 数据库主进程收到信号 │
│ ├─ 但正在处理事务 │
│ └─ 事务未提交 │
│ │
│ T=10:00:30 超时,SIGKILL 发送 │
│ ├─ 强制终止进程 │
│ ├─ 事务回滚 │
│ ├─ 数据可能不一致 │
│ └─ 需要手动修复 │
│ │
│ 影响: │
│ ├─ 数据可能损坏 │
│ ├─ 需要运维介入 │
│ └─ 用户不满 │
└─────────────────────────────────────┘
使用优雅驱逐:
┌─────────────────────────────────────┐
│ T=10:00:00 触发驱逐 │
│ ├─ preStopDelay=10s │
│ └─ 先不调用 Eviction │
│ │
│ T=10:00:00 发送 PreStop Hook │
│ ├─ 通知应用即将关闭 │
│ ├─ 应用停止接受新请求 │
│ └─ 开始处理未完成事务 │
│ │
│ T=10:00:08 事务处理完成 │
│ ├─ 数据库提交所有事务 │
│ ├─ 关闭所有连接 │
│ └─ 准备好关闭 │
│ │
│ T=10:00:10 调用 Eviction API │
│ └─ Pod 进入 Terminating │
│ │
│ T=10:00:11 SIGTERM 发送 │
│ ├─ 数据库主进程收到信号 │
│ ├─ 没有未完成事务 │
│ └─ 立即退出 │
│ │
│ T=10:00:12 Pod 成功退出 │
│ ├─ 数据完整 │
│ ├─ 无需恢复 │
│ └─ 释放资源 │
│ │
│ 影响: │
│ ├─ 数据完整性保证 │
│ ├─ 无需运维介入 │
│ └─ 用户无感知 │
└─────────────────────────────────────┘
效果对比:
指标 不使用优雅驱逐 使用优雅驱逐
───────────────────────────────────────────────
数据完整性 可能损坏 100% 保证
驱逐时间 30s(强制) 12s(主动)
需要人工介入 是 否
用户影响 可能影响 无影响
9.7 预测性驱逐
基于趋势预测的主动驱逐:
预测性驱逐的目标:
└─ 在资源压力达到紧急状态前,提前驱逐 BE Pod
└─ 避免 LS Pod 受到影响
└─ 防患于未然
预测算法:
输入:
├─ 过去 1 小时的资源使用趋势
├─ 当前资源使用量
└─ 历史同时段的负载模式
输出:
└─ 预测未来 10 分钟的资源压力
算法流程:
1. 趋势计算
trend = (current_usage - usage_10min_ago) / 10min
# 例:CPU 从 60% → 80%,trend = 2%/min
2. 线性预测
predicted_usage = current_usage + trend * 10min
# 例:80% + 2%/min * 10min = 100%
3. 阈值判断
if predicted_usage > 90%:
trigger_预测性驱逐()
4. 提前量计算
advance_time = (90% - current_usage) / trend
# 例:(90% - 80%) / 2%/min = 5 分钟
# 意味着 5 分钟后会达到 90%
5. 驱逐量计算
evict_amount = predicted_usage - 80%
# 例:100% - 80% = 20% 的资源需要释放
生产案例:预测性驱逐避免 SLO 违反
场景:在线服务的负载增长预测
时间线:
T=10:00 当前状态
├─ CPU 使用: 60%
├─ 趋势: +2%/min(过去 10 分钟)
├─ 预测 10 分钟后: 80%
└─ 压力: 正常,无需操作
T=10:05 继续观察
├─ CPU 使用: 70%
├─ 趋势: +2.5%/min(加速)
├─ 预测 10 分钟后: 95%
└─ 压力: 开始上升
T=10:06 预测性驱逐触发
├─ 预测 5 分钟后会达到 90%
├─ 决策: 提前驱逐 BE Pod
├─ 目标: 释放 15% CPU
├─ 驱逐 2 个 BE Pod
└─ 释放 12 CPU (15%)
T=10:10 流量高峰到达
├─ LS 需求: 70 → 85% CPU
├─ 但已提前释放了 15% CPU
├─ 实际压力: 85% - 15% = 70%
└─ LS 延迟: < 50ms ✅
T=10:15 高峰持续
├─ LS 需求: 90% CPU
├─ 压力: 75%(已提前释放)
└─ LS SLO: 99.99% ✅
T=10:30 高峰结束
├─ LS 需求降到 60%
├─ 允许 BE Pod 重新调度
└─ 恢复正常混部
对比:不使用预测性驱逐
───────────────────────────
T=10:10 流量高峰到达
├─ LS 需求: 85% CPU
├─ 压力: 85%(未提前释放)
├─ CPUSuppress 开始执行
├─ 但反应较慢
T=10:11 压力继续上升
├─ LS 需求: 90% CPU
├─ 压力: 90%
├─ 触发 CPUEvict
├─ 紧急驱逐 BE Pod
T=10:12 LS 延迟恢复
├─ LS 延迟曾经达到 150ms ❌
├─ SLO 违反: 1 分钟
└─ SLO 降到 99.8% ❌
效果对比:
指标 不使用预测 使用预测性驱逐
──────────────────────────────────────────────
SLO 违反时间 1 分钟 0 秒
最大延迟 150ms 50ms
SLO 达成率 99.8% 99.99%
BE 作业完成率 70% 75%
结论:
└─ 预测性驱逐可以提前应对压力
└─ 避免 LS Pod 受到影响
└─ 提升整体 SLO
驱逐后的恢复机制
9.8 被驱逐 Pod 的重新调度
Kubernetes 的驱逐和重调度:
驱逐后的默认行为:
1. Pod 被驱逐
└─ Pod 状态变为 Failed
└─ Reason: Evicted
2. ReplicaSet/Deployment 检测到 Pod 失败
└─ 期望副本数: 3
└─ 实际副本数: 2(1 个被驱逐)
└─ 触发重新创建
3. Scheduler 重新调度 Pod
├─ 选择合适的节点
├─ 可能调度到原节点
├─ 也可能调度到其他节点
└─ 取决于节点资源和亲和性
4. Pod 在新节点启动
└─ kubelet 创建容器
└─ 恢复运行
Koordinator 的调度优化:
优化策略:
1. 驱逐标记(Eviction Taint)
├─ 驱逐 Pod 时,给节点打上临时 taint
├─ taint: koordinator.sh/eviction:NoSchedule
├─ 持续时间: 5 分钟
└─ 防止被驱逐的 Pod 立即调度回来
2. 资源预留(Resource Reservation)
├─ 驱逐后,为 LS Pod 预留资源
├─ 预留时间: 10 分钟
└─ 确保 LS 有足够资源
3. 调度优先级
├─ 被驱逐的 BE Pod 降低调度优先级
├─ 优先调度到其他节点
└─ 避免频繁在同一节点驱逐
4. 冷却期后自动恢复
├─ 冷却期结束后,移除 taint
├─ BE Pod 可以重新调度回来
└─ 恢复正常混部
时间线示例:
T=10:00 BE Pod 被驱逐
├─ 节点打上 taint
├─ taint 持续 5 分钟
└─ BE Pod 状态: Failed
T=10:01 BE Pod 重新调度
├─ Scheduler 看到 taint
├─ 选择其他节点
└─ BE Pod 在其他节点启动
T=10:05 Taint 过期,自动移除
├─ 节点恢复正常
├─ 可以接受新的 BE Pod
└─ 恢复混部
T=10:30 资源压力降低
├─ BE Pod 可以调度回原节点
└─ 充分利用资源
生产案例:避免驱逐-调度循环
场景:BE Pod 被驱逐后立即调度回来
不使用调度优化:
┌─────────────────────────────────────┐
│ T=10:00 BE Pod 被驱逐 │
│ └─ CPU 压力 95 │
│ │
│ T=10:01 BE Pod 重新调度 │
│ ├─ Scheduler 选择原节点 │
│ ├─ 因为原节点资源最多 │
│ └─ BE Pod 启动 │
│ │
│ T=10:02 CPU 压力再次升高 │
│ ├─ BE Pod 刚启动完成 │
│ ├─ 开始消耗 CPU │
│ └─ 压力: 93 │
│ │
│ T=10:03 BE Pod 再次被驱逐 │
│ └─ 循环往复 │
│ │
│ 问题: │
│ ├─ BE Pod 无法完成任务 │
│ ├─ 浪费调度和启动开销 │
│ └─ 节点压力持续波动 │
└─────────────────────────────────────┘
使用调度优化:
┌─────────────────────────────────────┐
│ T=10:00 BE Pod 被驱逐 │
│ ├─ CPU 压力 95 │
│ ├─ 节点打上 taint │
│ └─ taint 持续 5 分钟 │
│ │
│ T=10:01 BE Pod 重新调度 │
│ ├─ Scheduler 看到 taint │
│ ├─ 跳过原节点 │
│ ├─ 选择其他节点 │
│ └─ BE Pod 在其他节点启动 │
│ │
│ T=10:02 原节点压力降低 │
│ ├─ LS 充分使用 CPU │
│ ├─ 压力: 70 │
│ └─ 但 taint 仍存在 │
│ │
│ T=10:05 Taint 过期,移除 │
│ ├─ 节点恢复正常 │
│ ├─ 可以接受新 BE Pod │
│ └─ 但不是刚被驱逐的 Pod │
│ │
│ T=10:30 资源压力稳定 │
│ ├─ 允许 BE Pod 调度回来 │
│ └─ 恢复正常混部 │
│ │
│ 优势: │
│ ├─ 避免驱逐-调度循环 │
│ ├─ BE Pod 可以在其他节点完成任务 │
│ └─ 节点压力稳定 │
└─────────────────────────────────────┘
生产调优指南
9.9 驱逐配置参数
# Koordinator 驱逐配置示例
apiVersion: v1
kind: ConfigMap
metadata:
name: koordlet-config
namespace: koordinator-system
data:
eviction-config.yaml: |
# CPU 驱逐配置
cpuEvict:
enabled: true
evictThreshold: 90 # 压力 > 90 触发驱逐
cooldownPeriod: 60s # 驱逐后冷却 60 秒
maxEvictionPerBatch: 3 # 单次最多驱逐 3 个
maxEvictionPerMinute: 5 # 每分钟最多驱逐 5 个
# 内存驱逐配置
memoryEvict:
enabled: true
evictThreshold: 85 # 内存压力 > 85 触发
oomRiskThreshold: 90 # OOM 风险 > 90 触发
cooldownPeriod: 120s # 冷却 2 分钟
maxEvictionPerBatch: 2 # 单次最多 2 个
# 故障驱逐配置
faultyEvict:
enabled: true
oomCountThreshold: 3 # OOM 3 次触发
memoryLeakDuration: 3600s # 泄漏持续 1 小时
# 预测性驱逐配置
predictiveEvict:
enabled: true
predictionWindow: 600s # 预测未来 10 分钟
advanceEvictThreshold: 90 # 预测压力 > 90 触发
trendWindow: 600s # 趋势计算窗口 10 分钟
# 优雅驱逐配置
gracefulEviction:
enabled: true
preStopDelaySeconds: 10 # 驱逐前延迟 10 秒
batchInterval: 10s # 批次间隔 10 秒
verifyAfterEviction: true # 驱逐后验证
# 调度优化配置
schedulingOptimization:
evictionTaint: true # 启用驱逐 taint
taintDuration: 300s # taint 持续 5 分钟
resourceReservation: true # 启用资源预留
reservationDuration: 600s # 预留 10 分钟
9.10 调参指南
场景 1:延迟敏感业务(金融交易)
# 优化目标:绝对保障 LS,BE 可牺牲
cpuEvict:
evictThreshold: 85 # 降低阈值,更早驱逐
cooldownPeriod: 30s # 缩短冷却,快速响应
maxEvictionPerBatch: 5 # 增加批量,快速释放
predictiveEvict:
enabled: true
advanceEvictThreshold: 85 # 提前预测并驱逐
predictionWindow: 300s # 预测 5 分钟
gracefulEviction:
enabled: false # 禁用优雅驱逐,快速释放
# 或者缩短延迟
preStopDelaySeconds: 5
场景 2:吞吐优先业务(数据分析)
# 优化目标:尽量不驱逐 BE,允许 LS 适度波动
cpuEvict:
evictThreshold: 95 # 提高阈值,减少驱逐
cooldownPeriod: 120s # 延长冷却,减少频率
maxEvictionPerBatch: 1 # 减少批量,精确控制
predictiveEvict:
enabled: false # 禁用预测性驱逐
gracefulEviction:
enabled: true
preStopDelaySeconds: 30 # 延长优雅关闭时间
batchInterval: 30s # 延长批次间隔
场景 3:均衡场景(通用 Web 服务)
# 优化目标:LS 和 BE 的平衡
cpuEvict:
evictThreshold: 90 # 标准阈值
cooldownPeriod: 60s # 标准冷却
maxEvictionPerBatch: 3 # 标准批量
predictiveEvict:
enabled: true
advanceEvictThreshold: 90 # 标准预测
predictionWindow: 600s # 10 分钟预测
gracefulEviction:
enabled: true
preStopDelaySeconds: 10 # 标准延迟
9.11 监控指标
# 驱逐相关监控指标
# 驱逐次数
koordlet_eviction_total{reason="cpu_pressure"}
koordlet_eviction_total{reason="memory_pressure"}
koordlet_eviction_total{reason="oom_risk"}
koordlet_eviction_total{reason="faulty_pod"}
# 驱逐延迟(从触发到完成)
koordlet_eviction_latency_seconds
# 当前冷却状态
koordlet_eviction_cooldown_active
# 预测性驱逐准确率
koordlet_predictive_eviction_accuracy
# 被驱逐的 Pod 数量
koordlet_evicted_pods_total
# 驱逐后资源释放量
koordlet_eviction_freed_cpu_cores
koordlet_eviction_freed_memory_bytes
# 驱逐效果验证
koordlet_eviction_pressure_after_eviction
9.12 常见问题排查
问题 1:LS Pod 仍然受影响
诊断步骤:
1. 检查驱逐阈值
$ kubectl get cm koordlet-config -o yaml | grep evictThreshold
如果阈值 > 90:
└─ 降低到 85,更早触发驱逐
2. 检查驱逐是否真正执行
$ kubectl get events | grep Evicted
如果没有驱逐事件:
└─ 检查 QOSManager 日志,确认触发逻辑
3. 检查冷却期是否过长
$ kubectl logs koordlet-xxx | grep "cooldown"
如果频繁处于冷却期:
└─ 缩短 cooldownPeriod 到 30s
4. 检查驱逐目标选择
$ kubectl logs koordlet-xxx | grep "eviction target"
如果选择的 Pod 资源占用少:
└─ 调整选择算法权重
解决方案:
├─ 降低 evictThreshold 到 85
├─ 启用预测性驱逐
├─ 缩短 cooldownPeriod
└─ 增加 maxEvictionPerBatch
问题 2:BE Pod 频繁被驱逐
诊断:
1. 检查驱逐频率
$ promql: rate(koordlet_eviction_total[5m])
如果 > 0.1/s(每 10 秒一次):
└─ 过于频繁
2. 检查节点资源分配
$ kubectl describe node <node-name>
如果 LS request 过高:
└─ LS 占用过多资源,BE 空间不足
解决方案:
├─ 提高 evictThreshold 到 95
├─ 延长 cooldownPeriod 到 120s
├─ 减少 BE Pod 数量或调整到其他节点
└─ 增加节点资源
问题 3:驱逐后压力仍高
诊断:
1. 检查驱逐释放的资源
$ promql: koordlet_eviction_freed_cpu_cores
如果释放量 < 目标量:
└─ maxEvictionPerBatch 过小
2. 检查驱逐后的压力
$ promql: koordlet_eviction_pressure_after_eviction
如果仍 > 85:
└─ 需要驱逐更多 Pod
解决方案:
├─ 增加 maxEvictionPerBatch 到 5
├─ 减少 cooldownPeriod,允许连续驱逐
└─ 启用预测性驱逐,提前应对
总结 - 章节要点汇总
9.13 关键概念速查
| 驱逐类型 | 触发条件 | 响应时间 | 影响范围 |
|---|---|---|---|
| CPU驱逐 | 压力 > 90 | < 1s | BE Pod |
| 内存驱逐 | 压力 > 85 或 OOM 风险 | < 1s | BE Pod |
| 故障驱逐 | OOM 3次 或 泄漏 1h | 实时 | 故障 Pod |
| 预测性驱逐 | 预测压力 > 90 | 提前 5-10min | BE Pod |
9.14 驱逐决策流程
驱逐决策流程总结:
1. 触发检测(1s)
└─ QOSManager 检测资源压力
2. 目标选择(5ms)
└─ 按 QoS/Priority/资源占用排序
3. 驱逐执行(30s)
└─ 调用 Eviction API,优雅关闭
4. 效果验证(1s)
└─ 监控压力变化
5. 冷却期(60s)
└─ 防止频繁驱逐
总耗时:< 2分钟(从检测到完成)
9.15 最佳实践
- 根据业务特点调整驱逐阈值(延迟敏感 vs 吞吐优先)
- 启用预测性驱逐,提前应对资源压力
- 配置合理的速率控制,避免频繁驱逐
- 使用优雅驱逐保护数据完整性
- 监控驱逐效果,持续优化参数