CDN监控一直是运维体系里比较难标准化的环节。一个中型团队往往同时使用多家CDN服务商,每家的API返回格式不一样:有的用JSON返回带宽数据,有的用CSV导出请求数,还有的只提供私有SDK。当这些数据需要汇总到同一个监控平台做告警和看板时,格式转换、字段对齐的工作量会持续消耗维护精力。OpenMetrics的出现为这个问题提供了思路,它定义了一套语言无关、平台无关的指标暴露规范,而Prometheus从2.x版本开始就对其原生支持,两者结合可以让CDN监控指标输出变得规范统一。

OpenMetrics规范的核心要素
OpenMetrics是CNCF旗下的项目,它在Prometheus文本暴露格式的基础上做了正式化定义,成为一份独立的规范文档。理解它的核心要素,是标准化CDN指标的第一步。
首先是指标类型。OpenMetrics明确规定了六种类型:gauge(可增可减的瞬时值,比如当前在线边缘节点数)、counter(只增不减的累计值,比如累计请求数)、histogram(分布统计,比如请求延迟分布)、summary、info和unknown。CDN监控中大部分指标都能归入前三种。缓存命中率这种比率值通常建议用两个counter相除来表达,而不是直接暴露一个百分比gauge,这样Prometheus可以在任意时间窗口内通过rate()函数重算命中率。
其次是元数据描述。每个指标必须携带HELP描述和UNIT单位声明,比如带宽指标声明UNIT为bytes,延迟指标声明UNIT为seconds。单位统一用基本单位而不是bps或ms,这是很多团队在标准化时容易忽略的细节。UNIT声明之后,Prometheus查询时可以直接得到基本单位的数值,避免各单位换算混乱。
最后是命名约定。指标名采用小写下划线分隔,命名空间一般以业务或系统开头,比如cdn_cache_hit_total、cdn_origin_bandwidth_bytes。标签用于维度拆分,常见的维度包括provider(厂商)、domain(加速域名)、region(节点区域)。一个规范的CDN指标输出示例如下:
# HELP cdn_cache_hit_total CDN缓存命中请求累计数
# UNIT cdn_cache_hit_total requests
# TYPE cdn_cache_hit_total counter
cdn_cache_hit_total{provider="vendorA",domain="static.ipipp.com",region="cn-north"} 1029384756
# HELP cdn_request_duration_seconds CDN请求处理耗时
# UNIT cdn_request_duration_seconds seconds
# TYPE cdn_request_duration_seconds histogram
cdn_request_duration_seconds_bucket{provider="vendorA",le="0.1"} 812345
cdn_request_duration_seconds_bucket{provider="vendorA",le="0.5"} 998765
cdn_request_duration_seconds_bucket{provider="vendorA",le="+Inf"} 1029384
cdn_request_duration_seconds_sum{provider="vendorA"} 234567.8
cdn_request_duration_seconds_count{provider="vendorA"} 1029384
# EOF
注意结尾的# EOF标记,这是OpenMetrics区别于Prometheus旧文本格式的显著特征,标记数据暴露的结束位置。
将CDN典型指标映射为标准格式
有了规范基础,下一步是梳理CDN业务里常见的指标,并把它们逐一映射到OpenMetrics的类型体系中。这个过程建议先建一张映射表,统一团队认知后再动手写代码。
| CDN原始指标 | 映射后指标名 | 类型 | 说明 |
|---|---|---|---|
| 回源带宽(bps) | cdn_origin_bandwidth_bytes | gauge | 建议换算为bytes基本单位 |
| 缓存命中率(百分比) | cdn_cache_hit_total / cdn_requests_total | counter | 用两个counter表达,查询时相除 |
| QPS | cdn_requests_total | counter | 用rate计算得出QPS |
| 状态码分布 | cdn_response_code_total | counter | 以code标签区分维度 |
| 边缘节点数 | cdn_edge_nodes_active | gauge | 瞬时活跃节点数量 |
映射时有两个原则值得强调。第一,能用counter就不要用gauge,counter配合rate()和increase()可以在任意时间窗口内计算变化量,数据价值远高于固定周期的gauge采样。很多CDN厂商API直接返回每分钟的命中率百分比,如果你原样暴露成gauge,后续想做五分钟窗口的滑动平均就会发现数据精度不够。正确做法是拿到命中请求数和总请求数这两个累计值再暴露。
第二,标签设计要克制。标签基数过大会导致Prometheus时序数据库膨胀,比如把用户IP作为标签就是典型的反模式。CDN场景下合理的标签维度通常是厂商、域名、区域、状态码这几类,单指标活跃序列控制在数千以内比较健康。
开发Exporter对接Prometheus抓取
完成指标映射后,需要开发一个Exporter,负责从各CDN厂商的API拉取数据并转换为OpenMetrics格式暴露。以Go语言为例,借助prometheus/client_golang库可以快速实现,这个库输出格式本身就兼容OpenMetrics规范。
package main
import (
"log"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
cacheHitTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "cdn_cache_hit_total",
Help: "CDN缓存命中请求累计数",
},
[]string{"provider", "domain", "region"},
)
originBandwidth = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "cdn_origin_bandwidth_bytes",
Help: "CDN回源带宽,单位bytes",
},
[]string{"provider", "domain"},
)
)
func init() {
prometheus.MustRegister(cacheHitTotal, originBandwidth)
}
func main() {
// 定时从各厂商API拉取数据并更新指标
go func() {
for {
fetchFromVendors(cacheHitTotal, originBandwidth)
time.Sleep(60 * time.Second)
}
}()
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":9150", nil))
}
func fetchFromVendors(hit *prometheus.CounterVec, bw *prometheus.GaugeVec) {
// 此处调用厂商API,解析后按维度递增或赋值
// 示例:hit.WithLabelValues("vendorA", "static.ipipp.com", "cn-north").Add(float64(delta))
}
代码中需要注意counter的处理方式。如果厂商API返回的是累计值,不能直接调用Add累加,否则重启后累计基数丢失会导致指标回退。推荐的做法是维护一个本地偏移量,重启时以当前API值为新基准,保证暴露出去的counter单调递增。gauge则没有这个限制,直接Set即可。
Exporter部署好之后,在Prometheus侧添加抓取配置即可。如果希望以OpenMetrics格式协商,可以显式开启对应参数:
scrape_configs:
- job_name: 'cdn-exporter'
scrape_interval: 60s
static_configs:
- targets: ['cdn-exporter:9150']
metric_relabel_configs:
- source_labels: [__name__]
regex: 'cdn_.*'
action: keep
Prometheus较新版本默认就会在抓取时通过Accept头协商OpenMetrics协议,无需额外配置即可解析# EOF结尾的格式。如果Exporter输出的是标准的旧版Prometheus文本格式,也完全不影响兼容性,这正是OpenMetrics设计的初衷之一。
在Grafana中查询标准化后的指标
指标标准化后最大的收益体现在查询侧。因为命名和单位都遵循统一约定,写PromQL时不再需要记忆各家厂商的字段差异,看板模板也可以复用。
计算全网缓存命中率的典型查询如下:
sum(rate(cdn_cache_hit_total[5m])) by (provider) / sum(rate(cdn_requests_total[5m])) by (provider)
这条语句按厂商分组计算五分钟窗口命中率,如果后续新增厂商,只要Exporter按规范输出同样命名的指标,看板自动覆盖新数据源,零改动。再看延迟分位数查询:
histogram_quantile(0.95, sum(rate(cdn_request_duration_seconds_bucket[5m])) by (le, domain) )
由于UNIT统一声明为seconds,查询结果直接就是秒,不需要在各家毫秒、秒混杂的单位之间换算。回源带宽这类gauge指标则可以直接用avg_over_time观察趋势。
告警规则同样受益于标准化。比如回源带宽占比过高可能意味着缓存失效异常,可以写一条规则:当cdn_origin_bandwidth_bytes与边缘带宽之和的比值超过阈值持续十分钟则触发告警。这类规则一旦写好,对所有厂商统一生效,不需要为每家CDN单独维护一套告警逻辑。
整体来看,用OpenMetrics标准化CDN监控指标的前期投入主要在指标映射表的设计和Exporter的开发上,但换来的是后续查询、看板、告警层面的高度统一。当团队接入新的CDN厂商或者更换监控平台时,标准化的指标输出能显著降低迁移成本,这也是投入这套体系最实际的回报。
OpenMetricsCDN监控指标Prometheus修改时间:2026-09-10 12:40:41