【集群】云原生批调度实战:Volcano 数据收集方法深度解析与Prometheus Histogram误差问题

【集群】云原生批调度实战:Volcano 数据收集方法深度解析与Prometheus Histogram误差问题

Fre5h1nd Lv6

本系列《云原生批调度实战:Volcano 监控与性能测试》计划分为以下几篇,点击查看其它内容。

  1. 云原生批调度实战:调度器测试与监控工具 kube-scheduling-perf
  2. 云原生批调度实战:调度器测试与监控工具 kube-scheduling-perf 实操注意事项说明
  3. 云原生批调度实战:调度器测试监控结果
  4. 云原生批调度实战:本地环境测试结果与视频对比分析
  5. 监控与测试环境解析:测试流程拆解篇
  6. 监控与测试环境解析:指标采集与可视化篇
  7. 监控与测试环境解析:自定义镜像性能回归测试
  8. 监控与测试环境解析:数据收集方法深度解析与Prometheus Histogram误差问题
  9. 云原生批调度实战:Volcano调度器enqueue功能禁用与性能测试
  10. 云原生批调度实战:Volcano Pod创建数量不足问题排查与Webhook超时修复
  11. 云原生批调度实战:Volcano版本修改与性能测试优化
  12. 云原生批调度实战:Volcano Webhook禁用与性能瓶颈分析

在前期的文章中,我提到了调度器性能对比分析中一个重要论断:传统方法使用Prometheus会造成很大误差,而audit-exporter方法能够准确记录性能。当时我误以为这种差异源于数据处理阶段的Prometheus histogram统计方法,但深入分析指标采集链路后发现,两种方法的核心差异实际上在数据收集阶段,而非数据处理阶段

本文旨在澄清这一技术细节,并深入探讨Prometheus histogram的误差机制与替代方案。


🎯 问题澄清:数据收集 vs 数据处理

1. 传统Prometheus方法 vs Audit-Exporter方法

传统Prometheus方法的数据收集路径

传统的Kubernetes调度器性能监控依赖调度器自身暴露的Prometheus指标:

graph LR
    A[调度器内部逻辑] --> B[调度器暴露metrics端点]
    B --> C[Prometheus定期抓取]
    C --> D[存储到TSDB]
    D --> E[Grafana查询展示]

核心特点

  • 数据源:调度器进程内部的instrumentation代码
  • 时间精度:受限于调度器代码的埋点位置和精度
  • 数据完整性:可能遗漏调度过程中的某些阶段
  • 系统开销:对调度器性能有直接影响

Audit-Exporter方法的数据收集路径

而audit-exporter方法从kube-apiserver的审计日志中提取性能数据:

graph LR
    A[调度器向APIServer发起请求] --> B[APIServer记录审计日志]
    B --> C[audit-exporter解析日志]
    C --> D[转换为Prometheus指标]
    D --> E[Prometheus抓取存储]
    E --> F[Grafana查询展示]

核心特点

  • 数据源:kube-apiserver的完整请求审计记录
  • 时间精度:基于APIServer的高精度时间戳
  • 数据完整性:涵盖从请求到响应的完整生命周期
  • 系统开销:不影响调度器性能,仅增加APIServer审计开销

2. 本质差异分析

✅ 数据收集阶段的差异(核心)

传统方法的局限性

  1. 可控能力差:调度器内部的metric埋点可能无法覆盖所有关键路径,受制于代码中指标记录调用的位置和频率
  2. 潜在性能影响:在高负载下,调度器本身指标统计可能会影响调度性能

Audit-exporter方法的优势

  1. 可控能力强:可以自定义捕获数据,例如本项目能监控每个API请求的完整生命周期(RequestReceived → ResponseComplete),且因为基于外部 APIServer,所以所有组件(调度器、控制器等)的请求都被统一记录
  2. 无性能影响:基于外部 APIServer,低侵入性,不影响被监控组件的性能

具体来说,通过audit日志能够精确记录关键事件:

1
2
3
4
5
6
7
{
"verb": "create",
"objectRef": {"resource": "pods"},
"stage": "ResponseComplete",
"stageTimestamp": "2025-01-27T10:30:45.123456Z",
"requestReceivedTimestamp": "2025-01-27T10:30:45.098123Z"
}

通过解析这些事件,可以计算出精确的 Pod创建延迟调度延迟 等数据。

❌ 数据处理阶段的相似性

重要澄清:两种方法在数据处理阶段实际上是相同的!(之前我的博客中,知道数据处理方法中 histogram 会存在误差,但现在才发现这种误差两种方法都会有)

无论是传统方法还是audit-exporter方法,最终都会:

  1. 将原始数据转换为Prometheus histogram指标
  2. 使用相同的bucket配置和histogram_quantile()函数
  3. 面临相同的线性插值误差问题

因此,Prometheus histogram的误差问题在两种方法中都存在,差异并非来自数据处理阶段。


📊 Prometheus Histogram误差深度解析

1. Histogram工作原理

Prometheus histogram通过预定义的bucket边界来统计数据分布:

1
2
3
4
5
// 示例:调度延迟histogram配置
latencyHistogram := prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "scheduler_latency_seconds",
Buckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 5, 10}, // bucket边界
})

当收集到延迟数据时,每个观测值会被分配到相应的bucket中:

1
2
3
观测值: 0.3秒

累计到bucket: le="0.5", le="1", le="5", le="10", le="+Inf"

其中 le 指 less,即 bucket 记录“小于 x 的数字有多少个”。

2. 线性插值误差机制

均匀分布假设的问题

Prometheus使用histogram_quantile()函数计算百分位数时,假设每个bucket内的数据均匀分布

1
2
3
4
// Prometheus源码中的线性插值逻辑(简化)
func linearInterpolation(bucketStart, bucketEnd float64, rank, count float64) float64 {
return bucketStart + (bucketEnd-bucketStart)*rank/count
}

例子:假设我们有bucket配置[0.1, 0.5, 1.0],观测到以下数据:

Bucket范围累计计数实际分布
(0, 0.1]10均匀分布
(0.1, 0.5]90集中在0.12秒附近
(0.5, 1.0]100均匀分布

计算P90(第90个样本):

  • Prometheus假设:P90在(0.1, 0.5]区间内均匀分布,计算得P90 ≈ 0.5秒
  • 实际情况:如果80个样本都集中在0.12秒附近,真实P90 ≈ 0.12秒
  • 结论:存在非常大的误差(尤其当桶边界非线性时,目前看大多数情况下桶边界都是指数级增长,具体原因可能是大部分数据都非均匀分布,数据范围太大时线性分布粒度过粗)

3. Bucket配置的关键影响

默认Bucket的问题

不同Prometheus客户端库的默认bucket配置:

1
2
3
4
5
6
7
8
9
10
// Go客户端默认bucket
prometheus.DefBuckets = []float64{
.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10,
}

// Python客户端默认bucket
DEFAULT_BUCKETS = (
.005, .01, .025, .05, .075, .1, .25, .5, .75,
1.0, 2.5, 5.0, 7.5, 10.0,
)

这些默认配置的问题:

  1. 覆盖范围过广:从5ms到10s,对特定应用场景分辨率不足
  2. 分布不均:低延迟区间密集,高延迟区间稀疏

可能的备选Bucket策略

1. 基于SLO设计

1
2
3
4
5
6
// 假设SLO为P95 < 500ms, P99 < 1s
sloBuckets := []float64{
0.050, 0.100, 0.200, 0.350, // P95周围密集采样
0.500, 0.650, 0.800, 0.950, // P99周围密集采样
1.000, 2.000, 5.000, // 异常情况覆盖
}

2. 对数分布

1
2
3
4
5
// 指数增长,适合跨多个数量级的指标
logBuckets := []float64{
0.001, 0.002, 0.005, 0.01, 0.02, 0.05,
0.1, 0.2, 0.5, 1, 2, 5, 10,
}

3. 动态调整:某些高级方案支持根据实际数据分布动态调整bucket(VictoriaMetrics似乎有这个功能)


🔧 Histogram替代方案与优化

1. Summary指标

Prometheus提供了Summary类型作为histogram的替代:

1
2
3
4
5
6
7
8
summary := prometheus.NewSummary(prometheus.SummaryOpts{
Name: "latency_seconds",
Objectives: map[float64]float64{
0.5: 0.05, // P50误差±5%
0.9: 0.01, // P90误差±1%
0.99: 0.001, // P99误差±0.1%
},
})

优势

  • 客户端精确计算百分位数,无插值误差
  • 内存占用相对固定
  • 查询性能好

劣势

  • 无法聚合:不能跨实例计算全局百分位数
  • 预定义百分位:无法在查询时动态计算其他百分位数
  • 客户端开销:需要维护滑动窗口和排序

🔚 总结

通过本文的深入分析,我们可以得出以下关键结论:

核心澄清

  1. 数据收集vs数据处理:audit-exporter方法与传统Prometheus方法的主要差异在于数据收集阶段(数据源和精度),而非数据处理阶段(histogram计算)。

  2. Histogram误差本质:Prometheus histogram的误差源于bucket内均匀分布假设与实际数据分布的差异,这在两种方法中都存在。默认bucket配置可能不匹配实际应用的延迟分布,需要根据SLO定制。



  • 希望这篇博客对你有帮助!如果你有任何问题或需要进一步的帮助,请随时提问。
  • 如果你喜欢这篇文章,欢迎动动小手给我一个follow或star。

📝参考文献

[1] 大规模集群仿真模拟与调度器压测方法

[2] Prometheus中文文档 - Histogram and Summary%E3%80%82)

[3] 蓝胖子编程梦 - prometheus描点原理

[4] 蓝胖子编程梦 - prometheus Histogram 统计原理

[5] Github - kube-scheduling-perf

[6] Github - kube-apiserver-audit-exporter

  • 标题: 【集群】云原生批调度实战:Volcano 数据收集方法深度解析与Prometheus Histogram误差问题
  • 作者: Fre5h1nd
  • 创建于 : 2025-08-10 00:04:35
  • 更新于 : 2025-08-21 19:57:43
  • 链接: https://freshwlnd.github.io/2025/08/10/k8s/k8s-scheduler-performance-volcano-analysis/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论