ELK/EFK 监控告警体系搭建:从 ElastAlert 到 Watcher
ELK/EFK 监控告警体系搭建:从 ElastAlert 到 Watcher
在 ELK/EFK 日志系统中,告警(Alerting)是日志价值的最终体现——仅在 Kibana 中查询日志是不够的,当异常发生时,系统需要主动通知运维人员。本文深入对比主流告警方案,详解 ElastAlert 和 Elasticsearch Watcher 的配置与实践。
一、日志告警的三大方式对比
目前主流的 ELK 告警方案有三种,各有优劣:
| 方案 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Elasticsearch Watcher | X-Pack 内置,DSL 查询 + 条件触发 | 原生集成、零依赖、UI 配置 | 商业许可、价格昂贵 | 企业级生产环境(预算充足) |
| ElastAlert | Python 框架,定时查询 ES 并匹配规则 | 开源免费、规则丰富、灵活 | 需单独部署、维护成本 | 中小团队、开源优先 |
| Grafana + Alertmanager | Grafana 告警引擎 + Prometheus Alertmanager | 统一监控告警、可视化强 | 需额外维护 Grafana | 已有 Grafana 的团队 |
本文重点讲解 ElastAlert 和 Watcher 两种方案。
二、Elasticsearch Watcher(X-Pack 商业版)
Elasticsearch Watcher 是 X-Pack 提供的原生告警组件,从 7.x 起作为 Elastic Stack 的一部分提供(需要 Platinum 或 Enterprise 许可)。
2.1 Watcher 核心概念
- Watch:告警规则定义,包含 trigger、input、condition、actions
- Trigger:触发时机(定时、周期性)
- Input:从 ES 加载数据(查询结果)
- Condition:判断是否触发告警
- Actions:触发后的动作(邮件、Slack、Webhook、日志等)
2.2 Watcher 完整配置示例
以下 Watch 示例:每分钟检查过去 5 分钟内 Nginx 错误日志数量是否超过阈值:
PUT _watcher/watch/nginx-5xx-alert
{
"trigger": {
"schedule": {
"interval": "1m"
}
},
"input": {
"search": {
"request": {
"indices": ["nginx-logs-*"],
"body": {
"size": 0,
"query": {
"bool": {
"filter": [
{ "range": { "@timestamp": { "gte": "now-5m" } } },
{ "prefix": { "status": "5" } }
]
}
}
}
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total.value": {
"gte": 50
}
}
},
"actions": {
"send_email": {
"email": {
"to": ["ops@example.com"],
"subject": "Nginx 5xx 告警({{ctx.watch_id}})",
"body": "过去 5 分钟检测到 {{ctx.payload.hits.total.value}} 个 5xx 错误"
}
},
"webhook_notify": {
"webhook": {
"method": "POST",
"host": "hooks.example.com",
"port": 443,
"path": "/alerts/nginx",
"body": "{\"message\":\"Nginx 5xx alert\",\"count\":{{ctx.payload.hits.total.value}}}"
}
}
}
}
2.3 Watcher Schedule 配置
Watcher 支持多种调度方式:
- interval:固定间隔,如
"interval": "5m" - cron:Cron 表达式,如
"cron": "0 0/5 * * * ?" - hourly:每小时指定分钟触发
- daily:每天指定时间触发
Watcher 还可以设置 throttle_period 避免重复告警:
"throttle_period": "10m" // 同一 Watch 10 分钟内不会重复触发
三、ElastAlert(开源告警框架)
ElastAlert 是 Yelp 开源的 Python 告警框架,定时查询 Elasticsearch 数据,匹配规则后发送告警。兼容 ES 5.x~8.x。
3.1 安装配置
方式一:Docker 部署(推荐)
# docker-compose.yml
version: '3'
services:
elastalert:
image: jertel/elastalert-docker:latest
volumes:
- ./config:/opt/elastalert/config
- ./rules:/opt/elastalert/rules
environment:
- ELASTICSEARCH_HOST=192.168.1.100
- ELASTICSEARCH_PORT=9200
restart: always
方式二:源码安装
# 安装 ElastAlert
pip install elastalert
# 创建配置文件 config.yaml
cat > config.yaml << 'EOF'
es_host: 192.168.1.100
es_port: 9200
use_ssl: False
writeback_index: elastalert_status
alert_time_limit: 2 days
run_every:
minutes: 1
buffer_time:
minutes: 15
EOF
3.2 规则类型详解
ElastAlert 支持多种内建规则类型,常见的有:
- frequency:在指定时间内某事件出现 N 次则告警
- spike:事件数量在时间窗口内激增(上升/下降)
- flatline:事件数量低于阈值(静默检测)
- any:每次匹配都告警
- blacklist:匹配黑名单字段
- whitelist:不在白名单中的事件告警
- change:字段值变化告警
- new_term:出现新字段或新值告警
- percentage_match:匹配比例超过阈值告警
四、告警通知方式
ElastAlert 支持多种告警通知渠道,配置灵活。
4.1 邮件告警
# config.yaml 中配置 SMTP
smtp_host: smtp.example.com
smtp_port: 587
smtp_ssl: true
smtp_auth_file: /opt/elastalert/config/smtp_auth.yaml
email_reply_to: alerts@example.com
from_addr: elastalert@example.com
# 规则中指定
alert:
- "email"
email:
- "ops@example.com"
- "oncall@example.com"
4.2 Slack 通知
alert:
- "slack"
slack_webhook_url: "https://hooks.slack.com/services/T000/B000/XXXX"
slack_channel: "#alerts"
slack_username: "ElastAlert"
slack_msg_color: "danger"
4.3 飞书(Lark)机器人
飞书通过 Webhook 机器人接收告警,需配置自定义告警脚本或使用 Alerta 中转。推荐直接在规则中用 Webhook 类型发往飞书:
alert:
- "webhook"
webhook_url: "https://open.feishu.cn/open-apis/bot/v2/hook/xxxx-xxxx"
webhook_body: |
{
"msg_type": "interactive",
"card": {
"header": {
"title": { "tag": "plain_text", "content": "🚨 ELK 告警通知" }
},
"elements": [
{ "tag": "div", "text": { "tag": "lark_md", "content": "**规则**: {{rule}}\n**时间**: {{@timestamp}}\n**匹配数**: {{match_count}}" } }
]
}
}
4.4 通用 Webhook
alert:
- "webhook"
webhook_url: "https://alert-api.example.com/webhook"
webhook_body: '{"alert_rule": "{{rule}}", "hits": "{{matches}}", "timestamp": "{{@timestamp}}"}'
webhook_headers:
X-API-Key: "your-api-key"
五、ElastAlert 规则示例
5.1 错误率激增告警(spike)
监控应用日志中 ERROR 级别数量是否在短时间激增:
# rules/error_spike.yaml
name: "ERROR Spike Detection"
type: spike
index: app-logs-*
use_terms_query: false
# 参考时间段 vs 当前时间段
spike_height: 3 # 超过参考值 3 倍
spike_type: "up" # 上升
timeframe:
minutes: 5
# 参考时间窗口
buffer_time:
minutes: 30
# ES 查询过滤
filter:
- query:
query_string:
query: "level:ERROR"
alert:
- "slack"
- "email"
slack_webhook_url: "https://hooks.slack.com/services/xxx/xxx/xxx"
email:
- "ops@example.com"
realert:
minutes: 30 # 30分钟内不重复告警
5.2 日志关键字匹配告警(any / frequency)
当关键字 OOM(Out Of Memory)或 NullPointerException 出现时立即告警:
# rules/oom_keyword.yaml
name: "OOM Detection"
type: frequency
index: app-logs-*
num_events: 1 # 出现 1 次即告警
timeframe:
minutes: 1
filter:
- query:
query_string:
query: "message:(OOM OR 'OutOfMemoryError' OR 'Cannot allocate memory')"
alert:
- "slack"
- "email"
realert:
minutes: 60 # 同规则 1 小时内不重复
5.3 日志静默检测(flatline)
监控日志是否突然消失(可能服务挂了):
# rules/log_flatline.yaml
name: "Log Flatline Detection"
type: flatline
index: app-logs-*
threshold: 10 # 时间窗口内日志少于 10 条则告警
timeframe:
minutes: 15
filter:
- query:
query_string:
query: "level:(ERROR OR WARN)"
alert:
- "slack"
- "email"
realert:
minutes: 15
六、告警去重和抑制策略
避免告警风暴(Alert Storm)是生产环境的必修课。以下是常用策略:
6.1 ElastAlert 的去重机制
- realert:规则级别去重,同一规则在指定时间内不重复发送
- aggregation:将告警聚合后批量发送
- alert_subject:根据特定字段分组合并
# 聚合告警配置示例
realert:
minutes: 10 # 10 分钟内不重复
aggregation:
schedule:
minutes: 2 # 每 2 分钟聚合一次
alert_subject: "{{rule}} - {{service}}" # 按服务分组
6.2 Watcher 的节流(Throttle)
- throttle_period:Watch 级别静默周期
- condition 中使用脚本逻辑进行高级判断
- Ack(确认)机制:运维人员确认后停止告警
"throttle_period": "1h",
"actions": {
"acknowledge": {
"ack": {}
},
"send_email": {
"email": {
"to": ["ops@example.com"],
"subject": "已确认告警: {{ctx.watch_id}}"
},
"condition": {
"always": {}
}
}
}
6.3 全局抑制策略
- 维护窗口:在已知维护期暂停告警
- 依赖告警:如果上游服务已告警,下游不重复告警
- 分级告警:P0 立即通知,P1 延迟 5 分钟,P2 汇总日报
七、实战案例
7.1 Nginx 5xx 错误告警
场景:Nginx 上游服务故障时,5xx 错误飙升,需要立即通知。
ElastAlert 规则:
# rules/nginx_5xx.yaml
name: "Nginx 5xx Alert"
type: frequency
index: nginx-logs-*
num_events: 50
timeframe:
minutes: 5
filter:
- query:
query_string:
query: "status:[500 TO 599]"
query_key: "hostname"
alert:
- "slack"
- "email"
slack_webhook_url: "https://hooks.slack.com/services/xxx/xxx/xxx"
slack_channel: "#alerts-urgent"
email:
- "oncall@example.com"
realert:
minutes: 15
Kibana 对应查询:
GET nginx-logs-*/_search
{
"query": {
"bool": {
"filter": [
{ "range": { "@timestamp": { "gte": "now-5m" } } },
{ "terms": { "status": [500,502,503,504] } }
]
}
},
"aggs": {
"by_host": {
"terms": { "field": "hostname.keyword" }
}
}
}
7.2 应用异常检测
场景:Java 应用频繁抛出 Connection Timeout 或 Database Down 异常。
ElastAlert 规则:
# rules/app_exception.yaml
name: "Application Exception Alert"
type: spike
index: app-logs-*
spike_height: 5
spike_type: "up"
timeframe:
minutes: 5
filter:
- query:
query_string:
query: "message:(ConnectionTimeout OR 'ConnectException' OR 'CannotGetJdbcConnection' OR 'TLS handshake failed')"
query_key: "service"
alert:
- "webhook"
webhook_url: "https://open.feishu.cn/open-apis/bot/v2/hook/xxxx"
webhook_body: |
{
"msg_type": "interactive",
"card": {
"header": {
"title": { "tag": "plain_text", "content": "🚨 {{service}} 异常告警" }
},
"elements": [
{ "tag": "div", "text": { "tag": "lark_md", "content": "**环境**: {{environment}}\n**异常类型**: Connection/DB 异常\n**过去 5 分钟异常数**: {{match_count}}\n**对比基数**: {{spike_ref_count}}" } }
]
}
}
realert:
minutes: 30
7.3 基于 Watcher 的异常检测
如果使用 X-Pack,可以用 Watcher + Painless 脚本实现更灵活的逻辑:
PUT _watcher/watch/error-rate-watch
{
"trigger": { "schedule": { "interval": "5m" } },
"input": {
"search": {
"request": {
"indices": ["app-logs-*"],
"body": {
"size": 0,
"query": {
"bool": {
"filter": [
{ "range": { "@timestamp": { "gte": "now-5m" } } }
]
}
},
"aggs": {
"total": { "value_count": { "field": "@timestamp" } },
"errors": {
"filter": { "term": { "level": "ERROR" } }
}
}
}
}
}
},
"condition": {
"script": {
"source": """
def total = ctx.payload.aggregations.total.value;
def errors = ctx.payload.aggregations.errors.doc_count;
if (total == 0) return false;
def rate = (double) errors / total * 100;
ctx.error_rate = rate;
return rate > 10;
"""
}
},
"actions": {
"slack_notify": {
"slack": {
"message": {
"text": "⚠️ 错误率异常: {{ctx.error_rate}}% (错误 {{ctx.payload.aggregations.errors.doc_count}}/总计 {{ctx.payload.aggregations.total.value}})"
}
}
}
},
"throttle_period": "15m"
}
八、总结与建议
- 预算充足:推荐使用 Elasticsearch Watcher,原生集成、零维护、功能完整
- 开源优先:ElastAlert 是完全够用的方案,规则灵活、社区活跃
- 统一监控:如果已有 Grafana,优先使用 Grafana 告警引擎
- 避免告警疲劳:务必配置去重、抑制、分级策略
- 告警可观测:告警本身也应有日志和监控,防止告警系统静默失效
无论选择哪种方案,核心原则不变:只对真正需要人工介入的事件告警,减少噪音才能提升响应速度。





