Prometheus 深度解析:指标采集、PromQL、服务发现、Recording Rule 与 Alertmanager

系统级 Prometheus 实战指南:数据模型与指标类型详解、Exporter 生态与自定义 Exporter 开发、服务发现机制(DNS/K8s/Consul/File SD)、PromQL 高级查询(聚合/子查询/二元运算/预测/同比环比)、Recording Rule 与告警规则设计、Alertmanager 分组/抑制/静默/路由、高可用联邦与远程存储、Thanos/VictoriaMetrics 扩展架构。附完整配置与实战代码。

Prometheus 不仅是一个时序数据库,它是云原生监控的事实标准。 从 Kubernetes 集群到 Spring Boot 应用,从网络设备到自定义业务指标,Prometheus 生态已经覆盖了现代可观测性的每一个角落。


一、Prometheus 架构

1.1 核心组件

Prometheus 架构:
                    ┌─────────────┐
                    │   Target    │  ──→ 被监控的应用/服务
                    │  (exporter) │      /metrics HTTP 端点
                    └──────┬──────┘
                           │ pull
                    ┌──────▼──────┐
                    │  Prometheus │  ──→ 抓取、存储、查询
                    │  Server     │
                    └──┬──────┬───┘
                       │      │
        ┌──────────────┘      └──────────────┐
        ↓                                    ↓
 ┌─────────────┐                     ┌─────────────┐
 │ Alertmanager│  ──→ 告警管理       │  Grafana    │  ──→ 可视化
 │ (分组/路由)  │                     │ (Dashboard) │
 └─────────────┘                     └─────────────┘
        ↓
 ┌─────────────┐
 │  PagerDuty /│  ──→ 通知渠道
 │  Slack /    │
 │  Webhook    │
 └─────────────┘

1.2 为什么 Pull 模式

PullPush
控制节奏,防止目标被压垮目标自主推送
集中配置,无需目标感知每个目标需配置服务器地址
便于故障检测(目标不响应 = 宕机)无法区分"没数据"和"没推送"
支持临时目标、批处理任务批处理需要 Pushgateway

二、指标类型详解

2.1 Counter(计数器)

# Python prometheus_client 示例
from prometheus_client import Counter, start_http_server

# 定义 Counter
http_requests_total = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'status', 'route']
)

# 使用
@app.route('/api/users')
def get_users():
    http_requests_total.labels(method='GET', status='200', route='/api/users').inc()
    return jsonify(users)

@app.errorhandler(500)
def handle_error(e):
    http_requests_total.labels(method='GET', status='500', route='/api/users').inc()
    return jsonify({'error': 'Internal error'}), 500
# PromQL:计算每秒请求率
rate(http_requests_total[5m])

# 按状态码分组统计
sum by (status) (rate(http_requests_total[5m]))

# 错误率
sum(rate(http_requests_total{status=~"5.."}[5m])) /
sum(rate(http_requests_total[5m]))

2.2 Gauge(仪表盘)

from prometheus_client import Gauge

# 内存使用量(可增可减)
memory_usage_bytes = Gauge(
    'memory_usage_bytes',
    'Current memory usage',
    ['instance']
)

# 更新值
memory_usage_bytes.labels(instance='web-01').set(get_memory_usage())

# 增减
queue_length = Gauge('queue_length', 'Current queue length')
queue_length.inc()   # +1
queue_length.dec(5)  # -5
queue_length.set(10) # 设为 10

2.3 Histogram(直方图)

from prometheus_client import Histogram

# 请求延迟直方图
request_duration = Histogram(
    'http_request_duration_seconds',
    'HTTP request latency',
    ['route'],
    buckets=[0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
)

# 记录延迟
start = time.time()
process_request()
request_duration.labels(route='/api/checkout').observe(time.time() - start)
# 直方图在 Prometheus 中存储为多个时间序列:
# _bucket(le="xxx"):各桶的累计计数
# _sum:所有观测值的总和
# _count:观测总数

# 计算 P95 延迟
histogram_quantile(0.95,
  sum by (le, route) (
    rate(http_request_duration_seconds_bucket{route="/api/checkout"}[5m])
  )
)

# 平均延迟
rate(http_request_duration_seconds_sum[5m]) /
rate(http_request_duration_seconds_count[5m])

2.4 Summary(摘要)

from prometheus_client import Summary

# 客户端预计算分位数(服务端压力小,客户端压力大)
request_latency = Summary(
    'http_request_latency_seconds',
    'Request latency',
    ['method'],
    # 计算 P50 和 P95
    quantiles=[(0.5, 0.05), (0.95, 0.01)]
)

Histogram vs Summary:Histogram 在服务端计算分位数,适合聚合多个实例。Summary 在客户端计算,不可聚合但精确。绝大多数场景推荐 Histogram。


三、服务发现(Service Discovery)

3.1 Kubernetes 服务发现

# prometheus.yml
scrape_configs:
  # 发现 Pod
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
        namespaces:
          names:
            - default
            - production
    relabel_configs:
      # 只抓取带有 prometheus.io/scrape: "true" 注解的 Pod
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      # 从注解中读取端口
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port]
        action: replace
        target_label: __address__
        regex: ([^:]+)(?::\d+)?;(\d+)
        replacement: $1:$2
      # 添加标签
      - source_labels: [__meta_kubernetes_namespace]
        target_label: namespace
      - source_labels: [__meta_kubernetes_pod_name]
        target_label: pod

  # 发现 Service Endpoints
  - job_name: 'kubernetes-endpoints'
    kubernetes_sd_configs:
      - role: endpoints
    relabel_configs:
      - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
        action: keep
        regex: true

3.2 File SD(文件服务发现)

# prometheus.yml
scrape_configs:
  - job_name: 'file-sd'
    file_sd_configs:
      - files:
          - /etc/prometheus/targets/*.json
        refresh_interval: 30s
// /etc/prometheus/targets/web-servers.json
[
  {
    "targets": ["web-01:9100", "web-02:9100"],
    "labels": {
      "env": "production",
      "tier": "frontend"
    }
  }
]

四、PromQL 高级查询

4.1 基础查询

# 瞬时向量 —— 当前值
http_requests_total

# 范围向量 —— 过去 5 分钟的数据
http_requests_total[5m]

# 偏移查询
http_requests_total offset 1d

# 标签过滤
http_requests_total{status="200", method="GET"}
http_requests_total{status=~"2.."}     # 正则匹配
http_requests_total{status!~"4..|5.."} # 反向正则

4.2 聚合操作

# 按标签分组求和
sum by (status) (rate(http_requests_total[5m]))

# 计算每个实例的 CPU 使用率
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Top 5 内存使用量
topk(5, container_memory_usage_bytes)

# 分位数
quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

4.3 二元运算

# 错误率计算
sum(rate(http_requests_total{status=~"5.."}[5m])) /
sum(rate(http_requests_total[5m]))

# 内存使用率
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) /
node_memory_MemTotal_bytes * 100

# 磁盘使用率
(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) /
node_filesystem_size_bytes{mountpoint="/"} * 100

4.4 预测与同比环比

# 预测 4 小时后磁盘是否满
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[6h], 4 * 3600) < 0

# 同比增长(对比昨天同一时刻)
(
  sum(rate(http_requests_total[5m]))
  -
  sum(rate(http_requests_total[5m] offset 1d))
) / sum(rate(http_requests_total[5m] offset 1d))

# 环比(对比 5 分钟前)
(
  sum(rate(http_requests_total[5m]))
  -
  sum(rate(http_requests_total[5m] offset 5m))
) / sum(rate(http_requests_total[5m] offset 5m))

五、Recording Rule 与告警规则

5.1 Recording Rule(记录规则)

# recording_rules.yml
groups:
  - name: http_rules
    interval: 30s
    rules:
      # 预计算请求率,加速查询
      - record: job:http_requests:rate5m
        expr: |
          sum by (job, status) (
            rate(http_requests_total[5m])
          )

      # P95 延迟
      - record: job:http_request_latency:p95
        expr: |
          histogram_quantile(0.95,
            sum by (job, le) (
              rate(http_request_duration_seconds_bucket[5m])
            )
          )

5.2 告警规则

# alerting_rules.yml
groups:
  - name: service_alerts
    rules:
      # 高错误率
      - alert: HighErrorRate
        expr: |
          (
            sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
            /
            sum by (job) (rate(http_requests_total[5m]))
          ) > 0.05
        for: 5m
        labels:
          severity: critical
          team: backend
        annotations:
          summary: "High error rate on {{ $labels.job }}"
          description: "Error rate is {{ $value | humanizePercentage }}"

      # 高延迟
      - alert: HighLatency
        expr: |
          histogram_quantile(0.95,
            sum by (le, job) (
              rate(http_request_duration_seconds_bucket[5m])
            )
          ) > 2
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "P95 latency > 2s on {{ $labels.job }}"

      # 实例宕机
      - alert: InstanceDown
        expr: up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Instance {{ $labels.instance }} down"

六、Alertmanager

6.1 路由与分组

# alertmanager.yml
global:
  smtp_smarthost: 'smtp.example.com:587'
  smtp_from: 'alerts@example.com'

route:
  # 默认路由
  receiver: 'default'
  group_by: ['alertname', 'job']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

  routes:
    # critical 级别 → PagerDuty
    - match:
        severity: critical
      receiver: pagerduty-critical
      continue: false

    # 按团队路由
    - match:
        team: backend
      receiver: backend-slack
    - match:
        team: frontend
      receiver: frontend-slack

receivers:
  - name: default
    slack_configs:
      - api_url: 'https://hooks.slack.com/xxx'
        channel: '#alerts'

  - name: pagerduty-critical
    pagerduty_configs:
      - service_key: '<key>'

  - name: backend-slack
    slack_configs:
      - api_url: 'https://hooks.slack.com/xxx'
        channel: '#backend-alerts'

6.2 抑制与静默

# 抑制:高错误率时不发实例宕机告警
inhibit_rules:
  - source_match:
      alertname: HighErrorRate
      severity: critical
    target_match:
      alertname: InstanceDown
    equal: ['job', 'instance']

# 静默:计划维护期间
# 通过 Alertmanager UI 或 API 创建静默规则

七、高可用与扩展

7.1 Prometheus HA 联邦

# 联邦配置:中心 Prometheus 抓取边缘 Prometheus
scrape_configs:
  - job_name: 'federate'
    scrape_interval: 15s
    honor_labels: true
    metrics_path: '/federate'
    params:
      'match[]':
        - '{job=~".*"}'
    static_configs:
      - targets:
          - 'prometheus-dc1:9090'
          - 'prometheus-dc2:9090'

7.2 Thanos 架构

Thanos 扩展架构:
┌──────────────────────────────────────────────┐
│  Querier — 全局查询层                           │
│  聚合多个 Store/Sidecar 的数据                    │
└──────────────┬───────────────────────────────┘
               │
    ┌──────────┼──────────┐
    ↓          ↓          ↓
┌───────┐  ┌───────┐  ┌───────┐
│StoreGW│  │StoreGW│  │StoreGW│  ──→ 读取对象存储的历史数据
│(S3)   │  │(S3)   │  │(GCS)  │
└───────┘  └───────┘  └───────┘
    ↑          ↑          ↑
    │          │          │
┌─────────┐  ┌─────────┐  ┌─────────┐
│Prom+Side│  │Prom+Side│  │Prom+Side│  ──→ 本地 Prometheus + Sidecar
│  (DC 1)  │  │  (DC 2)  │  │  (DC 3)  │
└─────────┘  └─────────┘  └─────────┘
    │          │          │
    ↓          ↓          ↓
  对象存储(S3/GCS/Azure Blob)← Compactor 压缩数据

7.3 VictoriaMetrics

# 用 VictoriaMetrics 替代 Prometheus(更高性能/更低资源)
# docker-compose.yml
version: '3'
services:
  victoriametrics:
    image: victoriametrics/victoria-metrics:latest
    ports:
      - "8428:8428"
    volumes:
      - vmdata:/storage
    command:
      - '-storageDataPath=/storage'
      - '-retentionPeriod=30d'

  vmagent:
    image: victoriametrics/vmagent:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - '-promscrape.config=/etc/prometheus/prometheus.yml'
      - '-remoteWrite.url=http://victoriametrics:8428/api/v1/write'

volumes:
  vmdata:

八、常用 Exporter

Exporter用途端口
node_exporterLinux 系统指标9100
blackbox_exporterHTTP/ICMP/DNS/TCP 探活9115
cadvisor容器指标8080
postgres_exporterPostgreSQL9187
redis_exporterRedis9121
mysql_exporterMySQL9104
kafka_exporterKafka9308
elasticsearch_exporterElasticsearch9114
nginx_exporterNginx9113

九、Prometheus Checklist

检查项配置/实践
指标命名规范service_name_unit_total — 全小写+下划线
标签基数控制避免高基数标签(如 user_id、order_id)
抓取间隔15-30s(平衡精度与开销)
保留期15d(本地)+ 远程存储(长期)
Recording Rule复杂查询预计算
告警 for 持续时间避免毛刺误报
标签一致性job、instance、env 在所有指标上统一
高可用Thanos / Cortex / VictoriaMetrics

参考与延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「infra」更多文章

  1. 可观测性数据存储选型:TSDB、列式存储、对象存储与成本优化
  2. 云原生 APM 与性能剖析:Continuous Profiling 与火焰图
  3. Kubernetes 可观测性实战:集群、Pod、网络、存储全链路监控