2. 运维监控体系搭建:Prometheus + Grafana 监控特征存储、计算节点与 API 网关

监控体系,说白了就是运维人员的眼睛。没有它,你就像在黑夜里开车——出了故障只能靠摸。我在搭建特征平台初期,就吃过这个亏。有一次计算节点内存泄漏,直到用户反馈特征获取超时,我们才发现节点已经 OOM 重启三次了。嗯,从那以后,我第一件事就是把监控体系立起来。

这一章,我们聚焦三个核心组件:特征存储、计算节点和 API 网关。我会用 Prometheus 做指标采集,Grafana 做可视化展示。下面直接上实战。

2.1 整体架构设计

先看一张图,理清数据流和组件关系。

特征平台监控体系架构图 特征存储 Redis / MySQL / HBase 计算节点 Flink / Spark / 自定义 API 网关 Nginx / Kong / Envoy Prometheus 指标采集 Pull 模式 / Pushgateway Grafana 可视化 仪表盘 / 告警规则 告警通知(钉钉 / 邮件 / PagerDuty) 每个组件暴露 /metrics 端点 Prometheus 定期拉取

核心思路很简单:每个组件暴露 /metrics 端点,Prometheus 定期拉取,Grafana 负责展示和告警。我个人习惯把告警规则也写在 Grafana 里,这样配置统一,不用再维护一套 Alertmanager 的规则文件。

2.2 特征存储监控

特征存储是特征平台的心脏。我见过太多因为存储抖动导致特征获取延迟的案例。监控重点放在连接数、延迟和内存上。

2.2.1 Redis 监控(以 Redis 为例)

Redis 的 Prometheus exporter 很成熟。我用的是 redis_exporter,直接暴露指标。

# 启动 redis_exporter
./redis_exporter \
  --redis.addr=localhost:6379 \
  --web.listen-address=0.0.0.0:9121

# 验证指标
curl http://localhost:9121/metrics | grep redis

关键指标我列在下面:

指标名 含义 告警阈值
redis_connected_clients 当前连接数 > 最大连接数 80%
redis_memory_used_bytes 已用内存 > maxmemory 85%
redis_db_keys Key 数量 突增 50% 以上
redis_commands_duration_seconds_total 命令执行耗时 P99 > 100ms
注意: 我曾经遇到过一个坑——redis_exporter 默认会扫描所有 DB 的 key 数量,如果实例里 key 特别多(比如上千万),扫描本身就会造成性能抖动。建议加上 --check-keys= 参数,只监控关键 DB。

2.2.2 自定义存储指标

如果特征存储是自己封装的 SDK(比如基于 MySQL 或 HBase),我建议在 SDK 内部埋点。用 Prometheus 的 client 库暴露自定义指标。

from prometheus_client import Histogram, Gauge, start_http_server

# 特征读取延迟
feature_read_latency = Histogram(
    'feature_read_latency_seconds',
    'Feature read latency in seconds',
    buckets=[0.001, 0.005, 0.01, 0.05, 0.1, 0.5]
)

# 存储连接池状态
storage_conn_pool_size = Gauge(
    'storage_conn_pool_size',
    'Current connection pool size'
)

# 在读取特征时记录
def get_feature(key):
    with feature_read_latency.time():
        return storage.get(key)

嗯,这里要注意:指标名一定要规范。我见过团队里有人用 feature_read_time,有人用 read_feature_latency,最后 Grafana 面板上乱成一团。建议统一用 <组件>_<操作>_<单位> 的格式。

2.3 计算节点监控

计算节点负责特征加工,通常是流式作业(Flink)或批处理(Spark)。监控的核心是处理延迟和背压。

2.3.1 Flink 作业监控

Flink 自带 Prometheus 集成,在 flink-conf.yaml 里配置:

metrics.reporter.prom.class: org.apache.flink.metrics.prometheus.PrometheusReporter
metrics.reporter.prom.port: 9249

然后 Prometheus 配置抓取:

scrape_configs:
  - job_name: 'flink'
    static_configs:
      - targets: ['flink-jobmanager:9249', 'flink-taskmanager-1:9249']

我重点关注的 Flink 指标:

  • flink_taskmanager_job_task_operator_currentNumberOfElementsInBuffer —— 背压信号,超过 100 就要警惕
  • flink_jobmanager_job_latency_source_id_operator_id_operator_subtask_operator_latency —— 端到端延迟,P99 超过 5 秒就要排查
  • flink_jobmanager_job_numberOfFailedCheckpoints —— 检查点失败次数,连续失败 3 次触发告警
个人经验: 计算节点最容易出问题的是背压。有一次我发现特征产出延迟从 2 秒飙升到 30 秒,查了半天发现是下游存储写入变慢,导致 Flink 算子 buffer 堆积。后来我在 Grafana 上专门加了一个「背压热力图」,一眼就能看出哪个算子堵了。

2.3.2 自定义计算节点

如果计算节点是自己写的(比如 Python 进程),我建议用 prometheus_client 暴露处理进度和错误率。

from prometheus_client import Counter, Gauge

# 处理记录数
records_processed = Counter('records_processed_total', 'Total records processed')
# 当前处理延迟(秒)
processing_lag = Gauge('processing_lag_seconds', 'Current processing lag')

# 每处理一批数据更新
def process_batch(batch):
    records_processed.inc(len(batch))
    processing_lag.set(time.time() - batch[-1].timestamp)

2.4 API 网关监控

API 网关是特征平台的入口。用户能不能拿到特征,全看网关稳不稳。监控重点:QPS、延迟、错误率。

2.4.1 Nginx 监控(以 Nginx 为例)

Nginx 用 nginx-prometheus-exporter 暴露指标。前提是 Nginx 开启 stub_status。

# nginx.conf 配置
location /nginx_status {
    stub_status on;
    allow 127.0.0.1;
    deny all;
}

# 启动 exporter
./nginx-prometheus-exporter \
  --nginx.scrape-uri=http://localhost:80/nginx_status

关键指标:

指标名 含义 告警阈值
nginx_connections_active 活跃连接数 > worker_connections 80%
nginx_http_requests_total 总请求数 用于计算 QPS
nginx_up Nginx 是否存活 0 触发告警

2.4.2 自定义 API 网关指标

如果网关是自己写的(比如基于 Go 或 Java),我建议在中间件里统一采集。下面是一个 Go 的例子:

import "github.com/prometheus/client_golang/prometheus"

var (
    httpRequestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request latency in seconds",
            Buckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 5},
        },
        []string{"path", "method", "status"},
    )
    httpRequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests",
        },
        []string{"path", "method", "status"},
    )
)
核心原则: API 网关的告警要分等级。比如 5xx 错误率超过 1% 是 P0 告警,需要立即响应;延迟 P99 超过 500ms 是 P1 告警,可以在 15 分钟内处理。我习惯在 Grafana 上建一个「网关健康看板」,把 QPS、延迟、错误率放在同一张图上,方便快速定位。

2.5 Grafana 仪表盘搭建

数据采集好了,接下来就是可视化。我一般按组件分 Dashboard,每个 Dashboard 包含 4-6 个核心面板。

2.5.1 特征存储仪表盘

  • 连接数趋势:折线图,展示过去 1 小时连接数变化
  • 内存使用率:仪表盘图,显示当前内存占比
  • 命令延迟 P99:热力图,按命令类型分组
  • Key 数量变化:面积图,观察是否有突增

2.5.2 计算节点仪表盘

  • 处理延迟:时间序列图,展示端到端延迟
  • 背压状态:热力图,按算子分组
  • 检查点状态:状态图,显示成功/失败次数
  • 吞吐量:折线图,展示每秒处理记录数

2.5.3 API 网关仪表盘

  • QPS 趋势:折线图,按路径分组
  • 延迟分布:柱状图,展示 P50/P90/P99
  • 错误率:饼图,按状态码分组
  • 上游健康:状态列表,显示每个上游节点的存活状态
一个小技巧: Grafana 的告警规则可以直接写在面板上。比如在「延迟分布」面板上右键 -> Alert,设置 P99 超过 500ms 触发告警。这样告警和面板绑定,维护起来方便。我现在的团队已经全面用这种方式,不再单独维护 Alertmanager 规则文件了。

2.6 告警规则配置

告警不是越多越好。我见过有人配了 50 条告警规则,结果天天被骚扰,最后大家直接忽略告警。我的原则是:每条告警都要能对应一个明确的故障场景。

下面是我常用的几条核心告警规则:

告警名称 表达式 严重级别 响应时间
存储连接池耗尽 redis_connected_clients / redis_config_maxclients > 0.8 P0 5 分钟
计算节点背压 flink_taskmanager_job_task_operator_currentNumberOfElementsInBuffer > 100 P1 15 分钟
API 网关 5xx 率 rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01 P0 立即
特征读取延迟突增 histogram_quantile(0.99, rate(feature_read_latency_seconds_bucket[5m])) > 0.1 P1 10 分钟
避坑指南: 我曾经把告警阈值设得太敏感,比如存储连接数超过 50% 就告警。结果每天收到几十条告警,全是正常的业务波动。后来我把阈值调到 80%,并加了持续 5 分钟的条件,告警量降了 90%,而且真正出问题时一条都没漏。

2.7 小结

监控体系搭建,说白了就是三个步骤:埋点、采集、展示。埋点要规范,采集要稳定,展示要直观。我个人习惯是先搭一个最小可用版本,再逐步完善。别想着一步到位,先把核心指标跑起来,后面再慢慢加细节。