前言
监控是运维的"眼睛"。没有监控,系统出问题只能被动发现;告警不合理,要么漏报要么告警疲劳。Prometheus作为云原生监控的事实标准,提供了完整的指标采集、存储、查询和告警能力。
但搭建Prometheus只是第一步,如何设计指标、编写告警规则、优化查询性能,才是决定监控体系是否真正有用的关键。这篇文章从指标设计原则到告警优化策略,系统性地讲解如何构建一个高效的监控体系。
一、Prometheus架构与核心概念
1.1 数据模型
Prometheus使用时间序列数据模型:
metric_name{label1="value1", label2="value2"} timestamp value示例:
http_requests_total{method="GET", status="200", endpoint="/api/users"} 1640000000 1234标签(Labels)的作用:
- 维度化查询:
http_requests_total{status="500"} - 聚合计算:
sum(http_requests_total) by (method) - 但标签过多会导致基数爆炸(Cardinality Explosion)
1.2 指标类型
| 类型 | 说明 | 示例 |
|---|---|---|
| Counter | 只增不减的计数器 | http_requests_total |
| Gauge | 可增可减的仪表盘 | memory_usage_bytes |
| Histogram | 直方图,分桶统计 | http_request_duration_seconds_bucket |
| Summary | 摘要,计算分位数 | http_request_duration_seconds{quantile="0.95"} |
1.3 采集方式
Pull模式:Prometheus主动拉取
# prometheus.ymlscrape_configs:-job_name:'node-exporter'static_configs:-targets:['localhost:9100']Push模式:应用主动推送(通过Pushgateway)
# 应用推送指标echo"some_metric 3.14"|curl--data-binary @- http://pushgateway:9091/metrics/job/my_job二、指标设计原则:避免常见陷阱
2.1 指标命名规范
# 好的命名 http_requests_total # 单位明确(total) http_request_duration_seconds # 单位明确(seconds) memory_usage_bytes # 单位明确(bytes) # 不好的命名 http_requests # 缺少单位 request_time # 单位不明确 memory # 太模糊命名规则:
- 使用下划线分隔
- 包含单位(
_total,_seconds,_bytes) - 使用复数形式(
requests而非request)
2.2 标签设计:平衡查询灵活性与基数
问题:标签基数爆炸
# 错误示例:用户ID作为标签 http_requests_total{user_id="12345"} # 如果有100万用户,就有100万条时间序列 # 正确示例:用聚合代替 http_requests_total{endpoint="/api/users"} # 只有几个endpoint标签选择原则:
- 高基数数据不要做标签:用户ID、订单ID、IP地址
- 低基数数据适合做标签:环境(prod/staging)、服务名、HTTP方法
- 标签值数量 < 100:理想情况下每个标签的值不超过100个
2.3 指标粒度设计
# 细粒度:每个接口一个指标 http_requests_total{endpoint="/api/users"} http_requests_total{endpoint="/api/orders"} # 粗粒度:聚合后一个指标 http_requests_total{service="api"} # 所有接口聚合 # 推荐:细粒度 + 可聚合 # 细粒度用于问题定位,粗粒度用于整体监控2.4 Histogram vs Summary
Histogram(推荐):
// Go客户端示例histogram:=prometheus.NewHistogramVec(prometheus.HistogramOpts{Name:"http_request_duration_seconds",Buckets:[]float64{0.1,0.5,1.0,2.0,5.0},// 自定义分桶},[]string{"method","endpoint"},)优点:
- 可以聚合(多个实例的Histogram可以合并)
- 可以计算任意分位数
- 存储效率高
Summary:
summary:=prometheus.NewSummaryVec(prometheus.SummaryOpts{Name:"http_request_duration_seconds",Objectives:map[float64]float64{0.5:0.05,0.95:0.01},// 预定义分位数},[]string{"method","endpoint"},)缺点:
- 不能聚合(多个实例的Summary无法合并)
- 只能计算预定义的分位数
建议:优先使用Histogram
三、PromQL查询优化:提升查询性能
3.1 避免高基数查询
# 错误:查询所有时间序列 http_requests_total # 正确:使用标签过滤 http_requests_total{service="api", status="500"} # 错误:对高基数指标聚合 sum(http_requests_total) by (user_id) # user_id基数太高 # 正确:先过滤再聚合 sum(http_requests_total{status="500"}) by (service)3.2 使用Recording Rules预计算
问题:复杂查询每次都要计算,消耗CPU
解决方案:Recording Rules
# prometheus.ymlrule_files:-"recording_rules.yml"# recording_rules.ymlgroups:-name:api_rulesinterval:30s# 每30秒计算一次rules:-record:api:http_requests:rate5mexpr:rate(http_requests_total[5m])-record:api:http_errors:rate5mexpr:rate(http_requests_total{status=~"5.."}[5m])-record:api:error_rateexpr:|api:http_errors:rate5m / api:http_requests:rate5m使用预计算结果:
# 直接查询预计算结果(快) api:error_rate # 而不是每次都计算(慢) rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])3.3 合理使用时间范围
# 错误:查询太长时间范围 rate(http_requests_total[1h]) # 如果指标采集间隔是15s,1h数据量太大 # 正确:根据采集间隔选择范围 rate(http_requests_total[5m]) # 5分钟足够计算rate # 对于Gauge类型,可以用instant query memory_usage_bytes # 不需要range3.4 避免不必要的计算
# 错误:在Grafana中重复计算 sum(rate(http_requests_total[5m])) by (service) # 每次刷新都计算 # 正确:用Recording Rule预计算 sum(api:http_requests:rate5m) by (service) # 直接查询预计算结果四、告警规则设计:减少告警疲劳
4.1 告警规则结构
# alert_rules.ymlgroups:-name:api_alertsinterval:30srules:-alert:HighErrorRateexpr:|rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05for:5m# 持续5分钟才告警labels:severity:warningannotations:summary:"API错误率过高"description:"{{ $labels.service }} 错误率 {{ $value | humanizePercentage }}"4.2 告警级别设计
| 级别 | 说明 | 响应时间 | 示例 |
|---|---|---|---|
| critical | 服务不可用 | 立即 | 服务down、数据库连接失败 |
| warning | 异常但可用 | 30分钟内 | 错误率升高、延迟增加 |
| info | 信息性 | 无需响应 | 部署完成、配置变更 |
4.3 避免告警风暴
问题1:重复告警
# 错误:每个实例都告警-alert:HighCPUexpr:cpu_usage>0.8# 如果有100个实例,可能同时触发100个告警# 正确:聚合后告警-alert:HighCPUexpr:avg(cpu_usage)>0.8# 只有一个告警问题2:瞬时抖动
# 错误:瞬时值告警-alert:HighErrorRateexpr:rate(http_requests_total{status="500"}[1m])>10# 正确:使用for避免瞬时抖动-alert:HighErrorRateexpr:rate(http_requests_total{status="500"}[5m])>10for:5m# 持续5分钟才告警问题3:告警恢复通知
# 告警恢复时也发送通知(避免告警静默)-alert:HighErrorRateexpr:...annotations:summary:"API错误率{{ if eq $value 0 }}已恢复{{ else }}过高{{ end }}"4.4 告警规则最佳实践
groups:-name:service_alertsrules:# 1. 服务可用性-alert:ServiceDownexpr:up{job="api"}== 0for:1mlabels:severity:criticalannotations:summary:"服务 {{ $labels.instance }} 不可用"# 2. 错误率-alert:HighErrorRateexpr:|sum(rate(http_requests_total{status=~"5.."}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service) > 0.05for:5mlabels:severity:warningannotations:summary:"{{ $labels.service }} 错误率过高"# 3. 延迟(P95)-alert:HighLatencyexpr:|histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service) ) > 1.0for:5mlabels:severity:warningannotations:summary:"{{ $labels.service }} P95延迟过高"# 4. 资源使用-alert:HighMemoryUsageexpr:|(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.9for:10mlabels:severity:warningannotations:summary:"{{ $labels.instance }} 内存使用率过高"五、Prometheus性能优化
5.1 存储优化
问题:数据量增长导致查询慢、存储占用大
解决方案:
# prometheus.ymlglobal:scrape_interval:15s# 采集间隔(默认15s)evaluation_interval:15s# 规则评估间隔# 数据保留时间storage:tsdb:retention.time:30d# 保留30天retention.size:50GB# 或限制大小长期存储:使用Thanos或VictoriaMetrics
5.2 采集优化
减少采集目标:
# 只采集必要的指标scrape_configs:-job_name:'node-exporter'static_configs:-targets:['localhost:9100']metric_relabel_configs:# 只保留需要的指标-source_labels:[__name__]regex:'node_(cpu|memory|disk).*'action:keep减少标签:
metric_relabel_configs:# 删除不需要的标签-regex:'instance'action:labeldrop# 如果不需要instance标签5.3 查询优化
# 1. 使用Recording Rules(前面已讲) # 2. 限制查询时间范围 http_requests_total[5m] # 而不是 [1h] # 3. 使用offset避免重复查询 http_requests_total offset 1h # 查询1小时前的数据 # 4. 避免在Grafana中使用高基数查询 # 使用变量过滤 http_requests_total{service="$service"} # $service是Grafana变量六、高可用与联邦
6.1 Prometheus高可用
Prometheus本身不提供集群功能,需要外部方案:
方案1:多个Prometheus实例 + 负载均衡
Grafana -> Load Balancer -> Prometheus-1 -> Prometheus-2方案2:Prometheus联邦
# 全局Prometheus(federate)scrape_configs:-job_name:'federate'honor_labels:truescrape_interval:15sstatic_configs:-targets:-'prometheus-1:9090'-'prometheus-2:9090'metrics_path:'/federate'params:'match[]':-'{job=~".+"}'# 采集所有指标方案3:Thanos
Thanos提供长期存储、查询聚合、降采样等功能。
6.2 跨网络监控
如果Prometheus需要监控跨网络、跨机房的节点:
方案1:Pushgateway
# 节点推送指标到Pushgatewaynode_exporter|curl--data-binary @- http://pushgateway:9091/metrics/job/node/instance/node1方案2:组网工具
使用组网工具(如WireGuard、ZeroTier、星空组网等)将不同网络的节点组成虚拟内网,Prometheus可以直接通过虚拟IP拉取指标:
scrape_configs:-job_name:'cross-network-nodes'static_configs:-targets:-'10.0.0.10:9100'# 虚拟内网IP-'10.0.0.11:9100'-'10.0.0.12:9100'优势:
- 统一网络后,Prometheus配置简单
- 不需要为每个网络单独部署Pushgateway
- 支持服务发现(如Kubernetes服务发现)
七、实战案例:构建完整监控体系
7.1 案例:API服务监控
指标设计:
// Go应用暴露指标var(httpRequestsTotal=prometheus.NewCounterVec(prometheus.CounterOpts{Name:"http_requests_total",Help:"Total HTTP requests",},[]string{"method","endpoint","status"},)httpRequestDuration=prometheus.NewHistogramVec(prometheus.HistogramOpts{Name:"http_request_duration_seconds",Buckets:[]float64{0.1,0.5,1.0,2.0,5.0},},[]string{"method","endpoint"},))告警规则:
-alert:APIHighErrorRateexpr:|sum(rate(http_requests_total{status=~"5.."}[5m])) by (endpoint) / sum(rate(http_requests_total[5m])) by (endpoint) > 0.05for:5mlabels:severity:warningannotations:summary:"{{ $labels.endpoint }} 错误率过高"-alert:APIHighLatencyexpr:|histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, endpoint) ) > 1.0for:5mlabels:severity:warningannotations:summary:"{{ $labels.endpoint }} P95延迟过高"7.2 案例:数据库监控
指标:使用mysqld_exporter或postgres_exporter
告警规则:
-alert:DatabaseDownexpr:up{job="mysql"}== 0for:1mlabels:severity:critical-alert:DatabaseSlowQueriesexpr:mysql_global_status_slow_queries>100for:5mlabels:severity:warning-alert:DatabaseConnectionsHighexpr:|mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.8for:5mlabels:severity:warning八、总结
| 优化方向 | 关键措施 | 预期效果 | 注意事项 |
|---|---|---|---|
| 指标设计 | 合理标签、避免高基数 | 查询性能提升、存储节省 | 标签值数量 < 100 |
| 查询优化 | Recording Rules、合理时间范围 | 查询延迟降低 | 预计算需要额外存储 |
| 告警设计 | 聚合告警、for避免抖动 | 减少告警疲劳 | 避免漏报和误报 |
| 性能优化 | 存储限制、采集优化 | 资源占用降低 | 平衡数据保留和性能 |
| 高可用 | 多实例、联邦、Thanos | 服务可用性提升 | 复杂度增加 |
| 跨网络 | 组网工具统一网络 | 配置简化、统一管理 | 需要安全审计 |
核心思路:
- 指标设计:避免高基数、合理使用标签
- 查询优化:Recording Rules预计算、合理时间范围
- 告警设计:聚合告警、避免瞬时抖动
- 性能优化:限制存储、优化采集
- 跨网络:组网工具统一网络后监控更简单
注意事项:
- 指标基数过高会导致性能问题
- 告警规则需要持续优化,避免告警疲劳
- 跨网络场景可以用组网工具统一网络后再监控