3. 监控基础设施搭建:Prometheus + Grafana + Spark Metrics System 集成方案
在生产环境中运行 Spark 模型推理或训练任务时,仅依赖 Spark Web UI 的实时监控远远不够。历史趋势分析、多集群统一观测、告警联动等需求,要求我们搭建一套持久化、可查询、可可视化的监控基础设施。本章将详细讲解 Prometheus + Grafana + Spark Metrics System 的集成方案,覆盖从指标暴露、数据采集到仪表盘构建的完整链路。
PrometheusSink 将指标暴露为 Prometheus 可抓取的 HTTP 端点。
3.1 整体架构与数据流
下图展示了从 Spark 应用指标产生到 Grafana 展示的完整数据流。核心组件包括:Spark Driver/Executor 内嵌的 Metrics System、PrometheusSink、Prometheus Server 以及 Grafana。
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 |
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-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 集成验证与常见问题
完成所有配置后,按照以下步骤进行端到端验证:
- 检查 PrometheusSink 端点:使用
curl访问 Driver 和 Executor 的/metrics,确认返回 Prometheus 格式数据。 - 检查 Prometheus Target 状态:在 Prometheus UI 的
/targets页面确认 Spark 应用的状态为UP。 - 检查 Grafana 数据源连通性:在 Grafana 中执行一个简单查询,如
spark_jvm_heap_usage,确认有数据返回。 - 模拟异常触发告警:通过压力测试或注入高延迟,验证告警规则是否正常触发并发送通知。
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 的存储容量,避免因指标量过大导致查询性能下降。