Pi-hole报警系统:实时异常检测与通知

Pi-hole报警系统:实时异常检测与通知

【免费下载链接】pi-hole A black hole for Internet advertisements 【免费下载链接】pi-hole 项目地址: https://gitcode.com/GitHub_Trending/pi/pi-hole

网络广告过滤工具Pi-hole在家庭和小型办公网络中广泛应用,但其默认配置缺乏主动监控能力。当DNS查询异常激增、广告拦截率骤降或服务意外终止时,用户往往无法及时察觉。本文将系统讲解如何构建基于原生组件的Pi-hole报警系统,通过日志分析、进程监控和阈值检测实现全方位异常预警。

报警系统架构设计

Pi-hole报警系统采用三层架构设计,通过模块化组件实现异常检测与通知:

mermaid

核心组件说明

  • 数据采集层:通过解析pihole.logpihole-FTL.log获取DNS查询数据,利用系统命令采集CPU/内存/磁盘使用率,通过utils.sh中的getFTLPID()函数监控服务进程
  • 异常检测层:实现三类检测机制(阈值检测、趋势分析、服务状态检查),通过piholeDebug.sh中的诊断逻辑扩展异常判断能力
  • 通知分发层:支持本地日志标记、邮件推送和Webhook集成,可通过修改pihole.cron配置定时任务触发检测

关键异常指标定义

基于Pi-hole运行特性,需重点监控以下六类异常指标,建议设置三级告警阈值:

指标类别监控参数警告阈值严重阈值数据来源
DNS服务状态FTL进程存活进程不存在 >5秒进程不存在 >30秒getFTLPID()
查询流量DNS查询量/分钟>基础值200%>基础值300%pihole.log
拦截效率广告拦截率<70%<50%pihole -c
系统资源内存使用率>85%>95%free -m
磁盘空间/var/log使用率>80%>90%df -h
上游DNS查询失败率>5%>10%pihole-FTL.log

表:Pi-hole核心监控指标及阈值建议

阈值计算方法:基础值建议取7天滑动平均值,通过pihole-FTL sqlite3查询历史数据:

# 获取最近7天平均DNS查询量
sqlite3 /etc/pihole/pihole-FTL.db "SELECT AVG(count) FROM queries WHERE timestamp > strftime('%s','now','-7 days') GROUP BY strftime('%Y-%m-%d %H:%M', timestamp, 'unixepoch');"

异常检测实现方案

1. FTL服务存活监控

利用utils.sh中的getFTLPID()函数实现进程监控,扩展添加超时重启与告警功能:

#!/bin/bash
# 保存为 /usr/local/bin/monitor_ftl.sh
source /opt/pihole/utils.sh
source /opt/pihole/COL_TABLE

FTL_PID_FILE="/run/pihole-FTL.pid"
ALERT_THRESHOLD=3  # 连续失败次数
RESTART_THRESHOLD=5  # 触发自动重启的失败次数

check_ftl_status() {
    local fail_count=0
    local restart_count=0
    
    while true; do
        PID=$(getFTLPID "$FTL_PID_FILE")
        
        if [ "$PID" -eq -1 ]; then
            fail_count=$((fail_count + 1))
            restart_count=$((restart_count + 1))
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_RED}FTL服务异常${COL_NC} (连续失败: $fail_count次)" >> /var/log/pihole/alert.log
            
            if [ $restart_count -ge $RESTART_THRESHOLD ]; then
                echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_YELLOW}尝试重启FTL服务${COL_NC}" >> /var/log/pihole/alert.log
                systemctl restart pihole-FTL
                restart_count=0  # 重置重启计数器
            fi
            
            if [ $fail_count -ge $ALERT_THRESHOLD ]; then
                send_alert "FTL服务异常" "FTL进程已连续失败$fail_count次,可能需要手动干预"
                fail_count=0  # 重置告警计数器
            fi
        else
            fail_count=0
            restart_count=0
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_GREEN}FTL服务正常${COL_NC} (PID: $PID)" >> /var/log/pihole/monitor.log
        fi
        
        sleep 10  # 10秒检查一次
    done
}

send_alert() {
    # 实现告警通知逻辑,后续章节详细说明
    echo "ALERT: $1 - $2" >> /var/log/pihole/critical_alert.log
}

check_ftl_status

将上述脚本添加到系统服务并设置开机启动:

# 创建systemd服务文件
sudo tee /etc/systemd/system/pihole-alert.service <<EOF
[Unit]
Description=Pi-hole FTL Service Monitor
After=pihole-FTL.service

[Service]
ExecStart=/usr/local/bin/monitor_ftl.sh
Restart=always
User=root

[Install]
WantedBy=multi-user.target
EOF

# 启用并启动服务
sudo systemctl enable pihole-alert.service
sudo systemctl start pihole-alert.service

2. DNS查询异常检测

通过分析pihole.log实现DNS查询量异常检测,使用滑动窗口算法识别异常流量模式:

#!/bin/bash
# 保存为 /usr/local/bin/query_alert.sh
source /opt/pihole/COL_TABLE

LOG_FILE="/var/log/pihole/pihole.log"
ALERT_LOG="/var/log/pihole/query_alert.log"
WINDOW_SIZE=5  # 5分钟滑动窗口
BASELINE_DAYS=7  # 7天基准数据

# 计算基准查询量(最近7天相同时间段的平均值)
calculate_baseline() {
    local current_hour=$(date +%H)
    local current_minute=$(date +%M)
    local current_day=$(date +%u)
    
    # 计算相同小时段的平均查询量
    baseline=$(awk -v h="$current_hour" -v m="$current_minute" '
        BEGIN { total=0; count=0 }
        { 
            time = substr($1, 1, 5)  # 提取HH:MM
            if (time >= h ":" (m - 5) && time <= h ":" (m + 5)) {
                total += 1
                count += 1
            }
        }
        END { print total/count }
    ' $(find /var/log/pihole -name "pihole.log.*.gz" | sort -r | head -n $((BASELINE_DAYS*24))))  # 取最近7天的日志
    
    echo $baseline
}

# 实时监控查询量
monitor_queries() {
    local baseline=$(calculate_baseline)
    local threshold_high=$(echo "$baseline * 3" | bc)  # 300%阈值
    local threshold_medium=$(echo "$baseline * 2" | bc)  # 200%阈值
    local window_count=0
    
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 基准查询量: $baseline QPM, 告警阈值: 中=$threshold_medium, 高=$threshold_high" >> $ALERT_LOG
    
    # 使用tail -f实时监控日志
    tail -F $LOG_FILE | while read -r line; do
        window_count=$((window_count + 1))
        
        # 每5分钟计算一次平均查询量
        if [ $((window_count % (60 * 5))) -eq 0 ]; then
            current_qpm=$window_count
            window_count=0
            
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] 当前查询量: $current_qpm QPM" >> $ALERT_LOG
            
            # 判断是否超过阈值
            if (( $(echo "$current_qpm > $threshold_high" | bc -l) )); then
                send_alert "DNS查询量异常" "当前查询量$current_qpm QPM,超过基准值300% (基准值: $baseline QPM)"
                echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_RED}严重告警: 查询量超过300%基准值${COL_NC}" >> $ALERT_LOG
            elif (( $(echo "$current_qpm > $threshold_medium" | bc -l) )); then
                echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_YELLOW}警告: 查询量超过200%基准值${COL_NC}" >> $ALERT_LOG
            fi
            
            # 每小时重新计算基准值,适应流量变化
            if [ $(( $(date +%M) )) -eq 0 ]; then
                baseline=$(calculate_baseline)
                threshold_high=$(echo "$baseline * 3" | bc)
                threshold_medium=$(echo "$baseline * 2" | bc)
                echo "[$(date '+%Y-%m-%d %H:%M:%S')] 更新基准值: $baseline QPM" >> $ALERT_LOG
            fi
        fi
    done
}

send_alert() {
    # 告警通知逻辑
    echo "ALERT: $1 - $2" >> /var/log/pihole/critical_alert.log
}

monitor_queries

3. 广告拦截率骤降检测

通过定时检查Pi-hole拦截效率,当拦截率低于设定阈值时触发告警:

#!/bin/bash
# 保存为 /usr/local/bin/block_rate_alert.sh
source /opt/pihole/COL_TABLE

ALERT_THRESHOLD_LOW=50  # 严重阈值:拦截率<50%
ALERT_THRESHOLD_MEDIUM=70  # 警告阈值:拦截率<70%
CHECK_INTERVAL=300  # 检查间隔(秒)
ALERT_LOG="/var/log/pihole/block_rate_alert.log"

check_block_rate() {
    while true; do
        # 使用pihole -c获取统计信息
        stats=$(pihole -c -e)
        
        # 提取拦截率和总查询量
        block_rate=$(echo "$stats" | grep "Blocked" | awk '{print $4}' | sed 's/%//')
        total_queries=$(echo "$stats" | grep "Queries" | awk '{print $2}')
        
        # 判断拦截率是否低于阈值
        if (( $(echo "$block_rate < $ALERT_THRESHOLD_LOW" | bc -l) )); then
            send_alert "拦截率严重下降" "当前拦截率: ${block_rate}%,总查询: ${total_queries}。已低于${ALERT_THRESHOLD_LOW}%阈值"
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_RED}严重告警: 拦截率${block_rate}%${COL_NC} (总查询: $total_queries)" >> $ALERT_LOG
        elif (( $(echo "$block_rate < $ALERT_THRESHOLD_MEDIUM" | bc -l) )); then
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_YELLOW}警告: 拦截率${block_rate}%${COL_NC} (总查询: $total_queries)" >> $ALERT_LOG
        else
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_GREEN}正常: 拦截率${block_rate}%${COL_NC} (总查询: $total_queries)" >> $ALERT_LOG
        fi
        
        sleep $CHECK_INTERVAL
    done
}

send_alert() {
    # 告警通知逻辑
    echo "ALERT: $1 - $2" >> /var/log/pihole/critical_alert.log
}

check_block_rate

将上述检测脚本添加到crontab:

# 每5分钟执行一次拦截率检查
echo "*/5 * * * * /usr/local/bin/block_rate_alert.sh" | sudo tee -a /etc/cron.d/pihole_alert

多渠道通知系统实现

1. 邮件通知集成

利用mailutils实现邮件通知功能,配置SMTP服务发送告警邮件:

#!/bin/bash
# 保存为 /usr/local/bin/send_email_alert.sh

# 配置邮件参数
SMTP_SERVER="smtp.example.com"
SMTP_PORT="587"
SMTP_USER="your_email@example.com"
SMTP_PASSWORD="your_email_password"
RECIPIENT="recipient@example.com"

send_email() {
    local subject="$1"
    local body="$2"
    
    # 使用mail命令发送邮件
    echo -e "Subject: Pi-hole告警: $subject\n\n$body" | \
    mail -S smtp="smtp://$SMTP_SERVER:$SMTP_PORT" \
         -S smtp-use-starttls \
         -S smtp-auth=login \
         -S smtp-auth-user="$SMTP_USER" \
         -S smtp-auth-password="$SMTP_PASSWORD" \
         -S from="$SMTP_USER" \
         "$RECIPIENT"
}

# 在之前的告警函数中调用
send_alert() {
    local subject="$1"
    local body="$2"
    
    # 写入本地日志
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ALERT: $subject - $body" >> /var/log/pihole/critical_alert.log
    
    # 发送邮件通知
    /usr/local/bin/send_email_alert.sh "$subject" "$body"
}

2. 系统日志集成

将告警信息集成到系统日志,便于集中管理和分析:

# 修改告警发送函数
send_alert() {
    local subject="$1"
    local body="$2"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    # 写入本地告警日志
    echo "[$timestamp] ALERT: $subject - $body" >> /var/log/pihole/critical_alert.log
    
    # 发送系统日志
    logger -p alert -t pihole-alert "[$subject] $body"
    
    # 发送邮件通知
    /usr/local/bin/send_email_alert.sh "$subject" "$body"
}

告警触发与响应流程

异常检测到告警分发的完整流程如下:

mermaid

典型异常响应流程

  1. 接收告警通知,记录异常发生时间和关键指标
  2. 登录Pi-hole管理界面查看实时统计
  3. 检查pihole-FTL.log获取详细错误信息
  4. 根据异常类型执行对应修复操作:
    • FTL服务异常:执行pihole restartdns重启服务
    • 查询量激增:检查网络中是否存在恶意设备或DNS放大攻击
    • 拦截率下降:检查广告列表更新状态,执行pihole -g更新 Gravity
  5. 恢复后验证异常是否解除

系统优化与最佳实践

1. 告警阈值动态调整

基于网络使用模式自动调整告警阈值,避免工作时段误报:

#!/bin/bash
# 动态阈值调整示例
get_dynamic_threshold() {
    local current_hour=$(date +%H)
    local current_day=$(date +%u)
    
    # 工作日高峰时段(8:00-22:00)提高阈值
    if [ $current_day -le 5 ] && [ $current_hour -ge 8 ] && [ $current_hour -le 22 ]; then
        echo "150"  # 150%基准值
    # 夜间时段(22:00-8:00)降低阈值
    else
        echo "120"  # 120%基准值
    fi
}

2. 监控数据可视化

通过piholeDebug.sh收集的诊断数据,结合Python脚本生成趋势图表:

#!/usr/bin/env python3
import matplotlib.pyplot as plt
import pandas as pd
import re
from datetime import datetime

# 解析告警日志
data = []
with open('/var/log/pihole/query_alert.log', 'r') as f:
    for line in f:
        if '当前查询量:' in line:
            # 提取时间戳和查询量
            timestamp = re.search(r'\[(.*?)\]', line).group(1)
            qpm = re.search(r'当前查询量: (\d+)', line).group(1)
            data.append({'timestamp': timestamp, 'qpm': int(qpm)})

# 转换为DataFrame并绘图
df = pd.DataFrame(data)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)

plt.figure(figsize=(12, 6))
df['qpm'].plot(title='DNS查询量趋势')
plt.ylabel('查询量/分钟')
plt.savefig('/var/www/html/query_trend.png')  # 保存图表供Web访问

3. 自动化修复集成

对常见异常实现自动修复,减少人工干预:

# 在FTL服务监控中添加自动修复逻辑
if [ $restart_count -ge $RESTART_THRESHOLD ]; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_YELLOW}尝试重启FTL服务${COL_NC}" >> /var/log/pihole/alert.log
    
    # 尝试标准重启
    systemctl restart pihole-FTL
    sleep 10
    PID=$(getFTLPID "$FTL_PID_FILE")
    
    if [ "$PID" -eq -1 ]; then
        # 标准重启失败,尝试强制重启
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${COL_RED}标准重启失败,尝试强制重启${COL_NC}" >> /var/log/pihole/alert.log
        killall -9 pihole-FTL
        systemctl start pihole-FTL
        sleep 10
        PID=$(getFTLPID "$FTL_PID_FILE")
        
        if [ "$PID" -eq -1 ]; then
            # 强制重启也失败,发送紧急告警
            send_alert "FTL服务无法启动" "标准重启和强制重启均失败,需要手动干预"
        fi
    fi
    
    restart_count=0
fi

总结与扩展方向

本文详细介绍了基于Pi-hole原生组件构建报警系统的完整方案,通过日志分析、进程监控和阈值检测实现了三类核心异常的实时告警。系统具有以下特点:

  1. 原生兼容性:完全基于Pi-hole现有组件开发,无需额外安装第三方监控工具
  2. 多维度监控:覆盖服务存活、查询流量、拦截效率等关键指标
  3. 灵活通知机制:支持本地日志、系统日志和邮件通知多渠道分发
  4. 自动化修复:集成常见异常的自动恢复逻辑

未来扩展方向

  • 集成Prometheus和Grafana实现更专业的可视化监控
  • 添加网络流量异常检测,识别潜在的DNS攻击
  • 开发移动App通知渠道,实现即时告警推送
  • 构建异常模式识别引擎,通过机器学习预测潜在故障

【免费下载链接】pi-hole A black hole for Internet advertisements 【免费下载链接】pi-hole 项目地址: https://gitcode.com/GitHub_Trending/pi/pi-hole

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值