揭秘云原生混布资源调度器Koordinator (九)驱逐机制

8 阅读20分钟

核心使命与设计理念

9.1 What - 驱逐机制是什么?

驱逐(Eviction)是 Koordinator 在资源压力严重时,主动终止低优先级 Pod 以释放资源,保障高优先级 Pod 的 SLO 的最后手段。

核心目标

  1. 紧急资源释放:在抑制无效时,快速释放资源
  2. 优先级保障:优先驱逐 BE Pod,保护 LS/LSR/LSE Pod
  3. 最小影响:选择影响最小的 Pod 进行驱逐
  4. 可控性:支持驱逐策略配置和驱逐速率控制
  5. 可恢复:被驱逐的 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 需求: 7085% 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< 1sBE Pod
内存驱逐压力 > 85 或 OOM 风险< 1sBE Pod
故障驱逐OOM 3次 或 泄漏 1h实时故障 Pod
预测性驱逐预测压力 > 90提前 5-10minBE Pod

9.14 驱逐决策流程

驱逐决策流程总结:

1. 触发检测(1s)
   └─ QOSManager 检测资源压力

2. 目标选择(5ms)
   └─ 按 QoS/Priority/资源占用排序

3. 驱逐执行(30s)
   └─ 调用 Eviction API,优雅关闭

4. 效果验证(1s)
   └─ 监控压力变化

5. 冷却期(60s)
   └─ 防止频繁驱逐

总耗时:< 2分钟(从检测到完成)

9.15 最佳实践

  • 根据业务特点调整驱逐阈值(延迟敏感 vs 吞吐优先)
  • 启用预测性驱逐,提前应对资源压力
  • 配置合理的速率控制,避免频繁驱逐
  • 使用优雅驱逐保护数据完整性
  • 监控驱逐效果,持续优化参数