完善EKF可观测性体系:基于ElastAlert2构建k8s智能钉钉日志告警系统
ElastAlert 钉钉告警部署文档

📋 目录
- 系统概述
- 架构说明
- 前置要求
- 部署步骤
- 配置说明
- 告警规则
- 故障排查
- 维护指南
系统概述
功能描述
ElastAlert 钉钉告警系统用于监控 Elasticsearch 中的日志数据,当检测到ERROR级别日志时自动发送钉钉告警通知。
技术栈
- ElastAlert2: latest (基于Python 3.14)
- Elasticsearch: 8.x
- Docker: Compose v2
- 告警通道: 钉钉自定义机器人(加签方式)
核心特性
- ✅ 实时监控filebeat日志索引
- ✅ 支持Data Stream格式索引
- ✅ 自定义过滤条件(按命名空间、容器筛选)
- ✅ 智能消息截取(只显示错误第一行)
- ✅ 防重复告警机制(realert)
- ✅ 钉钉加签认证
架构说明
系统架构图
┌─────────────────┐
│ Filebeat Pods │
│ (K8s Cluster) │
└────────┬────────┘│ 采集日志↓
┌─────────────────┐
│ Elasticsearch │
│ <your ip> │
│ Port: 9200 │
└────────┬────────┘│ 查询监控↓
┌─────────────────┐
│ ElastAlert2 │
│ (Docker容器) │
└────────┬────────┘│ 发送告警↓
┌─────────────────┐
│ 钉钉机器人 │
│ (加签认证) │
└─────────────────┘
目录结构
Kibana日志告警/
├── docker-compose.yml # Docker编排配置
├── elastalert-config.yaml # ElastAlert主配置
├── rules/ # 告警规则目录
│ ├── error-log-alert.yaml # 高频错误告警规则
│ └── high-frequency-error-alert.yaml # 高频错误告警规则
├── enhancements/ # 增强脚本目录
│ ├── __init__.py # Python包初始化
│ └── truncate_message.py # 消息截取脚本
├── logs/ # ElastAlert日志目录
├── certs/ # ES证书目录
│ └── http_ca.crt # ES CA证书(可选)
└── test_es.sh # ES连接测试脚本
前置要求
环境要求
- 操作系统: Linux (CentOS/Ubuntu)
- Docker: 20.10+
- Docker Compose: v2.0+
- 网络: 能访问 Elasticsearch 服务器
Elasticsearch要求
- 版本: 8.x
- 索引: filebeat-* 或 .ds-filebeat-* (Data Stream)
钉钉机器人要求
- 自定义机器人
- 安全方式: 加签认证
- 需要: access_token 和 secret
部署步骤
步骤1:准备项目目录
# 创建项目目录
mkdir -p /usr/local/elastalert
cd /usr/local/elastalert# 创建必要的子目录
mkdir -p logs rules certs enhancements
步骤2:创建配置文件
2.1 创建 docker-compose.yml
version: '3.8'services:elastalert:image: jertel/elastalert2:latestcontainer_name: elastalert-dingtalkrestart: unless-stoppeduser: rootvolumes:- ./elastalert-config.yaml:/opt/elastalert/config.yaml:ro- ./rules:/opt/elastalert/rules:ro- ./enhancements:/opt/elastalert/enhancements:ro- ./logs:/opt/elastalert/logs- ./certs:/opt/elastalert/certs- elastalert-data:/opt/elastalert/dataenvironment:- TZ=Asia/Shanghai- ELASTALERT_CONFIG=/opt/elastalert/config.yamlnetwork_mode: "host"healthcheck:test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:3030')"]interval: 30stimeout: 10sretries: 3start_period: 40svolumes:elastalert-data:driver: local
2.2 创建 elastalert-config.yaml
# ElastAlert 配置文件# 规则文件夹
rules_folder: /opt/elastalert/rules# 运行频率
run_every:minutes: 1# 缓冲区时间
buffer_time:minutes: 15# Elasticsearch 连接配置
es_host: <your ip>
es_port: 9200
es_username: elastic
es_password: <your pass># SSL配置
use_ssl: true
verify_certs: false
ssl_show_warn: false# ES 8.x 兼容性配置
es_send_get_body_as: GET
es_conn_timeout: 120# 写入索引
writeback_index: elastalert_status# 告警时间限制
alert_time_limit:days: 2# 最大查询大小
max_query_size: 10000# 日志配置
logging:version: 1disable_existing_loggers: falseformatters:standard:format: '%(asctime)s [%(levelname)s] %(name)s: %(message)s'handlers:console:class: logging.StreamHandlerlevel: INFOformatter: standardstream: ext://sys.stdoutfile:class: logging.handlers.RotatingFileHandlerlevel: INFOformatter: standardfilename: /opt/elastalert/logs/elastalert.logmaxBytes: 10485760backupCount: 5loggers:elastalert:level: INFOhandlers: [console, file]propagate: falseelasticsearch:level: WARNINGhandlers: [console, file]propagate: falseroot:level: INFOhandlers: [console, file]# 钉钉配置(全局默认)
dingtalk_webhook_url: "https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN"
dingtalk_sign: "YOUR_SECRET"
dingtalk_msgtype: "text"
⚠️ 注意: 将 YOUR_ACCESS_TOKEN 和 YOUR_SECRET 替换为实际的钉钉机器人配置。
2.3 创建增强脚本
enhancements/init.py
# ElastAlert Enhancements Package
enhancements/truncate_message.py
"""
ElastAlert Match Enhancement - 截取message第一行
只保留错误信息的第一行或前200个字符
"""from elastalert.enhancements import BaseEnhancementclass TruncateMessageEnhancement(BaseEnhancement):"""截取message字段的第一行"""def process(self, match):"""处理匹配的文档,截取message字段"""if 'message' in match:message = match['message']# 获取第一行(以换行符分割)first_line = message.split('\n')[0]# 限制长度为200字符if len(first_line) > 200:first_line = first_line[:200] + '...'# 添加新字段存储截取后的messagematch['message_summary'] = first_linereturn match
2.4 创建告警规则
rules/error-log-alert.yaml (系统错误日志告警)
# 错误日志钉钉告警规则
# 监控ERROR级别日志并发送钉钉通知name: "ERROR日志告警"
type: frequency
index: .ds-filebeat-*,filebeat-*,.ds-filebeat-latency-*,filebeat-latency-*
num_events: 1
timeframe:minutes: 1# 过滤条件:只监控default命名空间的ERROR日志
filter:
- bool:must:- match:message: "ERROR"- term:namespace: "default"must_not:- term:container_name: "uni-log"# 使用增强脚本截取message第一行
match_enhancements:- "enhancements.truncate_message.TruncateMessageEnhancement"# 钉钉告警配置
alert:
- "dingtalk"# 钉钉机器人配置(加签方式)
dingtalk_webhook: "https://oapi.dingtalk.com/robot/send"
dingtalk_access_token: "YOUR_ACCESS_TOKEN"
dingtalk_sign: "YOUR_SECRET"
dingtalk_msgtype: "markdown"# 告警标题
alert_subject: "🚨 错误日志告警 - {0}"
alert_subject_args:
- "namespace"# 告警内容模板
alert_text_type: alert_text_only
alert_text: |## 🚨 系统错误日志告警**时间**: {0}**来源信息**:- 命名空间: {1}- Pod: {2}- 容器: {3}- 集群: {4}- 环境: {5}**错误摘要**:{6}---*请登录Kibana查看完整日志*alert_text_args:
- "@timestamp"
- "namespace"
- "pod_name"
- "container_name"
- "cluster"
- "environment"
- "message_summary"# 告警频率限制
realert:minutes: 2# 告警过期时间
expire_after:minutes: 30# 聚合配置
aggregation:minutes: 1# 包含字段
include:
- "@timestamp"
- "namespace"
- "pod_name"
- "container_name"
- "cluster"
- "environment"
- "message"
- "message_summary"
rules/high-frequency-error-alert.yaml (高频错误日志告警)
# 高频错误日志告警规则
# 监控短时间内大量ERROR日志的情况name: "高频错误日志告警"
type: frequency
index: .ds-filebeat-*,filebeat-*,.ds-filebeat-latency-*,filebeat-latency-*
num_events: 10 # 1分钟内超过10个ERROR日志
timeframe:minutes: 1# 使用增强脚本截取message第一行
match_enhancements:- "enhancements.truncate_message.TruncateMessageEnhancement"# 过滤条件 - 只监控default命名空间的ERROR日志
filter:
- bool:must:- match:message: "ERROR"- term:namespace: "default"must_not:- term:container_name: "uni-log"# 钉钉告警配置
alert:
- "dingtalk"# 钉钉机器人配置(加签方式)
# 钉钉机器人配置(加签方式)
dingtalk_webhook: "https://oapi.dingtalk.com/robot/send"
dingtalk_access_token: "YOUR_ACCESS_TOKEN"
dingtalk_sign: "YOUR_SECRET"
dingtalk_msgtype: "markdown"# 告警标题
alert_subject: "🔥 高频错误日志告警 - {0}个错误/分钟"# 告警内容模板
alert_text_type: alert_text_only
alert_text: |## 🔥 高频错误日志告警**告警时间**: {0}**告警详情**:- 错误数量: {1}个/分钟- 阈值: 10个/分钟**来源信息**:- 命名空间: {2}- Pod: {3}- 集群: {4}- 环境: {5}**错误示例**:{6}**建议**: 立即检查系统日志和服务状态---*此告警由ElastAlert自动发送*alert_text_args:
- "@timestamp"
- "num_matches"
- "namespace"
- "pod_name"
- "cluster"
- "environment"
- "message_summary"# 告警频率限制
realert:minutes: 5# 告警过期时间
expire_after:minutes: 30# 聚合配置
aggregation:minutes: 1# 包含字段
include:
- "@timestamp"
- "num_matches"
- "namespace"
- "pod_name"
- "container_name"
- "cluster"
- "environment"
- "message"
- "message_summary"
⚠️ 注意: 将 YOUR_ACCESS_TOKEN 和 YOUR_SECRET 替换为实际配置。
步骤3:测试Elasticsearch连接
3.1 创建测试脚本 test_es.sh
#!/bin/bash
# Elasticsearch连接测试脚本ES_HOST="<your ip>"
ES_PORT="9200"
ES_USER="elastic"
ES_PASS="YOUR_PASSWORD"
ES_URL="https://${ES_HOST}:${ES_PORT}"echo "测试ES连接..."# 测试连接
curl -k -u "${ES_USER}:${ES_PASS}" "${ES_URL}/_cluster/health"# 测试索引访问
curl -k -u "${ES_USER}:${ES_PASS}" "${ES_URL}/filebeat-*/_count"
curl -k -u "${ES_USER}:${ES_PASS}" "${ES_URL}/.ds-filebeat-*/_count"
3.2 运行测试
chmod +x test_es.sh
./test_es.sh
预期输出应包含集群状态和文档数量。
步骤4:测试钉钉机器人
4.1 创建钉钉测试脚本 test_dingtalk_curl.sh
#!/bin/bash
# 钉钉加签测试脚本ACCESS_TOKEN="YOUR_ACCESS_TOKEN"
SECRET="YOUR_SECRET"# 获取时间戳(毫秒)
TIMESTAMP=$(date +%s000)# 计算签名
STRING_TO_SIGN="${TIMESTAMP}\n${SECRET}"
SIGN=$(echo -ne "${STRING_TO_SIGN}" | openssl dgst -sha256 -hmac "${SECRET}" -binary | base64 | python3 -c "import sys, urllib.parse; print(urllib.parse.quote_plus(sys.stdin.read().strip()))")# 构建URL
URL="https://oapi.dingtalk.com/robot/send?access_token=${ACCESS_TOKEN}×tamp=${TIMESTAMP}&sign=${SIGN}"# 发送测试消息
curl -X POST "${URL}" \-H 'Content-Type: application/json' \-d '{"msgtype": "text","text": {"content": "【ElastAlert配置测试】这是一条测试消息,如果收到说明配置正确!"}}'
4.2 运行测试
chmod +x test_dingtalk_curl.sh
./test_dingtalk_curl.sh
预期返回: {"errcode":0,"errmsg":"ok"},并在钉钉群收到测试消息。
步骤5:启动ElastAlert服务
# 启动服务
docker-compose up -d# 查看启动日志
docker logs -f elastalert-dingtalk
预期输出
Reading Elastic 8 index mappings:
Index elastalert_status already exists. Skipping index creation.
Successfully loaded 2 rules
Starting ElastAlert...
配置说明
Docker Compose配置要点
| 配置项 | 值 | 说明 |
|---|---|---|
| image | registry.cn-hangzhou.aliyuncs.com/docker_image-ljx/elastalert2:latest | 阿里云镜像,国内访问快 |
| user | root | 解决日志文件写入权限问题 |
| network_mode | host | 使用宿主机网络,直接访问ES |
| TZ | Asia/Shanghai | 时区设置为CST |
Elasticsearch连接配置
# 基础配置
es_host: <your ip>
es_port: 9200
es_username: elastic
es_password: YOUR_PASSWORD# SSL配置(关键)
use_ssl: true # 必须使用SSL
verify_certs: false # 禁用证书验证(简化配置)
ssl_show_warn: false # 隐藏SSL警告# ES 8.x兼容配置(必须)
es_send_get_body_as: GET
es_conn_timeout: 120
钉钉告警配置(关键)
⚠️ 重要:参数名称必须正确
# 正确的配置
dingtalk_webhook: "https://oapi.dingtalk.com/robot/send"
dingtalk_access_token: "YOUR_TOKEN"
dingtalk_sign: "YOUR_SECRET" # ← 必须是 dingtalk_sign,不是 dingtalk_secret
dingtalk_msgtype: "markdown" # 或 "text"
告警规则
规则1:高频错误日志告警
文件: rules/error-log-alert.yaml
触发条件:
- 1分钟内出现 ≥10 条ERROR日志
- 命名空间: default
- 排除容器: app-log
告警间隔: 5分钟
告警格式:
🔥 高频错误日志告警 - 15个错误/分钟## 🔥 高频错误日志告警告警时间: 2025-11-10T16:39:07告警详情:
- 错误数量: 15个/分钟
- 阈值: 10个/分钟来源信息:
- 命名空间: default
- Pod: ntpms-77b48654cb-nq97s
- 集群: k8s-prod
- 环境: production错误示例:2025-11-10 16:39:07.039 ERROR 1 --- [io-10170-exec-6] DefaultExceptionHandlerAutoConfiguration : org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe建议: 立即检查系统日志和服务状态---
此告警由ElastAlert自动发送
### 核心配置项说明
索引模式(关键)
index: .ds-filebeat-*,filebeat-*,.ds-filebeat-latency-*,filebeat-latency-*
说明:
.ds-filebeat-*: 匹配Data Stream格式索引(ES 8.x默认格式)filebeat-*: 兼容标准索引格式
过滤条件
filter:
- bool:must: # 必须满足的条件- match:message: "ERROR" # message包含ERROR- term:namespace: "default" # 只监控default命名空间must_not: # 排除条件- term:container_name: "app-log" # 排除app-log容器
增强脚本(消息截取)
match_enhancements:- "enhancements.truncate_message.TruncateMessageEnhancement"
作用: 自动截取message字段的第一行,最多200字符。
钉钉配置(必须正确)
dingtalk_webhook: "https://oapi.dingtalk.com/robot/send"
dingtalk_access_token: "7a386d1af5fc73cb4b7521696f085cf628dfc467e24b02304b47b14fcc375231"
dingtalk_sign: "SEC38c6c1654345979e255743bce979f28bedb4eed35387fc5d700aa662a6609497"
dingtalk_msgtype: "markdown"
⚠️ 关键:
- 参数名必须是
dingtalk_sign(不是dingtalk_secret) - msgtype 建议使用
markdown(格式更好)
告警内容优化
alert_text_type: alert_text_only # 只显示自定义内容,不附加额外字段alert_text_args:
- "@timestamp"
- "num_matches"
- "namespace"
- "pod_name"
- "cluster"
- "environment"
- "message_summary" # 使用截取后的message(第一行)
故障排查
常见问题1:权限错误
错误信息:
PermissionError: [Errno 13] Permission denied: '/opt/elastalert/logs/elastalert.log'
解决方案:
# docker-compose.yml 中添加
user: root
常见问题2:网络连接失败
错误信息:
ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))
解决方案:
# docker-compose.yml 中使用
network_mode: "host"
常见问题3:查询返回0 hits
错误信息:
[INFO] elastalert: Queried rule: 0 / 0 hits
可能原因:
- 索引模式不匹配(没有使用
.ds-前缀) - 过滤条件不正确(字段不存在)
- 时间范围内确实没有ERROR日志
解决方案:
# 确保索引模式包含Data Stream格式
index: .ds-filebeat-*,filebeat-*# 简化过滤条件
filter:
- match:message: "ERROR"
常见问题4:钉钉不发送消息
症状: ElastAlert显示"Trigger sent to dingtalk"但钉钉没收到
可能原因:
- ❌ 使用了错误的参数名
dingtalk_secret - ✅ 应该使用
dingtalk_sign
解决方案:
# 错误配置
dingtalk_secret: "SEC..." # ❌# 正确配置
dingtalk_sign: "SEC..." # ✅
常见问题5:消息过长
症状: 钉钉消息包含完整的堆栈跟踪,非常长
解决方案:
- 使用增强脚本截取第一行
- 添加
alert_text_type: alert_text_only - 使用
message_summary而不是message
查看日志
# 实时日志
docker logs -f elastalert-dingtalk# 最近100行
docker logs --tail 100 elastalert-dingtalk# 查看ERROR
docker logs elastalert-dingtalk 2>&1 | grep -i error# 查看告警发送记录
docker logs elastalert-dingtalk | grep "alerts sent"# 查看钉钉发送记录
docker logs elastalert-dingtalk | grep "dingtalk"
维护指南
日常维护命令
# 查看服务状态
docker ps | grep elastalert# 重启服务
docker-compose restart# 停止服务
docker-compose down# 启动服务
docker-compose up -d# 查看日志
docker logs -f elastalert-dingtalk# 查看规则加载情况
docker logs elastalert-dingtalk | grep "rules loaded"# 查看查询统计
docker logs elastalert-dingtalk | grep "query hits"
修改配置后的操作
# 1. 修改配置文件或规则文件# 2. 重启服务
docker-compose restart# 3. 验证配置
docker logs -f elastalert-dingtalk
调整告警规则
修改触发阈值
# rules/error-log-alert.yaml
num_events: 5 # 改为需要的数量
timeframe:minutes: 5 # 改为需要的时间窗口
修改告警频率
realert:minutes: 15 # 增加到15分钟,减少告警频繁度
监控其他命名空间
filter:
- bool:must:- match:message: "ERROR"- term:namespace: "production" # 改为目标命名空间
监控多个命名空间
filter:
- bool:must:- match:message: "ERROR"- terms:namespace: ["default", "production", "test"]
排除多个容器
must_not:
- terms:container_name: ["app-log", "test-app", "debug-pod"]
清理日志
# 清理ElastAlert日志
rm -f logs/elastalert.log*# 重启服务
docker-compose restart
备份配置
# 备份配置文件
tar -czf elastalert-backup-$(date +%Y%m%d).tar.gz \docker-compose.yml \elastalert-config.yaml \rules/ \enhancements/# 恢复配置
tar -xzf elastalert-backup-YYYYMMDD.tar.gz
性能优化
优化查询性能
# elastalert-config.yaml# 减少缓冲区时间(查询范围更小)
buffer_time:minutes: 10 # 从15分钟减少到10分钟# 限制查询大小
max_query_size: 5000 # 从10000减少到5000
优化告警频率
# 增加告警间隔,减少钉钉消息数量
realert:minutes: 30 # 同一告警30分钟内只发送一次
优化日志级别
# 生产环境建议使用INFO级别
loggers:elastalert:level: INFO # 不使用DEBUG,减少日志量
安全建议
1. 启用SSL证书验证(生产环境)
# elastalert-config.yaml
use_ssl: true
verify_certs: true
es_ca_certs: /opt/elastalert/certs/http_ca.crt
需要从ES服务器获取CA证书并放入 certs/ 目录。
2. 保护敏感信息
- 不要将包含密码和token的配置文件提交到Git
- 使用环境变量或密钥管理系统
3. 限制网络访问
- 配置防火墙规则
- 只允许ElastAlert访问ES的9200端口
4. 定期更新
# 更新镜像
docker-compose pull
docker-compose up -d
监控指标
关键指标
| 指标 | 说明 | 查看命令 |
|---|---|---|
| 容器状态 | 应为 Up | docker ps | grep elastalert |
| 规则加载 | 应显示加载规则数 | docker logs elastalert-dingtalk | grep "rules loaded" |
| 查询成功 | 应有query hits日志 | docker logs elastalert-dingtalk | grep "query hits" |
| 告警发送 | 应有alerts sent日志 | docker logs elastalert-dingtalk | grep "alerts sent" |
健康检查
# 检查容器健康状态
docker inspect elastalert-dingtalk | grep -A 5 Health# 查看最近的告警统计
docker logs elastalert-dingtalk | grep "alerts sent" | tail -10# 检查是否有错误
docker logs elastalert-dingtalk 2>&1 | grep -i "error\|exception" | tail -20
告警规则参考
基础规则模板
name: "规则名称"
type: frequency # 频率类型
index: .ds-filebeat-*,filebeat-* # 索引模式
num_events: 5 # 触发阈值
timeframe:minutes: 10 # 时间窗口# 时间字段
timestamp_field: "@timestamp"# 过滤条件
filter:
- bool:must:- match:message: "ERROR"- term:namespace: "default"# 告警配置
alert:
- "dingtalk"dingtalk_webhook: "https://oapi.dingtalk.com/robot/send"
dingtalk_access_token: "YOUR_TOKEN"
dingtalk_sign: "YOUR_SECRET"
dingtalk_msgtype: "text"# 告警内容
alert_text: "检测到{0}条ERROR日志"
alert_text_args:
- num_matches# 告警间隔
realert:minutes: 10
高级过滤示例
按Pod名称过滤
filter:
- bool:must:- match:message: "ERROR"- wildcard:pod_name: "api-*" # 只监控api-开头的Pod
按多个条件过滤
filter:
- bool:must:- match:message: "ERROR"- term:namespace: "default"- term:environment: "production"must_not:- terms:container_name: ["app-log", "test-app"]
使用正则表达式
filter:
- bool:must:- regexp:message: "ERROR.*Exception.*" # 匹配包含ERROR和Exception的日志
附录
A. 配置参数速查
| 参数 | 位置 | 必填 | 说明 |
|---|---|---|---|
| es_host | elastalert-config.yaml | ✅ | ES服务器地址 |
| es_port | elastalert-config.yaml | ✅ | ES端口 |
| use_ssl | elastalert-config.yaml | ✅ | 使用SSL |
| verify_certs | elastalert-config.yaml | ✅ | 证书验证(建议false) |
| dingtalk_access_token | rules/*.yaml | ✅ | 钉钉机器人token |
| dingtalk_sign | rules/*.yaml | ✅ | 钉钉加签secret |
| index | rules/*.yaml | ✅ | 索引模式 |
| namespace | filter | 根据需要 | 命名空间过滤 |
B. 错误码参考
ElastAlert错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
| PermissionError | 权限不足 | 添加 user: root |
| ConnectionError | 网络问题 | 使用 network_mode: host |
| 0 / 0 hits | 查询不到数据 | 检查索引模式和过滤条件 |
| TypeError: issubclass | 增强脚本格式错误 | 使用类而不是函数 |
钉钉错误码
| errcode | 说明 | 解决方案 |
|---|---|---|
| 0 | 成功 | - |
| 310000 | 关键词不匹配 | 检查机器人设置 |
| 300001 | 签名错误 | 检查secret是否正确 |
| 300002 | 时间戳过期 | 时间同步问题 |
| 300007 | IP不在白名单 | 添加IP或改用加签方式 |
| 43002 | 需要POST请求 | URL格式正确,这是正常的 |
至此部署完成! 🎉
