核心使命与设计理念
12.1 CPU Throttle 是什么?
CPU Throttle 是 Linux CFS 调度器在 Pod 超过 CPU quota 限制时采取的限流措施,通过拒绝 CPU 时间片的分配,导致进程运行被暂停(Throttle)。
核心概念:
┌─────────────────────────────────────────────┐
│ CPU Throttle 的工作原理 │
├─────────────────────────────────────────────┤
│ │
│ CPU quota 的含义: │
│ ├─ 每 100ms(cfs_period)内,可使用的 CPU │
│ ├─ 例:quota=200000 μs = 2 CPU × 100ms │
│ ├─ 即:每 100ms 最多运行 200ms 的代码 │
│ └─ 超过就会被 throttle │
│ │
│ Throttle 的含义: │
│ ├─ 进程已经使用了本周期的配额 │
│ ├─ 即使 CPU 空闲,也不被调度 │
│ ├─ 进程等待下一个周期开始 │
│ └─ 这个等待的时间称为 throttle 时间 │
│ │
│ 工作时间线: │
│ 时刻 进程状态 CPU 配额状态 │
│ ────────────────────────────────────── │
│ 0ms Running 配额: 200/200ms │
│ 50ms Running 配额: 150/200ms │
│ 100ms Running 配额: 100/200ms │
│ 180ms Running 配额: 20/200ms │
│ 190ms Running 配额: 10/200ms │
│ 199ms Running 配额: 1/200ms │
│ 200ms Throttled ❌ 配额: 0/200ms │
│ ... Throttled 等待中... │
│ 300ms Running ✅ 配额: 200/200ms (新周期)│
│ │
└─────────────────────────────────────────────┘
12.2 Throttle 会导致问题?
问题 1:Throttle 导致延迟增加
场景:在线 API 服务的 CPU Throttle 问题
Pod 配置:
├─ request: 2 CPU
├─ limit: 2 CPU
├─ cfs_quota_us: 200000 (2 CPU × 100ms)
时间线:
T=10:00:00 正常运行
├─ 请求处理耗时: 20ms
├─ API 响应: < 50ms
T=10:00:10 流量激增(同一时段)
├─ 多个请求同时到达
├─ 每个请求处理耗时: 20ms
├─ 并发: 15 个请求
├─ 总 CPU 需求: 300ms(在 100ms 周期内)
└─ 但配额只有 200ms
调度流程(100ms 周期):
T=0ms 15 个请求开始运行
├─ 请求 1-10: 获得 CPU 时间
├─ 每个 20ms
└─ 总: 200ms,用完配额
T=200ms 请求 11-15 仍在队列中
├─ 无法获得 CPU 时间
├─ 都被 Throttle 了
└─ 等待下一个周期(100ms)
T=300ms 新周期开始
├─ 请求 11-15 继续执行
├─ 但由于等待,延迟增加了
└─ 响应延迟: 50ms → 150ms ❌
影响:
├─ 用户感知延迟增加 3 倍
├─ API P99 延迟大幅增加
├─ SLO 违反
└─ 用户投诉
问题 2:Throttle 的冷启动问题
场景:Pod 冷启动后立即被 Throttle
Pod 创建流程:
├─ T=0: kubelet 创建容器
├─ T=100ms: 容器启动,开始初始化
├─ T=200ms: 应用启动,开始处理请求
初始化负载高:
├─ 加载配置文件
├─ 初始化数据库连接
├─ 预热缓存
├─ CPU 占用: 可能达到 150% (大于平时的 100%)
时间线:
T=0-100ms: 初始化运行,CPU 使用率 150%
├─ 周期 1 (0-100ms):
│ ├─ 配额: 200ms
│ ├─ 需求: 150ms
│ └─ 配额充足,正常运行
│
├─ 周期 2 (100-200ms):
│ ├─ 配额: 200ms
│ ├─ 需求: 150ms
│ └─ 配额充足
T=100-200ms: 初始化继续,CPU 150%
├─ 周期 2 (100-200ms):
│ ├─ 配额: 200ms
│ ├─ 需求: 150ms (但前 50ms 已耗尽)
│ └─ 实际可用: 150ms
│
└─ 进程每次都被 Throttle
问题:
├─ Pod 创建后,冷启动阶段性能受限
├─ 应用启动时间延长
├─ 服务不可用时间增加
└─ 启动延迟可能达到秒级
不使用 CPUBurst 的启动时间:
├─ 初始化阶段: 200ms (受 throttle 限制)
├─ 应用启动: 500ms
├─ 首次请求: 1000ms (等待初始化完成)
└─ 总: > 1.5 秒不可用
使用 CPUBurst 的启动时间:
├─ 初始化阶段: 100ms (允许 burst,使用更多 CPU)
├─ 应用启动: 300ms (更快)
├─ 首次请求: 500ms (更早可用)
└─ 总: < 1 秒(快 33%)
问题 3:Throttle 导致的"磁吸效应"
场景:Pod 频繁达到 quota 限制
问题现象:
Pod CPU 使用率曲线:
├─ 时间 0-50ms: 快速上升到 100% CPU
├─ 时间 50-100ms: 保持 100%
├─ 时间 100ms: 瞬间下降到 0%(Throttle)
├─ 时间 100-150ms: 继续下降(等待)
├─ 时间 150ms: 恢复 100%(新周期)
│
├─ 结果: 锯齿波形
这种模式导致的问题:
1. 不均衡的负载:
├─ 周期初期: CPU 满载,争夺激烈
├─ 周期末期: CPU 空闲,无人运行
└─ 缓存热度周期性变化
2. 吞吐量不稳定:
├─ 周期初期吞吐量高
├─ 周期末期吞吐量低
└─ 平均吞吐量 < 理论最大值
3. 延迟波动大:
├─ 请求在周期初期: 快速处理
├─ 请求在周期末期: 被 Throttle 延迟
└─ P99 延迟很高
原因分析:
├─ CPU quota 的"硬边界"特性
├─ CFS 调度器的实现方式
└─ Pod 的实际需求与配额的不匹配
12.3 检测和优化 Throttle
Throttle 的检测和度量:
Linux CGroup 提供的 Throttle 统计:
cat /sys/fs/cgroup/cpu/kubepods/pod-xxx/cpu.stat
cpu.stat 的内容:
├─ nr_periods: 调度周期总数(通常 = 运行时间 / 100ms)
├─ nr_throttled: 被 throttle 的周期数
├─ throttled_time: 被 throttle 的总时间(纳秒)
│
└─ 计算 Throttle 相关指标:
├─ throttle_ratio = nr_throttled / nr_periods × 100%
│ └─ 表示被 throttle 的周期占比
│
├─ throttle_duration_ratio = throttled_time / total_time × 100%
│ └─ 表示被 throttle 的时间占比
│
├─ 平均 throttle 时长 = throttled_time / nr_throttled
│ └─ 表示平均每次 throttle 持续多长
│
└─ throttle_burst_impact = throttled_time / (request_time - throttled_time)
└─ 表示 throttle 对吞吐量的影响
示例数据:
Pod A(配额充足,无 throttle):
├─ nr_periods: 1000
├─ nr_throttled: 0
├─ throttled_time: 0
│
└─ 指标:
├─ throttle_ratio: 0%
└─ throttle_duration_ratio: 0%
Pod B(配额不足,频繁 throttle):
├─ nr_periods: 1000
├─ nr_throttled: 400
├─ throttled_time: 8000000000 ns = 8s
│
└─ 指标:
├─ throttle_ratio: 40%(400 个周期中 40% 被 throttle)
├─ throttle_duration_ratio: 8% (总 100s 中有 8s 被 throttle)
├─ avg_throttle_time: 20ms (8s / 400 周期)
└─ 吞吐量影响: 8% / (100-8) ≈ 8.7%
Throttle 的优化策略:
优化方向 1:提高 CPU quota
问题:Pod 的 limit 设置过低
解决:
├─ 调查 Pod 的实际需求
├─ 增加 CPU limit
├─ 例:2 CPU → 3 CPU(假设需要)
优化方向 2:允许 CPU burst
问题:短期流量激增导致 throttle
解决:
├─ 启用 CPUBurst
├─ 允许 Pod 在有空闲 CPU 时超过 limit
├─ 例:limit=2 CPU,burst_ratio=1.5,实际可用 3 CPU
优化方向 3:优化应用,减少 CPU 需求
问题:应用逻辑效率低,CPU 占用高
解决:
├─ 优化应用代码
├─ 减少不必要的计算
├─ 改进算法复杂度
└─ 例:从 O(n²) 改为 O(n log n)
优化方向 4:使用更激进的 Suppress
问题:BE Pod 与 LS Pod 竞争
解决:
├─ 当检测到 LS Pod throttle 时
├─ 更激进地抑制 BE Pod
├─ 为 LS 预留充足空间
优化方向 5:使用 Throttle-aware 调度
问题:调度器不考虑 throttle 风险
解决:
├─ 预测 Pod 的 throttle 风险
├─ 在调度时考虑
├─ 选择配额充足的节点
Throttle 优化的生产案例
12.4 生产案例 1:API 网关的 Throttle 优化
场景:电商平台 API 网关,流量波动大
初始配置:
├─ Pod CPU request: 2
├─ Pod CPU limit: 2
├─ 副本数: 10
问题现象:
监控发现:
├─ API P99 延迟: 300ms(目标 < 100ms)
├─ throttle_ratio: 25%(高)
├─ 偶发请求超时(> 1s)
分析:
1. 检查流量模式
├─ 平均 QPS: 1000
├─ 峰值 QPS: 5000(5 倍)
├─ 峰值时的 CPU 需求:每个 Pod 需要 4 CPU(burst)
└─ 但 limit 仅 2 CPU
2. 检查资源利用率
├─ 低谷时: 0.5 CPU(利用率 25%)
├─ 平均: 1.5 CPU(利用率 75%)
├─ 峰值: 3+ CPU(受 limit 限制,throttle 严重)
└─ 问题: limit 设置过保守
优化方案 1:提高 limit
配置变更:
├─ Pod CPU request: 2
├─ Pod CPU limit: 4(从 2 改为 4)
├─ 副本数: 10(不变)
效果:
├─ API P99 延迟: 300ms → 80ms ✅
├─ throttle_ratio: 25% → 0% ✅
├─ 节点总 CPU 需求: 20 → 40(翻倍,可能超过节点容量)
问题:
├─ 节点可能无法容纳 10 个 Pod(总 limit 40 CPU 但节点仅 64 CPU)
├─ 其他服务受影响
└─ 不是最优方案
优化方案 2:启用 CPUBurst
配置变更:
├─ Pod CPU request: 2
├─ Pod CPU limit: 2(保持)
├─ CPUBurst 启用: true
├─ burst_ratio: 1.5
├─ 副本数: 10
CPUBurst 的工作方式:
├─ 正常时: Pod 使用 1-2 CPU(不超过 limit)
├─ 峰值时: 检测到节点有空闲 CPU
│ └─ 允许 Pod 使用 3 CPU (2 × 1.5)
│ └─ 持续 10 秒(burst 时间)
效果:
├─ 低谷时: 0.5 CPU × 10 = 5 CPU 总(节点利用 < 10%)
├─ 平均时: 1.5 CPU × 10 = 15 CPU 总(节点利用 < 25%)
├─ 峰值时: 2-3 CPU × 10 = 20-30 CPU 总(峰值可突破 limit)
└─ API P99 延迟: 300ms → 100ms ✅(接近目标)
优化方案 3:智能副本调整
问题:固定 10 个副本不够灵活
解决:
├─ 启用 HPA (Horizontal Pod Autoscaler)
├─ 目标 CPU 利用率: 70%
├─ Min Pod: 10, Max Pod: 20
效果:
├─ 低谷时 (0.5 CPU 需求):
│ ├─ HPA 缩容: 10 个 Pod → 5 个 Pod
│ ├─ 节点 CPU 利用: 5 × 1.5 / 64 ≈ 12%(降低)
│ └─ 成本降低 50%
│
├─ 平均时 (1.5 CPU 需求):
│ ├─ 10 个 Pod (1.5 × 10 = 15 CPU)
│ └─ 利用率: 15 / 64 ≈ 23%
│
└─ 峰值时 (4 CPU 需求):
├─ HPA 扩容: 10 个 Pod → 20 个 Pod
├─ 总 CPU: 2 × 20 = 40 CPU(假设 burst 后平均 2 CPU)
└─ 利用率: 40 / 64 ≈ 62%
综合效果:
指标 初始配置 优化后
─────────────────────────────────────
P99 延迟 300ms 90ms
throttle_ratio 25% < 1%
CPU 利用率 (平均) 30% 23%
Pod 数 (平均) 10 8(HPA 缩容)
成本 100% 75%(因副本减少)
最终方案:
└─ 启用 CPUBurst + HPA 结合
└─ 既保障性能(P99 延迟达标)
└─ 又降低成本(副本减少)
12.5 生产案例 2:后台任务的 Throttle 优化
场景:离线数据处理任务,可容忍延迟
初始配置:
├─ 任务类型:数据导出、ETL
├─ CPU limit: 4
├─ 运行周期: 8 小时
├─ 目标:在 8 小时内完成
问题现象:
监控发现:
├─ throttle_ratio: 60%(非常高!)
├─ 实际任务时间: 8 小时 + 2 小时(= 10 小时,超期)
├─ 原因:BE Pod 被频繁抑制,实际 CPU 不足
分析:
时间线:
├─ T=0-2h: 与 LS Pod 竞争,被大幅抑制,throttle 60%
├─ T=2-8h: LS 负载下降,抑制减轻,throttle 30%
├─ T=8h: 应该完成,但只完成了 80%
└─ T=8-10h: 继续运行完成剩余 20%
原因:
└─ BE Pod 的 CPU 配额被动态调整
└─ 峰值时被限制到 1.6 CPU(60% throttle)
└─ 无法在规定时间内完成
优化方案 1:增加副本并行处理
配置变更:
├─ 从 1 个 Pod(4 CPU)改为 4 个 Pod(每个 2 CPU)
├─ 并行处理不同的数据块
├─ 总 CPU: 8(从 4 增加)
效果:
├─ 虽然总 CPU 增加
├─ 但分散的 Pod 被抑制的程度降低
├─ throttle_ratio: 60% → 40%
├─ 任务时间: 10h → 7h (提速 43%)
优化方案 2:调整任务优先级
使用 Koordinator 的 Priority:
├─ 给数据导出任务设置高 Priority (-10)
├─ QOSManager 看到优先级,减轻抑制
├─ 允许高优先任务获得更多 CPU
效果:
├─ throttle_ratio: 60% → 35%
├─ 任务时间: 10h → 6.5h
├─ 同时不影响 LS Pod(LS 有更高的 QoS)
优化方案 3:错开运行时间(最经济)
配置变更:
├─ 原方案: 全天候运行(与 LS 竞争)
├─ 新方案: 仅在低峰期运行(晚上 10 点到早上 6 点)
├─ 运行时间: 8 小时(集中在低峰)
├─ 不竞争,无 throttle
效果:
├─ throttle_ratio: 60% → 0% ✅
├─ 任务时间: 8h(无 throttle,正常)
├─ CPU 利用率: 完全利用,无浪费
├─ 成本: 最低(仅需 1 个 Pod,无并行开销)
综合对比:
方案 Pod 数 CPU 完成时间 throttle 成本
─────────────────────────────────────────────────────
初始方案 1 4 10h 60% 4×10h=40
增加副本 4 8 7h 40% 8×7h=56
调整优先级 1 4 6.5h 35% 4×6.5h=26
错开时间 1 4 8h 0% 4×8h=32
最优方案:
└─ 错开时间(成本最低,性能最好)
└─ 或调整优先级(平衡性能和成本)
生产调优指南
12.6 Throttle 监控和告警
# Throttle 监控配置
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cpu-throttle-alerts
spec:
groups:
- name: cpu-throttle
interval: 30s
rules:
# 警报 1: 高 Throttle 比率
- alert: HighCPUThrottleRatio
expr: |
(container_cpu_cfs_throttled_seconds_total /
container_cpu_cfs_periods_total * 100) > 20
for: 5m
annotations:
summary: "Pod {{ $labels.pod }} 有高 throttle 比率"
description: "Throttle 比率 {{ $value }}%,高于 20% 阈值"
# 警报 2: Throttle 时间过长
- alert: HighCPUThrottleTime
expr: |
rate(container_cpu_cfs_throttled_seconds_total[5m]) > 0.1
for: 5m
annotations:
summary: "Pod {{ $labels.pod }} throttle 时间过长"
description: "每秒 throttle {{ $value }}s"
# 警报 3: P99 延迟与 Throttle 相关
- alert: HighLatencyWithThrottle
expr: |
(histogram_quantile(0.99, api_latency) > 100) and
(container_cpu_cfs_throttled_seconds_total > 10)
annotations:
summary: "高延迟且有 throttle,需要调整 CPU 配置"
12.7 调参指南
对于延迟敏感的服务(LS Pod):
# 方案:避免 Throttle,使用充足的 CPU limit
spec:
containers:
- name: app
resources:
requests:
cpu: "2"
limits:
cpu: "3" # 比 request 高 50%,留缓冲
# 启用 CPUBurst
env:
- name: KOORDINATOR_CPU_BURST_ENABLED
value: "true"
- name: KOORDINATOR_CPU_BURST_RATIO
value: "1.5" # 可以 burst 到 4.5 CPU
对于吞吐优先的任务(BE Pod):
# 方案:接受 throttle,但需要时间足够完成任务
spec:
containers:
- name: batch-job
resources:
requests:
cpu: "2"
limits:
cpu: "4" # BE 可以用完 limit
# 增加运行时间预算
env:
- name: JOB_TIMEOUT
value: "10h" # 从 8h 增加到 10h,考虑 throttle
12.8 常见问题排查
问题 1:Throttle 比率突然升高
诊断:
1. 检查 CPU limit 是否变化
$ kubectl get pod -o jsonpath='{.items[].spec.containers[].resources.limits.cpu}'
2. 检查节点 CPU 压力
$ kubectl top nodes
$ kubectl top pods
3. 检查 LS Pod 的流量变化
$ promql: sum(rate(request_total[5m])) by (pod)
4. 检查是否启用了新的 QOSManager 策略
$ kubectl logs koordlet-xxx | grep -i suppress
原因可能:
├─ 节点 CPU 整体压力增加
├─ LS Pod 流量激增
├─ 新的限流策略生效
└─ Pod 的实际需求增加
解决:
├─ 如果是流量增加,可考虑增加副本
├─ 如果是 CPU 配置不合理,调整 limit
├─ 如果是限流太激进,调整 suppress 参数
└─ 监控 throttle 的趋势,而不是绝对值
问题 2:应用性能下降但监控看不出原因
诊断:
1. 首先检查 throttle 统计
$ cat /sys/fs/cgroup/cpu/kubepods/pod-xxx/cpu.stat
看是否有高 throttle 比率
2. 检查 CPU 使用率是否接近 limit
$ promql: container_cpu_usage_seconds_total
如果 CPU 使用率 ≈ limit,说明 throttle 是原因
3. 对比应用的响应时间分布
$ promql: histogram_quantile(0.99, latency)
如果 P99 大幅高于 P50,可能是 throttle 导致
解决:
├─ 检查 limit 设置是否合理
├─ 查看是否被 BE Pod 抑制
├─ 考虑启用 CPUBurst
└─ 调整应用配置,减少 CPU 需求
12.9 监控指标
# CPU Throttle 关键指标
# Throttle 比率(最重要)
koordlet_container_cpu_throttle_ratio{pod=""}
= increase(cpu.stat[throttled_time]) / increase(cpu.stat[total_time])
# Throttle 次数
koordlet_container_cpu_throttle_count_total
# Throttle 导致的延迟增加
koordlet_container_cpu_throttle_latency_impact_ms
# 与 throttle 相关的性能下降
koordlet_qos_throttle_related_slo_violations_total
总结 - 章节要点汇总
12.10 关键概念速查
| 概念 | 含义 | 影响 |
|---|---|---|
| CPU Throttle | 超过配额被限制运行 | 延迟增加 |
| Throttle Ratio | 被 throttle 的周期占比 | > 20% 需要优化 |
| CPUBurst | 在有空闲时超过 limit | 改善冷启动 |
| Suppress | 主动降低 BE 的 limit | 保护 LS Pod |
12.11 Throttle 优化决策树
场景 1: LS Pod (延迟敏感)
├─ 监测到 throttle > 10%?
│ ├─ 是 → 增加 CPU limit
│ └─ 否 → 启用 CPUBurst 作为保险
│
└─ P99 延迟 > SLO?
├─ 是 → 立即增加 CPU
└─ 否 → 继续监控
场景 2: BE Pod (吞吐优先)
├─ 任务能按时完成?
│ ├─ 是 → 保持现状,充分利用资源
│ └─ 否 → 增加并行副本或错开运行时间
│
└─ 成本是否可接受?
├─ 否 → 减少副本,延长运行时间
└─ 是 → 增加副本加速任务