前面已经把 Prometheus、Flink 指标和 Grafana 看板都串起来了。到这里,监控链路已经能看到指标,但还缺少最关键的一步:指标异常时不能只靠人工查看,而应该由 Prometheus 主动计算规则并触发告警。
Flink 作业运行时,比较常见的问题有几类:
- JobManager 或 TaskManager 进程挂掉
- TaskManager 重启或反复抖动
- TaskManager 从 JobManager 中注销,导致可用 Slot 下降
- 预期长期运行的作业不再运行
这些问题如果只靠人工看 Grafana,很容易错过。更合适的做法是把这些判断写成 Prometheus 告警规则,让 Prometheus 定期计算规则,并在异常时进入 pending 或 firing 状态。
这一篇先配置 Prometheus 告警规则,并通过停止 Flink TaskManager 的方式验证规则是否真实触发。
1. 环境说明
当前仍然使用前几篇文章中的同一台服务器,Prometheus 和 Flink 已经配置完成:
hostname
date '+%Y-%m-%d %H:%M:%S'
/opt/module/prometheus-3.2.1/promtool --version
curl -s http://localhost:8081/overview
输出如下:
lavm-bzoq5mwl1h
2025-05-29 20:07:22
promtool, version 3.2.1
{"taskmanagers":1,"slots-total":1,"slots-available":1,"jobs-running":0,"jobs-finished":0,"jobs-cancelled":0,"jobs-failed":0,"flink-version":"1.20.1","flink-commit":"cb1e7b5"}
当前实验环境没有提交长期运行的 Flink 作业,所以后面的 FlinkNoRunningJobs 会触发,这是符合预期的。
当前 Prometheus 已经采集 Flink 的两个 Reporter 端口:
| job | target | 说明 |
|---|---|---|
| flink_jobmanager | localhost:9249 | JobManager 指标 |
| flink_taskmanager | localhost:9250 | TaskManager 指标 |
这里的 localhost 只适用于 Prometheus 和 Flink 部署在同一台机器上的实验环境;如果 Prometheus 单独部署,需要改成 Flink 所在机器的内网 IP 或 DNS。9249/9250 是 Flink Reporter 暴露指标的 HTTP 端口,生产环境不建议直接公网开放,应通过内网、安全组、防火墙或服务发现方式采集。
先确认采集状态:
curl -G -s 'http://localhost:9090/api/v1/query' \
--data-urlencode 'query=up{job="flink_taskmanager"}'
TaskManager 正常时返回。下面只截取 result 数组中的关键部分,不是完整 JSON 返回:
{
"metric": {
"__name__": "up",
"instance": "localhost:9250",
"job": "flink_taskmanager"
},
"value": [
1748520442.282,
"1"
]
}
up=1 说明 Prometheus 最近一次 scrape 可以正常拉取 TaskManager 指标,但不代表 TaskManager 业务状态完全正常,也不代表作业一定正常运行。后面的告警规则就是围绕这些指标来判断异常。
Prometheus 告警状态可以简单分成三种:
| 状态 | 含义 |
|---|---|
| inactive | 表达式不成立 |
| pending | 表达式成立,但还没有持续超过 for |
| firing | 表达式成立,并且持续超过 for |
for 的作用就是过滤短暂抖动。需要注意,PromQL 告警表达式返回空结果时不会触发告警;只有返回的时间序列满足条件时,才会进入 pending 或 firing。如果一个表达式返回多个 instance,每个 instance 都会生成一条独立告警。
2. 增加 rule_files 配置
Prometheus 的告警规则一般单独放到规则文件里,然后在 prometheus.yml 中通过 rule_files 引入。
当前配置文件路径是:
/opt/module/prometheus-3.2.1/prometheus.yml
修改前先备份原配置:
cp /opt/module/prometheus-3.2.1/prometheus.yml \
/opt/module/prometheus-3.2.1/prometheus.yml.bak
只需要在原配置中新增 rule_files,不要删除已有 scrape_configs。下面是当前实验环境的完整示例配置,请根据自己已有配置合并,不要盲目覆盖:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "rules/*.yml"
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "node_exporter"
static_configs:
- targets: ["localhost:9100"]
- job_name: "pushgateway"
honor_labels: true
static_configs:
- targets: ["localhost:9091"]
- job_name: "flink_jobmanager"
static_configs:
- targets: ["localhost:9249"]
- job_name: "flink_taskmanager"
static_configs:
- targets: ["localhost:9250"]
这里有两个时间参数要注意:
| 参数 | 说明 |
|---|---|
| scrape_interval | Prometheus 抓取指标的间隔 |
| evaluation_interval | Prometheus 计算规则的间隔 |
如果 evaluation_interval 是 15s,规则表达式每 15 秒计算一次。规则里再配合 for: 30s、for: 1m 这样的持续时间,可以避免短暂网络抖动直接触发告警。
rules/.yml 是相对路径,相对于 prometheus.yml 所在目录解析。本文的配置文件在 /opt/module/prometheus-3.2.1/ 下,所以实际会匹配 /opt/module/prometheus-3.2.1/rules/.yml。规则文件后缀需要和 rule_files 中的匹配模式一致。
for: 30s 不一定精确等于 30 秒触发,实际会受到 evaluation_interval 影响,通常需要经过多次规则计算后才会从 pending 进入 firing。如果 evaluation_interval 比 scrape_interval 短,规则可能多次基于同一次采样值计算;如果比 scrape_interval 长,告警发现会更慢。
3. 编写 Flink 告警规则
创建规则目录:
mkdir -p /opt/module/prometheus-3.2.1/rules
创建规则文件:
vim /opt/module/prometheus-3.2.1/rules/flink-alerts.yml
当前文件目录结构如下:
/opt/module/prometheus-3.2.1/
prometheus.yml
rules/
flink-alerts.yml
内容如下:
groups:
- name: flink.rules
rules:
- alert: FlinkJobManagerDown
expr: up{job="flink_jobmanager"} == 0
for: 30s
labels:
severity: critical
service: flink
annotations:
summary: "Flink JobManager metrics endpoint is down"
description: "Prometheus cannot scrape Flink JobManager metrics. cluster={{ $labels.cluster }}, job={{ $labels.job }}, instance={{ $labels.instance }}."
- alert: FlinkTaskManagerDown
expr: up{job="flink_taskmanager"} == 0
for: 30s
labels:
severity: critical
service: flink
annotations:
summary: "Flink TaskManager metrics endpoint is down"
description: "Prometheus cannot scrape Flink TaskManager metrics. cluster={{ $labels.cluster }}, job={{ $labels.job }}, instance={{ $labels.instance }}."
- alert: FlinkTaskManagerEndpointFlapping
expr: changes(up{job="flink_taskmanager"}[10m]) > 1
labels:
severity: warning
service: flink
annotations:
summary: "Flink TaskManager metrics endpoint is flapping"
description: "Flink TaskManager scrape state changed more than once in 10 minutes. cluster={{ $labels.cluster }}, job={{ $labels.job }}, instance={{ $labels.instance }}, value={{ $value }}."
- alert: FlinkTaskManagerMissing
expr: flink_jobmanager_numRegisteredTaskManagers < 1
for: 1m
labels:
severity: warning
service: flink
annotations:
summary: "Flink registered TaskManager count is too low"
description: "Current registered TaskManager count is {{ $value }}. cluster={{ $labels.cluster }}, job={{ $labels.job }}, instance={{ $labels.instance }}."
- alert: FlinkNoRunningJobs
expr: flink_jobmanager_numRunningJobs < 1
for: 2m
labels:
severity: warning
service: flink
annotations:
summary: "No running Flink jobs"
description: "No running Flink jobs were observed in the current cluster. cluster={{ $labels.cluster }}, job={{ $labels.job }}, instance={{ $labels.instance }}."
这几个规则分别对应不同层面的异常。
| 告警 | 表达式 | 说明 |
|---|---|---|
| FlinkJobManagerDown | up{job=”flink_jobmanager”} == 0 | JobManager Reporter 不可采集 |
| FlinkTaskManagerDown | up{job=”flink_taskmanager”} == 0 | TaskManager Reporter 不可采集 |
| FlinkTaskManagerEndpointFlapping | changes(up{job=”flink_taskmanager”}[10m]) > 1 | TaskManager Reporter 采集状态在 10 分钟内发生多次变化 |
| FlinkTaskManagerMissing | flink_jobmanager_numRegisteredTaskManagers < 1 | JobManager 视角下没有可用 TaskManager |
| FlinkNoRunningJobs | flink_jobmanager_numRunningJobs < 1 | 当前没有运行中的作业 |
这里要特别注意 FlinkNoRunningJobs。这个规则只适合有长期运行作业的集群,比如常驻的流式作业。如果当前 Flink 集群只是临时提交批任务,或者本来就允许没有运行作业,就不应该直接使用这条规则。多集群环境中应该加 cluster、env、job_type 等标签过滤,例如:
flink_jobmanager_numRunningJobs{job_type="streaming"} < 1
FlinkTaskManagerMissing 也只适合当前实验环境这种“期望至少 1 个 TaskManager”的场景。生产环境应该根据集群规模设置阈值,比如 < 3,或者结合 cluster 标签分别设置不同阈值。它依赖 JobManager Reporter 正常暴露指标,因此应和 FlinkJobManagerDown 一起看。
FlinkJobManagerDown 和 FlinkTaskManagerDown 只能检测已经存在于 Prometheus target 列表中的 Reporter 不可抓取。如果 scrape_configs 没有配置该 target,或者 job 标签写错,up == 0 不会触发。这类规则不能替代 target 缺失检查。可以用 absent(up{job=”flink_taskmanager”}) 发现完全没有对应序列的情况,但动态环境下容易误报,需要谨慎使用。
FlinkTaskManagerEndpointFlapping 看的是一段时间内 up 状态是否反复变化,它更偏向检测采集抖动或端点不稳定,不一定能精确代表 TaskManager 进程重启。网络抖动、Reporter 卡顿、Prometheus 采集失败也可能造成 up 变化。不配置 for 时,表达式一旦为 true 就会直接 firing。
生产环境建议在 scrape_configs 中给 Flink targets 增加 cluster/env 标签,并在告警 labels 或 annotations 中带上这些标签,方便定位是哪个集群触发。severity 也要根据集群规模和业务重要性调整,本文只是实验示例。labels 会决定告警身份和 Alertmanager 分组方式,生产环境中 alertname、cluster、instance、severity 等标签设计要稳定;annotations 主要用于展示描述信息,不参与告警身份判断,不建议把频繁变化的值放到 labels 中。
4. 校验规则文件
Prometheus 自带 promtool,可以先检查规则文件语法:
/opt/module/prometheus-3.2.1/promtool check rules \
/opt/module/prometheus-3.2.1/rules/flink-alerts.yml
输出如下:
Checking /opt/module/prometheus-3.2.1/rules/flink-alerts.yml
SUCCESS: 5 rules found
每次修改规则后都应该先 check rules,再 check config,确认规则文件语法正确,并且已经被 prometheus.yml 正确引用。再检查整体配置:
/opt/module/prometheus-3.2.1/promtool check config \
/opt/module/prometheus-3.2.1/prometheus.yml
输出:
Checking /opt/module/prometheus-3.2.1/prometheus.yml
SUCCESS: 1 rule files found
SUCCESS: /opt/module/prometheus-3.2.1/prometheus.yml is valid prometheus config file syntax
规则文件修改后,Prometheus 不会自动加载,需要 reload 或重启。Prometheus 如果启动时加了 –web.enable-lifecycle,可以执行:
curl -X POST http://localhost:9090/-/reload
本文前面没有开启 lifecycle,所以这里使用重启方式。实验环境可以直接重启;生产环境建议使用 systemd 管理 Prometheus,并考虑 reload 或滚动方式,避免影响监控采集。
cd /opt/module/prometheus-3.2.1
pkill -f "/opt/module/prometheus-3.2.1/prometheus"
nohup ./prometheus \
--config.file=/opt/module/prometheus-3.2.1/prometheus.yml \
--storage.tsdb.path=/opt/module/prometheus-3.2.1/data \
--web.listen-address=0.0.0.0:9090 \
> prometheus.log 2>&1 &
Prometheus 默认没有登录认证,0.0.0.0:9090 不建议直接暴露到公网。生产环境建议绑定内网地址,或通过反向代理、VPN、网关认证访问。启动后确认服务已经 ready:
curl -s http://localhost:9090/-/ready
tail -n 50 /opt/module/prometheus-3.2.1/prometheus.log
打开 Prometheus Web UI 中的 Rules / Rule health 页面,可以看到 flink.rules 规则组已经加载:
规则状态为 OK,说明 Prometheus 能正常解析并计算这些表达式。
如果某条告警不触发,可以先把 expr 复制到 Prometheus Graph 查询页,确认表达式是否有返回值。返回空结果和返回 0 是两回事:空结果不会触发告警。
5. 模拟 TaskManager 异常
为了验证告警是否真的能触发,这里手动停止 TaskManager:
cd /opt/module/flink-1.20.1
jps | grep TaskManagerRunner
bin/taskmanager.sh stop
本文只是在 Standalone 实验环境中模拟异常;生产环境或 YARN/Kubernetes 模式不要随意手动停止 TaskManager,应使用测试环境验证告警。
停止前,TaskManager 采集状态为 1:
curl -G -s 'http://localhost:9090/api/v1/query' \
--data-urlencode 'query=up{job="flink_taskmanager"}'
返回。下面仍然只展示 Prometheus API result 中的关键部分:
{
"metric": {
"__name__": "up",
"instance": "localhost:9250",
"job": "flink_taskmanager"
},
"value": [
1748520442.282,
"1"
]
}
停止 TaskManager 后,等待两个采集周期再查询。当前 scrape_interval 是 15s,两个采集周期大约是 30 秒;告警进入 firing 还会受到 evaluation_interval 和 for 影响:
curl -G -s 'http://localhost:9090/api/v1/query' \
--data-urlencode 'query=up{job="flink_taskmanager"}'
返回:
{
"metric": {
"__name__": "up",
"instance": "localhost:9250",
"job": "flink_taskmanager"
},
"value": [
1748520519.934,
"0"
]
}
此时 up=0,说明 Prometheus 已经无法采集 TaskManager 的 /metrics 端口。
继续查看当前告警:
curl -s http://localhost:9090/api/v1/alerts | jq
如果系统没有 jq,可以去掉 | jq。下面只截取 alerts 数组中的关键字段。实际从异常到 firing 的时间取决于 scrape_interval、evaluation_interval 和 for,FlinkTaskManagerMissing 可能比 FlinkTaskManagerDown 更晚进入 firing:
{
"labels": {
"alertname": "FlinkTaskManagerDown",
"instance": "localhost:9250",
"job": "flink_taskmanager",
"service": "flink",
"severity": "critical"
},
"state": "firing",
"value": "0e+00"
}
{
"labels": {
"alertname": "FlinkTaskManagerMissing",
"instance": "localhost:9249",
"job": "flink_jobmanager",
"service": "flink",
"severity": "warning"
},
"state": "firing",
"value": "0e+00"
}
Prometheus 页面里也能看到对应告警:
截图里还能看到 FlinkNoRunningJobs,这是因为当前实验环境没有提交长期运行的 Flink 作业,flink_jobmanager_numRunningJobs 本来就是 0。为了避免误以为配置错误,实验验证前也可以先临时注释掉 FlinkNoRunningJobs,或者把它作为可选规则。如果生产集群里允许某些时间段没有运行作业,这条规则就应该去掉,或者增加 cluster、job_type 这类标签后只对流式作业集群启用。
这里有一个细节:FlinkTaskManagerDown 是 Prometheus 从采集侧看到的异常,表示 localhost:9250 不可采集,可能是进程挂了、端口不通、网络问题或 Reporter 异常;FlinkTaskManagerMissing 是 JobManager 指标里看到的异常,表示当前注册的 TaskManager 数量为 0,偏 Flink 集群内部状态。两个规则看的是同一类问题,但角度不同。
如果 FlinkTaskManagerDown 触发但 FlinkTaskManagerMissing 没触发,可能是 Reporter 端口或网络采集问题;如果两个都触发,更可能是 TaskManager 真的退出或无法注册到 JobManager。实际排查时,这两个告警如果同时出现,基本可以优先检查 TaskManager 进程、端口、日志和机器状态。
6. 恢复 TaskManager
验证完成后,重新启动 TaskManager:
cd /opt/module/flink-1.20.1
bin/taskmanager.sh start
再次查看 Flink Overview:
curl -s http://localhost:8081/overview
返回:
{
"taskmanagers": 1,
"slots-total": 1,
"slots-available": 1,
"jobs-running": 0,
"jobs-finished": 0,
"jobs-cancelled": 0,
"jobs-failed": 0,
"flink-version": "1.20.1",
"flink-commit": "cb1e7b5"
}
再确认 Prometheus 采集和告警状态:
curl -G -s 'http://localhost:9090/api/v1/query' \
--data-urlencode 'query=up{job="flink_taskmanager"}'
curl -s http://localhost:9090/api/v1/alerts | jq
TaskManager 恢复后,仍需等待 scrape_interval + evaluation_interval,告警不会立即消失。FlinkTaskManagerDown 和 FlinkTaskManagerMissing 会在后续规则计算中恢复为 inactive。Prometheus 页面中的 Alerts 状态会自动更新;如果已经接入 Alertmanager,通知恢复还取决于 Alertmanager 的 resolve 逻辑和通知配置。
7. 关于告警发送
这一篇只配置了 Prometheus 规则,所以告警可以在 Prometheus 页面上看到 pending 和 firing 状态。
如果要真正发到外部系统,还需要接 Alertmanager。Prometheus 负责判断规则是否触发,Alertmanager 负责后续的分组、静默、抑制和通知发送。
一个最小的 Prometheus 告警发送配置大致如下:
alerting:
alertmanagers:
- static_configs:
- targets:
- "localhost:9093"
这段配置需要放在 prometheus.yml 顶层,不能放在 scrape_configs 下面。它只是 Prometheus 发送告警到 Alertmanager 的配置,前提是 Alertmanager 已经单独安装并监听在 9093。
也就是说,Prometheus 告警链路可以拆成两段:
| 阶段 | 组件 | 作用 |
|---|---|---|
| 规则判断 | Prometheus | 定期计算 PromQL,产生 pending/firing |
| 告警发送 | Alertmanager | 接收告警,分组、静默、抑制、发送通知 |
Prometheus 只负责产生告警状态;Alertmanager 不重新计算 PromQL,只负责接收、分组、静默、抑制和发送通知。如果当前只是先验证规则是否正确,可以先不接 Alertmanager;如果已经要用于生产,就应该把 Alertmanager 和通知通道一起补上,同时配置告警分组、重复发送间隔、静默规则、抑制规则和通知接收人,避免告警风暴。
8. 规则设计建议
Flink 告警规则可以按排查链路逐层设计:
- 先从 target 是否 UP 开始,确认 Prometheus 能不能抓到 Reporter。
- 再看 JobManager 视角下的 TaskManager 数量,确认 Flink 集群内部状态。
- 再看作业数量、Checkpoint、失败重启、延迟、吞吐、反压、GC 和内存等业务指标。
- 每条规则都要明确适用场景和误报条件,尤其是空闲集群、批任务集群和动态扩缩容环境。
本文只覆盖基础链路告警,还没有覆盖作业失败、Checkpoint 失败、反压、延迟、吞吐、GC、内存等更深入的 Flink 作业级告警。规则多起来后,也可以按类型拆成多个文件,例如 flink-target-alerts.yml、flink-job-alerts.yml、flink-resource-alerts.yml,便于维护。
9. 小结
这一篇把 Flink 的几个基础异常写成了 Prometheus 告警规则,并通过停止 TaskManager 验证了告警能够真实触发。
到这里,Prometheus 监控 Flink 的基础链路已经完整了:Prometheus 采集指标,PromQL 查询指标,Grafana 展示看板,Prometheus 规则负责发现异常。后续如果要继续完善,就可以把规则拆得更细,例如按集群、作业、队列、环境打标签,再接入 Alertmanager 做通知分发。