ELK/EFK 监控告警体系搭建:从 ElastAlert 到 Watcher

ELK/EFK 监控告警体系搭建:从 ElastAlert 到 Watcher

在 ELK/EFK 日志系统中,告警(Alerting)是日志价值的最终体现——仅在 Kibana 中查询日志是不够的,当异常发生时,系统需要主动通知运维人员。本文深入对比主流告警方案,详解 ElastAlert 和 Elasticsearch Watcher 的配置与实践。

一、日志告警的三大方式对比

目前主流的 ELK 告警方案有三种,各有优劣:

方案原理优点缺点适用场景
Elasticsearch WatcherX-Pack 内置,DSL 查询 + 条件触发原生集成、零依赖、UI 配置商业许可、价格昂贵企业级生产环境(预算充足)
ElastAlertPython 框架,定时查询 ES 并匹配规则开源免费、规则丰富、灵活需单独部署、维护成本中小团队、开源优先
Grafana + AlertmanagerGrafana 告警引擎 + Prometheus Alertmanager统一监控告警、可视化强需额外维护 Grafana已有 Grafana 的团队

本文重点讲解 ElastAlertWatcher 两种方案。

二、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 TimeoutDatabase 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 告警引擎
  • 避免告警疲劳:务必配置去重、抑制、分级策略
  • 告警可观测:告警本身也应有日志和监控,防止告警系统静默失效

无论选择哪种方案,核心原则不变:只对真正需要人工介入的事件告警,减少噪音才能提升响应速度

发表回复

后才能评论