4. Prometheus 基础回顾:Metric 类型与 PromQL 聚合算子

在构建 Kubernetes 存储组件的 SLO 与错误预算面板之前,必须牢固掌握 Prometheus 的四种核心 Metric 类型以及 PromQL 的聚合算子。这些基础构件决定了我们如何采集、建模和聚合存储指标(如 PVC 延迟、卷容量、API 调用错误率)。

4.1 四种 Metric 类型对比

Prometheus 客户端库提供四种核心 Metric 类型,每种类型适用于不同的监控场景。下表从语义、典型用途和存储特性三个维度进行对比:

类型 语义 典型用途(存储场景) 存储特性
Counter 只增不减的累计值(重启归零) CSI 卷挂载请求总数、存储 API 调用次数、磁盘写入字节累计 适合用 rate() 或 increase() 计算每秒速率
Gauge 可增可减的瞬时值 PVC 当前容量使用率、存储节点在线状态、卷挂载延迟(ms) 直接使用原始值,或通过 avg_over_time 平滑
Histogram 观测值分布(桶 + 总和 + 计数) 存储 I/O 延迟分布、卷扩容耗时分布 通过 histogram_quantile() 计算 P99/P50 延迟
Summary 预计算的分位数(客户端侧) 存储 API 响应延迟预计算分位数(较少用于存储场景) 无法聚合,适合已知分位数需求的场景
存储场景选型建议: 对于卷延迟 SLO,优先使用 Histogram,因为它支持服务端聚合计算任意分位数;对于卷容量和节点状态,使用 Gauge;对于请求计数和错误计数,使用 Counter。

4.2 Counter 与 Gauge 的 PromQL 操作

Counter 和 Gauge 是面板中最常用的两种类型,它们的 PromQL 操作方式有本质区别:

  • Counter 操作: 必须使用 rate() 或 increase() 转换为速率后再使用,否则原始累计值在面板上会呈现单调递增曲线,无法反映当前负载。
  • Gauge 操作: 可以直接使用原始值,也可以使用 avg_over_time()、max_over_time() 等聚合函数进行时间窗口平滑。
# Counter 示例:计算 CSI 卷挂载请求的每秒速率(过去 5 分钟)
rate(csi_volume_mount_requests_total[5m])

# Gauge 示例:查看当前 PVC 容量使用率(百分比)
(pvc_capacity_used_bytes / pvc_capacity_total_bytes) * 100

# Gauge 示例:过去 1 小时的平均卷延迟
avg_over_time(volume_io_latency_ms[1h])

4.3 Histogram 与 Summary 的 PromQL 操作

Histogram 和 Summary 都用于测量分布,但 PromQL 的使用方式不同:

操作 Histogram 示例 Summary 示例
计算 P99 延迟 histogram_quantile(0.99, rate(storage_io_latency_seconds_bucket[5m])) storage_api_latency_seconds{quantile="0.99"}
计算平均延迟 rate(storage_io_latency_seconds_sum[5m]) / rate(storage_io_latency_seconds_count[5m]) storage_api_latency_seconds_sum / storage_api_latency_seconds_count
聚合多个实例 支持(服务端聚合桶再计算分位数) 不支持(分位数在客户端预计算,无法聚合)
关键区别: Summary 的分位数在客户端预计算,无法跨实例聚合;Histogram 的分位数在服务端通过 PromQL 计算,支持聚合。对于 Kubernetes 存储集群的多节点场景,始终优先使用 Histogram。

4.4 PromQL 聚合算子

PromQL 聚合算子用于将多个时间序列合并为单个序列。在存储面板中,我们经常需要按 PVC、StorageClass 或节点维度聚合指标。

4.4.1 常用聚合算子一览

算子 作用 存储面板典型用法
sum() 求和 所有 PVC 的总容量使用量
avg() 平均值 集群平均卷延迟
max() / min() 最大值 / 最小值 最慢卷的延迟、最低可用容量
count() 计数 处于 Pending 状态的 PVC 数量
topk() / bottomk() 前 K 个 / 后 K 个 延迟最高的 Top 5 卷
quantile() 分位数(Gauge 专用) 卷延迟的 P50 分布(非 Histogram 场景)

4.4.2 聚合算子与 by / without 子句

聚合算子必须配合 by 或 without 子句指定保留的标签维度:

# 按 storage_class 聚合:每个 StorageClass 的总容量
sum by (storage_class) (pvc_capacity_bytes)

# 按 namespace 和 storage_class 聚合
sum by (namespace, storage_class) (pvc_capacity_bytes)

# 排除 instance 标签:聚合所有实例
sum without (instance) (csi_volume_mount_requests_total)
存储面板最佳实践: 在 SLO 面板中,通常按 namespace、storage_class、persistentvolumeclaim 三个维度聚合。避免过度聚合导致丢失故障定位所需的标签信息。

4.5 Metric 类型与 PromQL 聚合关系图

下图展示了四种 Metric 类型如何通过 PromQL 算子转化为面板可用的指标,以及它们在存储 SLO 计算中的角色:

Metric 类型 → PromQL 算子 → 存储 SLO 指标 Counter Gauge Histogram Summary PromQL 聚合算子 rate() / increase() avg_over_time() / max_over_time() histogram_quantile() sum() / avg() / max() / min() count() / topk() / bottomk() by / without 标签保留 quantile() (Gauge 专用) 卷挂载速率 容量使用率 P99 卷延迟 错误预算消耗 存储 SLO 面板中的 Metric 类型选择原则 • 错误计数 / 请求计数 → Counter → rate() 计算错误率 • 容量 / 节点状态 → Gauge → 直接展示或 avg_over_time 平滑 • 卷延迟 / 操作耗时 → Histogram → histogram_quantile 计算 P99/P50 • 聚合维度 → by(namespace, storage_class, persistentvolumeclaim)

4.6 存储场景 PromQL 实战示例

以下示例展示了如何将上述知识应用于 Kubernetes 存储组件的面板查询:

# 示例 1:计算每个 StorageClass 的卷挂载错误率(Counter + rate + sum by)
sum by (storage_class) (
  rate(csi_volume_mount_errors_total[5m])
)
/
sum by (storage_class) (
  rate(csi_volume_mount_requests_total[5m])
)

# 示例 2:计算集群中 PVC 容量使用率超过 80% 的卷数量(Gauge + count by)
count by (namespace) (
  (pvc_capacity_used_bytes / pvc_capacity_total_bytes) > 0.8
)

# 示例 3:计算每个卷的 P99 延迟(Histogram + histogram_quantile + rate)
histogram_quantile(
  0.99,
  sum by (persistentvolumeclaim, le) (
    rate(volume_io_latency_seconds_bucket[5m])
  )
)

# 示例 4:计算错误预算消耗(Counter + increase + 时间窗口)
1 - (
  increase(csi_volume_mount_errors_total[30d])
  /
  increase(csi_volume_mount_requests_total[30d])
)
关键要点: 在编写存储 SLO 面板的 PromQL 时,始终注意:
1. Counter 必须用 rate() 或 increase() 包裹;
2. Histogram 的 histogram_quantile() 必须配合 rate() 使用桶的增量;
3. 聚合时使用 by 保留故障定位所需的标签(如 persistentvolumeclaim、namespace);
4. 错误预算计算使用 increase() 在 SLO 时间窗口(如 30 天)内累计错误和请求总数。