基于Bash脚本的自动化值班告警推送系统实战

背景

运维工作中,值班人员需要在告警发生时第一时间收到通知。传统的做法是依赖监控平台推送,但监控平台可能存在延迟或推送失败的情况。本文介绍如何基于 Bash 脚本构建一个轻量级的自动化值班告警推送系统,在监控平台告警的基础上增加一层保障,确保告警不遗漏。

系统架构

整个系统由三个核心模块组成:

  1. 告警采集模块:从监控平台(如 Prometheus Alertmanager)拉取告警
  2. 告警过滤模块:根据规则过滤重复告警、静默告警
  3. 告警推送模块:通过邮件、钉钉/飞书 Webhook 等渠道推送

核心脚本实现

1. 告警采集脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# alert_fetch.sh - 从 Alertmanager 获取告警

ALERTMANAGER_URL="http://localhost:9093/api/v1/alerts"
OUTPUT_FILE="/var/log/ops/alerts_fetched.json"
LOCK_FILE="/var/lock/alert_fetch.lock"

# 防止重复执行
[ -f "$LOCK_FILE" ] && exit 0
touch "$LOCK_FILE"

# 获取当前告警
curl -s "$ALERTMANAGER_URL" -o "$OUTPUT_FILE"

# 清理锁文件
rm -f "$LOCK_FILE"

2. 告警过滤与处理脚本

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
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash
# alert_process.sh - 告警过滤与格式化

ALERT_FILE="/var/log/ops/alerts_fetched.json"
PROCESSED_FILE="/var/log/ops/alerts_processed.json"
DEDUP_FILE="/var/log/ops/alerts_dedup.json"

# 解析告警并过滤
process_alerts() {
# 过滤静默告警
jq '[.[] | select(.startsAt) | select(.status != "resolved")]' "$ALERT_FILE" \
> "$PROCESSED_FILE"
}

# 去重:基于告警名称和实例去重,保留最新告警
deduplicate_alerts() {
jq -s 'unique_by(.labels.alertname + .labels.instance)' \
"$PROCESSED_FILE" > "$DEDUP_FILE"
}

# 格式化告警为可读文本
format_alert_text() {
local alert=$1
local alertname=$(echo "$alert" | jq -r '.labels.alertname')
local instance=$(echo "$alert" | jq -r '.labels.instance')
local severity=$(echo "$alert" | jq -r '.labels.severity')
local description=$(echo "$alert" | jq -r '.annotations.description')

echo "【${severity^^}${alertname}"
echo "实例:${instance}"
echo "描述:${description}"
echo "时间:$(date '+%Y-%m-%d %H:%M:%S')"
}

process_alerts
deduplicate_alerts

3. 告警推送脚本

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/bin/bash
# alert_notify.sh - 多渠道告警推送

DEDUP_FILE="/var/log/ops/alerts_processed.json"
WEBHOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
EMAIL_TO="oncall@example.com"

send_to_feishu() {
local message=$1
local payload=$(cat <<EOF
{
"msg_type": "text",
"content": {
"text": "${message}"
}
}
EOF
)
curl -X POST -H "Content-Type: application/json" \
-d "$payload" "$WEBHOOK_URL" 2>/dev/null
}

send_to_email() {
local subject=$1
local body=$2
echo "$body" | mail -s "$subject" "$EMAIL_TO"
}

# 主循环
while IFS= read -r alert; do
alertname=$(echo "$alert" | jq -r '.labels.alertname')
instance=$(echo "$alert" | jq -r '.labels.instance')
severity=$(echo "$alert" | jq -r '.labels.severity')

message="【${severity^^}${alertname} 告警发生在 ${instance}"

send_to_feishu "$message"
send_to_email "[${severity^^}] ${alertname} 告警通知" "$message"
done < <(jq -c '.[]' "$DEDUP_FILE")

Cron 调度配置

将脚本加入 Cron 调度,每分钟执行一次:

1
2
3
4
5
6
7
# 编辑 Crontab
crontab -e

# 添加以下内容
* * * * * /opt/ops/scripts/alert_fetch.sh
* * * * * sleep 10 && /opt/ops/scripts/alert_process.sh
* * * * * sleep 20 && /opt/ops/scripts/alert_notify.sh

告警收敛策略

为避免告警风暴,引入告警收敛机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
# alert_merge.sh - 告警收敛

ALERT_FILE="/var/log/ops/alerts_processed.json"
MERGED_FILE="/var/log/ops/alerts_merged.json"

# 按告警名称聚合,5分钟内相同告警只推送一次
merge_alerts() {
jq --argjson interval 300 '
map({
key: .labels.alertname,
value: .
}) | from_entries | to_entries |
map(.value | .createdAt = (.startsAt | fromdateiso8601) |
select((now | floor) - .createdAt < $interval))
)
' "$ALERT_FILE" > "$MERGED_FILE"
}

merge_alerts

脚本日志与监控

为确保脚本稳定运行,需要完善的日志记录:

1
2
3
4
5
6
7
8
9
LOG_FILE="/var/log/ops/alert_system.log"

log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

log "INFO: Alert fetch started"
# ... 执行逻辑 ...
log "INFO: Alert fetch completed, processed $(wc -l < "$OUTPUT_FILE") alerts"

同时建议通过 Prometheus 监控 Cron 任务的执行状态:

1
2
3
4
5
# cron_exporter 配置
- job_name: 'cron_alerts'
scrape_interval: 30s
static_configs:
- targets: ['localhost:9000']

总结

通过 Bash 脚本构建的自动化值班告警推送系统具有以下优势:

特性 说明
轻量级 无需额外依赖,纯 Shell 实现
可扩展 易于添加新的告警渠道
可靠 去重 + 收敛机制避免告警风暴
可观测 完善的日志记录,便于排查问题

该方案适用于中小规模运维场景,大规模场景建议结合专业的监控平台使用。