上一篇解决了 Dashboard 的可视化组合逻辑。这一篇进入 Alerting 框架,核心问题是:Rule 定期执行、判定、触发 Action,Task Manager 怎么调度,告警状态怎么持久化。

Alerting 框架总览

Kibana Alerting(7.7 引入,8.x 重命名为 Kibana Alerting framework)把告警拆成三个可独立替换的契约:

1
2
3
4
5
6
7
Rule                  Connector              Action
────────────────── ──────────────────── ────────────────────
定义条件 + 调度周期 定义外部系统连接参数 定义向该系统发送的
(what/when to check) (where to send) 消息模板
│ │ │
└──────── Alert Instance 触发后 ──────────────┘
ActionExecution

三者在 .kibana 索引中各自存为 Saved Object,类型分别是 alert(rule)、action(connector)、规则内嵌的 action 引用。这种分离意味着同一个 Slack connector 可以被数十个 Rule 复用,修改 Slack token 只需改一处。

Task Manager 调度模型

Rule 的执行不由 cron 守护进程驱动,而是由 Kibana 内置的 Task Manager 调度。

1
2
3
4
5
6
7
8
9
10
11
12
Kibana 实例 A                Kibana 实例 B
┌──────────────────────┐ ┌──────────────────────┐
│ Task Manager worker │ │ Task Manager worker │
│ poll .kibana_task_ │ │ poll .kibana_task_ │
│ _manager index │ │ _manager index
│ → claim (optimistic │ │ → claim (optimistic │
│ locking via ES │ │ locking via ES │
update + version) │ │ update + version) │
└──────────────────────┘ └──────────────────────┘
│ │
└──────── Elasticsearch ─────┘
.kibana_task_manager

每个 Rule 对应 .kibana_task_manager 中的一条 task 文档,字段 runAt 表示下次执行时间。Task Manager 以短轮询(默认 3 秒)从索引中 claim 到期任务,通过 Elasticsearch 的乐观锁(version check update)保证同一任务不会被两个 Kibana 实例同时执行。

Rule 执行后 Task Manager 更新 runAt = now + schedule_interval,形成循环。

Rule 的执行流程

1
2
3
4
5
6
7
8
9
10
Task Manager 触发 executeRule()


RuleExecutor.run()
├── fetchRuleState() ← 读取上次 alert instances
├── RuleType.executor() ← 实际检查逻辑(ES query / threshold)
│ └── return { state, alertsToSchedule }
├── diffAlertInstances() ← 新增 / 持续 / 恢复 对比
├── scheduleActions() ← 生成 ActionExecution 记录
└── saveRuleState() ← 写回 .kibana Saved Object

RuleType.executor() 是规则类型的扩展点,官方提供的类型包括:

  • metrics.alert.threshold:指标阈值告警
  • .es-query:自定义 ES 查询,结果行数超过阈值触发
  • logs.alert.document.count:日志计数告警
  • xpack.ml.anomaly_detection_alert:ML 异常检测告警

插件可以通过 alertingStart.getAlertsClient() 注册自定义 Rule Type。

Alert Instance 的状态机

1
2
3
4
5
6
7
8
(new)
first execution meets condition

ACTIVE ──────────── condition no longer met ──────────── RECOVERED
│ │
│ muted / snoozed │ re-triggers
▼ │
MUTED ──────────────────────────────────────────────────── ACTIVE

每个 Alert Instance 对应一个具体触发实体(例如某个 host.name 的值)。实例进入 ACTIVE 后,Action 根据 notifyWhen 策略决定是否发送通知:

  • onActionGroupChange:状态组变化时发送
  • onActiveAlert:每次执行时发送
  • onThrottleInterval:在节流间隔内最多一次

实例状态以 JSON 形式存在 alert Saved Object 的 executionStatusalertInstances 字段里。

Connector 类型

Connector 封装了外部系统的连接参数。8.x 内置连接器包括:

Connector Type 用途
.email SMTP 邮件
.slack Slack incoming webhook
.pagerduty PagerDuty Events API
.webhook 通用 HTTP POST
.jira Jira issue 创建
.servicenow ServiceNow incident
xpack.ibm_resilient IBM Resilient

Connector 的敏感参数(密码、token)通过 Kibana 的 xpack.encryptedSavedObjects.encryptionKey 加密后存入 Saved Object,读取时自动解密但不暴露给 API 返回值。

实验:通过 API 创建并触发 Threshold Rule

以下实验在 Kibana 8.x + Elasticsearch 8.x 环境进行。假设已有 filebeat-* 索引,且 Kibana 配置了加密 key。

第一步:创建 Webhook Connector

1
2
3
4
5
6
7
8
9
10
POST kbn:/api/actions/connector
{
"name": "local-test-webhook",
"connector_type_id": ".webhook",
"config": {
"url": "https://httpbin.org/post",
"method": "post"
},
"secrets": {}
}

记录返回的 id,例如 connector_id = "abc-123"

第二步:创建 ES Query Rule(每 1 分钟检查一次)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
POST kbn:/api/alerting/rule
{
"name": "test-es-query-rule",
"rule_type_id": ".es-query",
"schedule": { "interval": "1m" },
"params": {
"index": ["filebeat-*"],
"timeField": "@timestamp",
"esQuery": "{\"query\":{\"match_all\":{}}}",
"size": 100,
"threshold": [1],
"thresholdComparator": ">",
"timeWindowSize": 5,
"timeWindowUnit": "m"
},
"actions": [
{
"id": "abc-123",
"group": "query matched",
"params": {
"body": "Rule {{rule.name}} fired: {{context.value}} hits"
}
}
]
}

第三步:观察执行日志

1
GET kbn:/api/alerting/rule/<rule_id>/_execution_log?date_start=now-1h

返回的 execution_log 数组中每条记录包含 status(succeeded / failed)、duration_msschedule_delay_ms(任务被 claim 的延迟,用于衡量 Task Manager 压力)。

第四步:查看告警状态

1
GET kbn:/api/alerting/rule/<rule_id>

executionStatus.status 值可能为 okactiveerrorpendingalertInstances 字段(内部)可通过 Elasticsearch 直查 .kibana 索引获得:

1
2
3
4
5
6
7
8
9
10
11
GET .kibana/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "type": "alert" } },
{ "term": { "alert.name": "test-es-query-rule" } }
]
}
}
}

alert.alertInstances 字段记录每个实例的 statemeta.lastScheduledActions 等信息。

映射到内部对象

实验观测 内部对象
Rule 定期执行 .kibana_task_manager 中 type=alerting:.<rule_type> 的 task
执行日志 .kibana-event-log-* 索引中的 event 文档
告警状态 .kibana 中 type=alert 的 Saved Object
Connector 配置 .kibana 中 type=action 的 Saved Object(含加密字段)
Action 发送记录 .kibana-event-log-* 中 event.action=execute-action

模式提炼

Kibana Alerting 框架的三段契约本质是关注点分离:Rule 只负责"什么条件触发",Connector 只负责"向哪里发",Action 只负责"发什么内容"。这种设计让同一 Rule 可以并联多个 Action(例如同时发 Slack 和 PagerDuty),也让 Connector 复用跨越多个 Rule 而无需重复配置认证信息。

Task Manager 的乐观锁 claim 机制将调度状态转移到 Elasticsearch,天然支持多 Kibana 实例水平扩展,无需外部消息队列。

工程迁移表

场景 Kibana 8.x 做法
指标超阈值告警 Rule Type: metrics.alert.threshold,配合 metrics-* 索引
日志错误率告警 Rule Type: logs.alert.document.count
自定义复杂查询告警 Rule Type: .es-query,params.esQuery 传 JSON 字符串
告警发送到多渠道 在 Rule 的 actions 数组中添加多个不同 connector_id
防止告警风暴 action.frequency.throttle 设置节流间隔(例如 1h
静音特定实例 Rule._muted_alert_instances 数组,或 UI Mute Instance
批量暂停告警 Rule.enabled=false(停止 Task Manager 调度该 Rule)

常见误解

Rule 每次执行不意味着每次发送通知。notifyWhen: onActionGroupChange 模式下,只有 Alert Instance 的状态组(例如从 warning 变为 critical)才会触发 Action;实例持续 active 但组不变时不发送。混淆"Rule 执行"与"Action 触发"是告警被忽视或轰炸的常见原因。

.es-query Rule Type 的 esQuery 参数接受 JSON 字符串而非对象,直接传对象会导致参数校验失败,错误信息不够清晰。

加密 Saved Object 配置(xpack.encryptedSavedObjects.encryptionKey)未设置或在多 Kibana 实例间不一致时,Connector 会无法解密,表现为 action execute 报 Unable to decrypt attribute 错误。

Connector 的 secrets 字段在 GET 响应中永远为空对象,不能用 GET 返回值直接 PUT 回去更新,必须在 secrets 中重新填写完整值。

练习

  1. 创建一个 .es-query Rule,thresholdComparator 设为 between,观察 thresholdComparator between 需要 threshold 数组有两个元素的约束。
  2. 修改 Rule 的 schedule.interval30s,通过 .kibana_task_manager 索引查看 runAt 字段的更新频率。
  3. 在 Rule 触发后,调用 mute instance API,观察 .kibanaalert.mutedInstanceIds 字段的变化。
  4. 配置两个 Action(Webhook 和 email),分别属于不同的 action group(query matchedrecovered),验证 recovery 时只有 recovered group 的 Action 被触发。

系列导航

参考资料

  1. Elastic 官方文档,Kibana Alerting - https://www.elastic.co/guide/en/kibana/current/alerting-getting-started.html
  2. Elastic 官方文档,Alerting API Reference - https://www.elastic.co/guide/en/kibana/current/alerts-api.html
  3. Elastic 博客,“Kibana Alerting: How it Works” - https://www.elastic.co/blog/kibana-alerting-how-it-works
  4. Kibana 源码,x-pack/plugins/alerting/server/task_runner/ - https://github.com/elastic/kibana