3. 监控基础设施搭建:Prometheus + Grafana + Spark Metrics System 集成方案

在生产环境中运行 Spark 模型推理或训练任务时,仅依赖 Spark Web UI 的实时监控远远不够。历史趋势分析、多集群统一观测、告警联动等需求,要求我们搭建一套持久化、可查询、可可视化的监控基础设施。本章将详细讲解 Prometheus + Grafana + Spark Metrics System 的集成方案,覆盖从指标暴露、数据采集到仪表盘构建的完整链路。

前置说明:本方案适用于 Spark 2.4+ 及 Spark 3.x 系列。Spark Metrics System 原生支持将指标输出到多种 Sink(如 Console、JMX、CSV、Graphite),我们通过自定义或社区扩展的 PrometheusSink 将指标暴露为 Prometheus 可抓取的 HTTP 端点。

3.1 整体架构与数据流

下图展示了从 Spark 应用指标产生到 Grafana 展示的完整数据流。核心组件包括:Spark Driver/Executor 内嵌的 Metrics System、PrometheusSink、Prometheus Server 以及 Grafana。

Spark 监控基础设施集成架构图 Spark 应用 Driver / Executor Metrics System PrometheusSink HTTP 端点暴露 /metrics Prometheus Server 数据抓取 & 存储 告警规则 Grafana 仪表盘 & 可视化 告警展示 运维 / 开发人员 数据流说明: 1. Spark 应用通过 Metrics System 收集 JVM / 任务 / Shuffle 指标 2. PrometheusSink 将指标格式化为 Prometheus 文本格式 3. Prometheus Server 定期抓取 /metrics 端点,存储时间序列数据 查询与可视化: 4. Grafana 以 Prometheus 为数据源,通过 PromQL 查询指标 5. 用户通过 Grafana 仪表盘实时监控 Spark 应用状态

3.2 组件选型与版本兼容性

在搭建前,需要确认各组件的版本兼容关系。下表列出了推荐版本组合及关键注意事项。

组件 推荐版本 关键说明
Apache Spark 3.2.0+ 内置 Metrics System,无需额外依赖;3.0+ 对 PrometheusSink 支持更友好
Prometheus 2.45+ 支持 HTTP SD 和服务发现;推荐使用 --web.enable-lifecycle 热加载配置
Grafana 9.5+ / 10.x 支持 Prometheus 数据源原生集成;推荐使用变量模板制作动态仪表盘
PrometheusSink (社区版) 1.0.0+ 需额外引入 JAR 包;支持 Spark 2.4+ 和 3.x;推荐 prometheus-spark-metrics-sink
版本兼容性警告:Spark 3.0 之后 Metrics System 的配置方式有细微变化。如果使用 Spark 2.4,请确保 PrometheusSink 的 JAR 包编译时使用了对应的 Scala 版本(2.11 vs 2.12)。建议统一使用 Scala 2.12 版本以避免类加载冲突。

3.3 PrometheusSink 部署与配置

PrometheusSink 是打通 Spark 与 Prometheus 的关键桥梁。以下步骤演示如何将其集成到 Spark 应用中。

3.3.1 获取 PrometheusSink JAR 包

推荐使用社区维护的 prometheus-spark-metrics-sink 项目。可以通过 Maven 坐标引入,或直接下载预编译 JAR。

# Maven 依赖(适用于 Spark 3.x + Scala 2.12)
<dependency>
    <groupId>ch.cern.sparkmetrics</groupId>
    <artifactId>prometheus-spark-metrics-sink</artifactId>
    <version>1.0.0</version>
</dependency>

# 或直接下载 JAR 包
wget https://github.com/cerndb/prometheus-spark-metrics-sink/releases/download/v1.0.0/prometheus-spark-metrics-sink_2.12-1.0.0.jar

3.3.2 配置 Spark Metrics System

在 Spark 的 conf/metrics.properties 文件中添加 PrometheusSink 配置。如果使用 spark-submit,也可以通过 --conf spark.metrics.conf 指定自定义配置文件。

# 启用 PrometheusSink
*.sink.prometheus.class=ch.cern.sparkmetrics.PrometheusSink
*.sink.prometheus.port=8090
*.sink.prometheus.path=/metrics

# 可选:为 Driver 和 Executor 分别配置端口偏移
driver.sink.prometheus.port=8091
executor.sink.prometheus.port=8092
关键配置说明:
  • *.sink.prometheus.port:PrometheusSink 暴露 HTTP 端点的端口号。Driver 和 Executor 默认使用相同端口,生产环境建议通过端口偏移避免冲突。
  • *.sink.prometheus.path:HTTP 路径,默认 /metrics,Prometheus 抓取时使用此路径。
  • 如果 Spark 应用运行在 Kubernetes 上,建议将端口配置为 0(随机端口),并通过 Service 或 Pod 注解暴露。

3.3.3 启动 Spark 应用并验证

启动 Spark 应用时,需要将 PrometheusSink JAR 包添加到 classpath 中。

spark-submit \
  --class com.example.SparkModelInference \
  --master yarn \
  --deploy-mode cluster \
  --jars prometheus-spark-metrics-sink_2.12-1.0.0.jar \
  --conf spark.metrics.conf=metrics.properties \
  my-spark-app.jar

应用启动后,访问 Driver 或 Executor 的 /metrics 端点验证指标是否正常暴露:

# 假设 Driver 所在节点 IP 为 192.168.1.100
curl http://192.168.1.100:8091/metrics

# 预期输出示例(Prometheus 文本格式)
# HELP spark_executor_threadpool_activeTasks Active tasks in the thread pool
# TYPE spark_executor_threadpool_activeTasks gauge
spark_executor_threadpool_activeTasks 3.0
# HELP spark_jvm_heap_usage JVM Heap Memory Usage
# TYPE spark_jvm_heap_usage gauge
spark_jvm_heap_usage 0.45

3.4 Prometheus Server 配置与抓取

Prometheus Server 需要配置抓取任务(scrape job)来定期拉取 Spark 应用的指标数据。对于动态变化的 Spark 应用(如 YARN 或 Kubernetes 上的临时任务),推荐使用 服务发现 机制。

3.4.1 静态配置(适用于固定节点测试)

# prometheus.yml
scrape_configs:
  - job_name: 'spark-app'
    static_configs:
      - targets: ['192.168.1.100:8091']  # Driver 端点
        labels:
          app: 'my-spark-app'
          role: 'driver'
      - targets: ['192.168.1.101:8092']  # Executor 端点
        labels:
          app: 'my-spark-app'
          role: 'executor'

3.4.2 基于 Consul 的服务发现(生产推荐)

Spark 应用启动时,通过脚本将 PrometheusSink 端点注册到 Consul,Prometheus 通过 Consul SD 自动发现。

# prometheus.yml
scrape_configs:
  - job_name: 'spark-dynamic'
    consul_sd_configs:
      - server: 'localhost:8500'
        services: ['spark-metrics']
    relabel_configs:
      - source_labels: ['__meta_consul_service_metadata_role']
        target_label: 'role'
      - source_labels: ['__meta_consul_service_metadata_app_name']
        target_label: 'app_name'
生产实践:对于 YARN 集群,可以使用 yarn-api 服务发现或基于文件的服务发现(file_sd_configs),由调度系统在应用启动时动态生成 targets 文件。对于 Kubernetes,推荐使用 kubernetes_sd_configs 结合 Pod 注解自动发现。

3.5 Grafana 仪表盘构建

Grafana 作为可视化层,需要配置 Prometheus 数据源并导入或创建仪表盘。以下是一个针对 Spark 模型监控的仪表盘设计思路。

3.5.1 添加 Prometheus 数据源

# 通过 Grafana API 或 UI 添加数据源
# 数据源类型: Prometheus
# URL: http://prometheus-server:9090
# Access: Server (默认)
# 保存并测试

3.5.2 核心监控面板设计

面板名称 PromQL 查询示例 监控目标
JVM 堆内存使用率 spark_jvm_heap_usage{app_name="$app"} 检测内存泄漏或 OOM 风险
活跃任务数 spark_executor_threadpool_activeTasks{app_name="$app"} 评估当前并行度与负载
Shuffle 读写速率 rate(spark_shuffle_bytesWritten{app_name="$app"}[1m]) 分析数据倾斜或网络瓶颈
模型推理延迟(自定义指标) spark_model_inference_latency_seconds{app_name="$app"} 监控推理性能是否符合 SLA
GC 暂停时间 rate(spark_jvm_gc_time{app_name="$app"}[1m]) 发现 GC 导致的抖动

3.5.3 使用变量实现多应用切换

在 Grafana 仪表盘设置中,添加 变量 $app,类型为 Query,数据源为 Prometheus,查询语句为:

label_values(spark_jvm_heap_usage, app_name)

这样可以在仪表盘顶部通过下拉菜单切换不同的 Spark 应用,无需为每个应用创建单独的仪表盘。

3.6 告警规则配置

Prometheus 的告警规则可以及时发现异常。以下是一个针对 Spark 模型监控的告警规则示例。

# spark_alerts.yml
groups:
  - name: spark_model_alerts
    rules:
      - alert: HighJVMHeapUsage
        expr: spark_jvm_heap_usage{role="driver"} > 0.85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Driver JVM 堆内存使用率超过 85%"
          description: "应用 {{ $labels.app_name }} Driver 堆内存使用率 {{ $value | humanizePercentage }}"

      - alert: InferenceLatencyHigh
        expr: spark_model_inference_latency_seconds{quantile="0.99"} > 2.0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "模型推理 P99 延迟超过 2 秒"
          description: "应用 {{ $labels.app_name }} 推理延迟异常"
告警配置注意:自定义指标(如 spark_model_inference_latency_seconds)需要在 Spark 应用中通过 MetricsSystem 注册并更新。建议在模型推理的关键路径上添加 Timer 或 Histogram 类型的指标,以便在 Prometheus 中计算分位数。

3.7 集成验证与常见问题

完成所有配置后,按照以下步骤进行端到端验证:

  1. 检查 PrometheusSink 端点:使用 curl 访问 Driver 和 Executor 的 /metrics,确认返回 Prometheus 格式数据。
  2. 检查 Prometheus Target 状态:在 Prometheus UI 的 /targets 页面确认 Spark 应用的状态为 UP。
  3. 检查 Grafana 数据源连通性:在 Grafana 中执行一个简单查询,如 spark_jvm_heap_usage,确认有数据返回。
  4. 模拟异常触发告警:通过压力测试或注入高延迟,验证告警规则是否正常触发并发送通知。

3.7.1 常见问题排查

问题现象 可能原因 解决方案
Prometheus 抓取超时 Spark 应用 GC 暂停或网络拥堵 增加 Prometheus 的 scrape_timeout;检查网络带宽
指标数据缺失 PrometheusSink 端口冲突或未正确启动 检查 Spark 日志中是否有 PrometheusSink 初始化信息;使用 netstat 确认端口监听
Grafana 面板显示无数据 PromQL 查询条件不匹配或标签错误 在 Prometheus UI 中直接执行查询,确认标签名(如 app_name vs app)
告警未触发 告警规则语法错误或 for 时间过长 在 Prometheus UI 的 /alerts 页面检查规则状态;缩短 for 时间进行测试
最佳实践总结:
  • 始终为 PrometheusSink 配置独立的端口范围,避免与 Spark 其他服务冲突。
  • 在 Grafana 中使用变量和模板,实现一个仪表盘监控所有 Spark 应用。
  • 将告警规则与 Prometheus 的 alertmanager 集成,配置邮件、钉钉或 Slack 通知。
  • 定期检查 Prometheus 的存储容量,避免因指标量过大导致查询性能下降。