Prometheus/Grafana 监控体系:从指标采集到告警收敛的深度部署
Prometheus/Grafana 监控体系:从指标采集到告警收敛的深度部署
一、监控盲区下的生产事故:当关键指标未被采集
一次线上事故复盘发现,数据库连接池耗尽导致服务大面积超时,但监控系统没有任何告警。原因很简单:只监控了 CPU 和内存,没有采集数据库连接池指标。等 CPU 飙高触发告警时,故障已经蔓延了 8 分钟。
这类问题的本质是监控体系设计不完整。很多团队的监控建设停留在"部署 Prometheus + Grafana,导入几个 Dashboard"的阶段,缺乏系统性的指标规划、采集策略和告警收敛设计。结果就是:该告警的没告警,不该告警的狂告警,Grafana 面板好看但实战价值有限。
一套生产级监控体系,需要从指标分层、采集架构、存储优化、告警收敛四个维度系统设计,而非简单堆砌组件。
二、监控体系架构:从 Exporter 到 Alertmanager 的数据流转
flowchart LR subgraph 采集层 A[Node Exporter] --> P[Prometheus Server] B[cAdvisor] --> P C[Kube State Metrics] --> P D[自定义业务 Exporter] --> P E[Blackbox Exporter] --> P end subgraph 存储与计算层 P --> F[本地 TSDB] P --> G[Remote Write] G --> H[Thanos Receive / Cortex] H --> I[对象存储 S3/MinIO] end subgraph 可视化与告警层 P --> J[Grafana Dashboard] P --> K[Alertmanager] K --> L[告警分组/抑制/静默] L --> M[钉钉/企微/PagerDuty] end subgraph 长期存储与查询 H --> N[Thanos Query] I --> N N --> J end关键机制解析:
1. 指标分层模型(USE + RED 方法论)
生产监控需要覆盖四个层次,每层采用不同的方法论:
| 层次 | 方法论 | 核心指标 | 采集源 |
|---|---|---|---|
| 基础设施 | USE(Utilization/Saturation/Errors) | CPU 利用率、内存饱和度、磁盘 IO 错误率 | Node Exporter |
| 容器运行时 | USE | 容器 CPU/内存 limits 使用率、OOM 事件 | cAdvisor |
| 编排平台 | RED(Rate/Errors/Duration) | Pod 重启次数、Deployment 副本偏差 | Kube State Metrics |
| 业务应用 | RED + 自定义 | 请求速率、错误率、P99 延迟、连接池使用率 | 自定义 Exporter |
2. Prometheus 远程读写与长期存储
Prometheus 本地 TSDB 适合短期存储(默认 15 天),长期存储需要通过 Remote Write 将数据写入 Thanos 或 Cortex。Thanos 的优势是兼容原生 Prometheus 查询语法,Cortex 的优势是多租户支持和更强的水平扩展能力。
3. Alertmanager 告警收敛三机制
- 分组(Grouping):将相同标签的告警合并为一条通知,避免告警风暴
- 抑制(Inhibition):当高优先级告警触发时,自动静默相关的低优先级告警
- 静默(Silencing):在计划维护期间,按规则静默特定告警
三、生产级监控部署与告警配置
3.1 Prometheus 核心配置
# prometheus.yml - 生产级 Prometheus 配置 global: scrape_interval: 15s # 默认采集间隔 evaluation_interval: 15s # 规则评估间隔 scrape_timeout: 10s # 采集超时 # 外部标签用于 Thanos 多集群查询时区分来源 external_labels: cluster: 'prod-cluster-01' replica: 'prometheus-0' # 远程写入 Thanos,实现长期存储 remote_write: - url: "http://thanos-receive:19291/api/v1/receive" queue_config: max_samples_per_send: 10000 # 每批发送的最大样本数 batch_send_deadline: 5s # 批量发送超时 max_shards: 50 # 最大分片数,控制写入并发 # 写入失败时重试,避免数据丢失 send_exemplars: true scrape_configs: # K8s 服务发现:自动发现带注解的 Service - job_name: 'kubernetes-service-endpoints' kubernetes_sd_configs: - role: endpoints # 只采集带 prometheus.io/scrape=true 注解的服务 relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: namespace3.2 告警规则与收敛策略
# alert_rules.yml - 分层告警规则 groups: - name: infrastructure.alerts rules: # 基础设施层:节点内存压力 - alert: NodeMemoryPressure expr: | (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) > 0.9 for: 3m labels: severity: critical layer: infrastructure annotations: summary: "节点 {{ $labels.instance }} 内存使用率超过 90%" runbook: "https://wiki.internal/runbook/node-memory-pressure" # 基础设施层:磁盘 IO 延迟 - alert: NodeDiskIOLatency expr: | rate(node_disk_read_time_seconds_total[5m]) > 0.1 for: 5m labels: severity: warning layer: infrastructure - name: application.alerts rules: # 应用层:HTTP 错误率飙升 - alert: HighErrorRate expr: | (sum(rate(http_requests_total{status=~"5.."}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service)) > 0.05 for: 2m labels: severity: critical layer: application annotations: summary: "服务 {{ $labels.service }} 5xx 错误率超过 5%" # 应用层:数据库连接池使用率 - alert: DBConnectionPoolExhaustion expr: | (db_connection_pool_active / db_connection_pool_max) > 0.85 for: 3m labels: severity: warning layer: application3.3 Alertmanager 告警收敛配置
# alertmanager.yml - 告警收敛与路由 route: # 顶层分组:按集群和告警级别分组 group_by: ['cluster', 'severity'] group_wait: 30s # 首次告警等待 30s,收集同组告警 group_interval: 5m # 同组新告警的发送间隔 repeat_interval: 4h # 未恢复告警的重复发送间隔 routes: # Critical 级别:立即通知,缩短等待时间 - match: severity: critical group_wait: 10s group_interval: 1m repeat_interval: 1h receiver: 'critical-pagerduty' # 基础设施层告警:发送到运维频道 - match: layer: infrastructure receiver: 'infra-wechat' # 抑制规则:节点 Down 时,抑制该节点上所有服务告警 inhibit_rules: - source_match: alertname: NodeDown severity: critical target_match: severity: warning # 同一节点上的告警被抑制 equal: ['instance'] # 静默规则示例(通过 API 动态创建) # 在计划维护期间静默特定命名空间的告警 # amtool silence add --namespace=maintenance --duration=2h "namespace=staging"3.4 自定义业务 Exporter 框架
from prometheus_client import start_http_server, Gauge, Counter, Histogram import time import logging from typing import Callable logger = logging.getLogger(__name__) class BusinessExporter: """自定义业务指标导出器框架""" def __init__(self, port: int = 9101): self.port = port # 数据库连接池使用率:按服务名和池名分组 self.db_pool_usage = Gauge( 'db_connection_pool_usage_ratio', 'Database connection pool usage ratio', ['service', 'pool_name'] ) # 请求延迟直方图:P50/P95/P99 self.request_duration = Histogram( 'http_request_duration_seconds', 'HTTP request duration in seconds', ['service', 'method', 'endpoint'], buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0] ) # 业务错误计数器 self.business_errors = Counter( 'business_errors_total', 'Total business logic errors', ['service', 'error_type'] ) def collect_db_pool_metrics(self, pool_fetcher: Callable): """采集数据库连接池指标 pool_fetcher: 返回 [(service, pool_name, active, max)] 的回调函数 """ try: pools = pool_fetcher() for service, pool_name, active, max_conn in pools: ratio = active / max_conn if max_conn > 0 else 0 self.db_pool_usage.labels( service=service, pool_name=pool_name ).set(ratio) except Exception as e: # 采集失败时记录日志,不影响其他指标 logger.error("采集数据库连接池指标失败: %s", e) def run(self, collect_interval: int = 15): """启动 Exporter HTTP 服务""" start_http_server(self.port) logger.info("业务 Exporter 启动,端口: %d", self.port) while True: time.sleep(collect_interval) # 周期性采集业务指标 self.collect_db_pool_metrics(self._default_pool_fetcher) @staticmethod def _default_pool_fetcher(): """默认的连接池数据获取逻辑,需根据实际环境替换""" return []四、监控体系的架构权衡与成本考量
权衡一:采集精度与存储成本
采集间隔从 15s 降到 5s,指标精度提升 3 倍,但存储量也增加 3 倍。对于 1000 个实例的集群,5s 间隔下每天约产生 2000 万个样本,15s 间隔下约 670 万个。建议基础设施层用 15s,关键业务指标用 5s,非关键指标用 30s-60s。
权衡二:本地存储与远程写入的可靠性
Remote Write 是异步操作,网络抖动可能导致数据丢失。Prometheus 使用 WAL(Write-Ahead Log)缓冲,但缓冲区有限。在弱网环境下,建议先写入本地 TSDB,再通过 Thanos Sidecar 异步上传,避免 Remote Write 阻塞采集。
权衡三:告警灵敏度与噪声水平
for子句控制告警的持续时间阈值。for: 1m灵敏度高但噪声大,for: 10m噪声低但延迟高。生产建议:Critical 级别for: 2-3m,Warning 级别for: 5-10m,并配合 Alertmanager 分组抑制降低噪声。
适用边界:
- Prometheus 的拉模型适合内网环境。跨公网监控需要 Pushgateway 或 Thanos Agent,但 Pushgateway 不支持标签自动发现,需谨慎使用。
- 单机 Prometheus 适合 100 万以内的活跃时间序列。超过此规模,必须引入 Thanos/Cortex 做联邦查询和分片。
禁用场景:
- 高基数指标(如 user_id 作为标签)会导致时间序列爆炸,严禁在 Prometheus 中直接存储。应使用聚合或 Loki 等日志系统替代。
- 实时性要求毫秒级的场景(如交易系统风控),Prometheus 的采集间隔无法满足,需要专门的流式指标系统。
五、总结
生产级监控体系的建设不是组件堆砌,而是从指标规划到告警收敛的系统工程。核心要点:
- 指标分层设计:基础设施用 USE 方法论,应用层用 RED 方法论,业务层用自定义指标,确保每层都有对应采集源。
- 告警收敛三件套:分组、抑制、静默必须组合使用,否则告警噪声会淹没真正重要的信号。
- 存储分层架构:短期数据存本地 TSDB,长期数据通过 Remote Write 写入 Thanos/Cortex,兼顾查询性能和存储成本。
- 自定义 Exporter 补齐业务盲区:连接池、队列深度、业务错误率等指标无法从通用 Exporter 获取,必须开发自定义 Exporter。
落地路线建议:先完成基础设施和容器层的监控覆盖,确保 USE 指标无盲区;再逐步接入应用层 RED 指标和业务自定义指标;最后配置告警收敛策略,将告警噪声降低 80% 以上,让每条告警都值得响应。
