Kubernetes v1.37 将原生直方图(Native Histograms)提升到 Beta,并默认启用。这个变化直接影响 API Server 延迟、调度耗时、容器运行时操作等指标:Prometheus 可以用更少的时序覆盖更宽的数值范围,同时更准确地计算 P95、P99 等分位数。
升级 Kubernetes 并不等于监控系统已经完成迁移。Kubernetes 负责暴露经典直方图和原生直方图,Prometheus 是否采集、Grafana 与告警规则使用哪一种格式,仍需要运维团队明确配置。
经典直方图的三个结构性问题
经典 Prometheus 直方图要求指标作者预先定义累积桶边界,例如:
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10
这种模型容易遇到三个问题。
固定桶无法适应分布变化。 如果请求耗时从毫秒下降到微秒级,或者长尾延迟超过最大桶,已有边界就无法提供足够的分辨率。开发者必须在真正观察到分布之前猜测合适的桶。
每个桶都会增加时序。 经典格式把每个边界暴露为一条带有 le 标签的 _bucket 时序。指标原本已有的 verb、resource、code 等标签会与所有桶组合,放大 Prometheus 内存、抓取和 TSDB 存储成本。
分位数依赖桶内插值。 histogram_quantile() 只能根据固定边界估算桶内分布。桶跨度越大,估算误差越明显。
原生直方图改用动态指数桶,并把正值跨度、负值跨度、零值阈值和缩放参数编码在一条结构化时序中。Kubernetes 的默认配置包括:
BucketFactor: 1.1:相邻桶宽度最多增加 10%,默认配置下分位数的最坏相对误差约为 5%。MaxBucketNumber: 160:限制单个直方图的桶数量,避免极端离群值持续扩大组件内存占用。- 单条结构化时序取代大量
_bucket{le="..."}时序,直方图相关时序数量最多可减少约 90%。
Kubernetes 如何保持兼容
原生直方图能力集成在共享指标库 k8s.io/component-base/metrics 中,因此 kube-apiserver、kube-scheduler、kubelet、kube-controller-manager 和 kube-proxy 等组件都会继承支持。典型指标包括 apiserver_request_duration_seconds、scheduler_plugin_execution_duration_seconds 和 scheduler_scheduling_algorithm_duration_seconds。
Beta 阶段的关键设计是双重暴露:
- 经典桶继续存在,旧版仪表盘和基于
_bucket的告警不需要立即修改。 - 原生跨度通过 Prometheus Protobuf 负载同时暴露,支持原生直方图的采集器可以读取它们。
- 普通文本或 OpenMetrics 文本抓取只能传输经典桶;Prometheus 启用原生直方图抓取后,会与 Kubernetes 指标端点协商 Protobuf 格式。
因此,Kubernetes v1.37 默认启用功能门控,只表示组件已经具备双重暴露能力,并不表示 Prometheus 会自动保存原生样本。
可以这样完成采集与查询迁移
下面假设使用 Prometheus 3.x。把配置加入对应的 scrape_configs,并根据现有环境补充服务发现、TLS 和鉴权设置:
scrape_configs:
- job_name: kubernetes-apiservers
scrape_native_histograms: true
always_scrape_classic_histograms: true
kubernetes_sd_configs:
- role: endpoints
迁移期不要省略 always_scrape_classic_histograms: true。否则 Prometheus 可能只摄取原生格式,旧查询依赖的 _bucket、_count 和 _sum 时序会消失,现有面板和告警可能立即失效。
Prometheus 2.40 至 2.x 使用全局功能开关启动:
prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--enable-feature=native-histograms
这个开关会影响所有抓取目标。Prometheus 3.x 更适合按 job 配置;较新版本中,全局原生直方图功能标志已经走向弃用。
采集完成后,可以并行运行经典查询和原生查询,比较结果:
# 经典格式:保留 le 维度后再计算跨实例 P99
histogram_quantile(
0.99,
sum by (le) (
rate(apiserver_request_duration_seconds_bucket[5m])
)
)
# 原生格式:直接聚合直方图样本,不再需要 le
histogram_quantile(
0.99,
sum(
rate(apiserver_request_duration_seconds[5m])
)
)
经典的 _count 和 _sum 查询也应迁移为直方图函数。例如,可以这样计算平均请求耗时:
sum(rate(histogram_sum(apiserver_request_duration_seconds[5m])))
/
sum(rate(histogram_count(apiserver_request_duration_seconds[5m])))
实际改造时,需要保留查询所需的业务维度。例如按 verb 观察 API 请求时,应在原生查询中使用 sum by (verb),不能为了删除 le 而把所有有意义的标签一起聚合掉。
在测试环境中,还可以直接确认 Kubernetes 指标端点是否返回 Protobuf 双重暴露数据。下面的命令假设在具有 ServiceAccount 令牌且能够访问本机 API Server 的 Pod 内运行;--insecure 会跳过证书校验,只适合隔离的测试场景:
TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
curl --insecure \
-H 'Accept: application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited' \
-H "Authorization: Bearer ${TOKEN}" \
https://localhost:6443/metrics \
--output metrics.pb
ls -lh metrics.pb
响应是二进制 Protobuf,不能用普通文本搜索验证。解码后,直方图 MetricFamily 应同时包含传统 bucket 数据,以及 schema、positive_span 等原生字段。
上线顺序与回滚边界
稳妥的迁移可以分成四步:开启两种格式采集;逐个迁移 Grafana 面板和告警;在预发布及生产环境对比分位数、SLO 和告警行为;确认旧查询全部退出后,再设置 always_scrape_classic_histograms: false。最后一步才会释放经典桶产生的大量时序和存储开销。
上线前建议检查:
- Prometheus 版本是否支持目标配置项和原生直方图查询。
- 远程写入、长期存储和查询前端是否都能处理原生直方图,而不只是本地 Prometheus。
- recording rules、Grafana 变量、SLO 工具和告警规则中是否仍引用
_bucket、_count或_sum。 - 新旧查询是否使用相同的标签聚合维度和时间窗口。
- Prometheus 内存、抓取负载、WAL、远程写入流量和 TSDB 占用是否符合预期。
如果采集侧出现兼容性问题,将对应 job 的 scrape_native_histograms 设置为 false 即可停止摄取原生格式,不需要重启 Kubernetes。更彻底的组件侧回滚可以使用 --feature-gates=NativeHistograms=false,但这需要重启相关 Kubernetes 组件。
原生直方图进入 Beta 并默认启用,不代表经典桶应在升级当天被删除。合理做法是把 Kubernetes v1.37 当作迁移起点:先双写、再改查询、验证完整链路,最后关闭经典格式采集。这样既能获得更稳定的分位数精度和显著的时序节省,也能保留清晰、快速的回滚路径。