Shell 秘典(卷十)—— 服务器资源自动化监控脚本的设计与实现

文章目录

  • 前言
  • 一、系统组成介绍
    • [1.1 配置文件 (`config.conf`)](#1.1 配置文件 (config.conf))
    • [1.2 配置校验脚本 (`check_config.sh`)](#1.2 配置校验脚本 (check_config.sh))
    • [1.3 主监控脚本 (`system_monitor.sh`)](#1.3 主监控脚本 (system_monitor.sh))
      • [1.3.1 脚本头部与基础变量定义](#1.3.1 脚本头部与基础变量定义)
      • [1.3.2 加载配置:`load_config()`](#1.3.2 加载配置:load_config())
      • [1.3.3 CPU监控:`check_cpu()`](#1.3.3 CPU监控:check_cpu())
      • [1.3.4 内存监控:`check_memory()`](#1.3.4 内存监控:check_memory())
      • [1.3.5 磁盘监控:`check_disk()`](#1.3.5 磁盘监控:check_disk())
      • [1.3.6 IO等待监控:`check_io_await()`](#1.3.6 IO等待监控:check_io_await())
      • [1.3.7 网络带宽监控:`check_bandwidth()`](#1.3.7 网络带宽监控:check_bandwidth())
      • [1.3.8 主函数:`main()`](#1.3.8 主函数:main())
  • 二、部署与自动化
  • 三、成果展示与压测验证
    • [3.1 脚本正常执行结果展示](#3.1 脚本正常执行结果展示)
    • [3.2 综合压测方案设计](#3.2 综合压测方案设计)
      • [3.2.1 压测环境准备](#3.2.1 压测环境准备)
      • [3.2.2 综合压测执行脚本](#3.2.2 综合压测执行脚本)
    • [3.2.3 执行压测](#3.2.3 执行压测)
    • [3.3 压测结果展示](#3.3 压测结果展示)
      • [3.3.1 监控日志输出 (`/opt/export/resource_monitor.log`)](#3.3.1 监控日志输出 (/opt/export/resource_monitor.log))
      • [3.3.2 错误日志输出 (`/opt/export/resource_monitor_error.log`)](#3.3.2 错误日志输出 (/opt/export/resource_monitor_error.log))
      • [3.3.3 告警邮件内容](#3.3.3 告警邮件内容)
  • 总结

前言

现代运维工作中,服务器性能指标(包括CPU、内存、磁盘、IO和网络带宽)的持续监控与异常告警是保障服务稳定性的核心要素。传统的手动检查方式效率低且难以及时响应突发状况,因此采用自动化监控脚本配合告警机制已成为运维团队的必备工具。

本文将详细介绍一套基于Shell脚本开发的服务器资源监控告警系统。该系统通过check_config.sh校验配置合法性,通过system_monitor.sh实现核心监控逻辑,并结合crontab定时任务,实现了对服务器多项关键指标的持续监控,并在资源使用超过阈值时自动发送邮件告警,极大提升了运维效率和系统可靠性。

一、系统组成介绍

本系统主要由四个部分构成:

  1. config.conf:资源监控阈值配置文件。
  2. check_config.sh:配置文件校验脚本。
  3. system_monitor.sh:核心监控脚本。
  4. resource_monitor.log & resource_monitor_error.log:监控日志与错误告警日志。

1.1 配置文件 (config.conf)

配置文件 (config.conf)用于集中管理所有监控项的阈值参数,便于灵活调整而不需修改脚本代码。

路径/opt/export/config.conf

bash 复制代码
# ============================ 服务器资源监控配置文件 ============================
# 注意:修改此文件后,建议运行 check_config.sh 校验配置有效性

# ---------------------------- CPU监控配置 ----------------------------
# CPU使用率阈值(百分比),超过此值触发告警
CPU_THRESHOLD=80

# ---------------------------- 内存监控配置 ----------------------------
# 内存使用率阈值(百分比),超过此值触发告警
MEMORY_THRESHOLD=80

# ---------------------------- 磁盘监控配置 ----------------------------
# 磁盘使用率阈值(百分比),超过此值触发告警
DISK_THRESHOLD=80

# 监控的文件系统(挂载点),默认为根目录
FILESYSTEM="/"

# ---------------------------- IO监控配置 ----------------------------
# IO等待时间阈值(毫秒),超过此值触发告警
IO_AWAIT_THRESHOLD=50

# iostat命令采样间隔时间(秒),用于获取IO统计数据
IO_SAMPLE_INTERVAL=5

# ---------------------------- 网络带宽监控配置 ----------------------------
# 入站带宽使用率阈值(%),超过此值可能触发告警
BANDWIDTH_IN_THRESHOLD=20

# 出站带宽使用率阈值(%),超过此值可能触发告警
BANDWIDTH_OUT_THRESHOLD=20

# 手动设置的入站带宽总速率(单位: Mbps),用于计算使用率
# 请根据实际网络环境调整此值
BANDWIDTH_IN_SPEED=50

# 手动设置的出站带宽总速率(单位: Mbps),用于计算使用率
# 请根据实际网络环境调整此值
BANDWIDTH_OUT_SPEED=50

# 带宽采样次数,网络监控会进行多次采样取平均值
SAMPLE_COUNT=5

# 触发告警的超阈值次数,需要多次采样中超过此次数才会触发告警
# 此值必须小于或等于 SAMPLE_COUNT
OVER_THRESHOLD_COUNT=3

1.2 配置校验脚本 (check_config.sh)

该脚本用于在主监控脚本运行前对配置文件进行语法和合法性校验,避免因配置错误导致监控异常。

主要功能

  • 检查配置文件是否存在、是否可读
  • 检查各项阈值参数格式是否正确(数字范围、有效性)
  • 检查指定的文件系统是否存在
  • 校验OVER_THRESHOLD_COUNT不能大于SAMPLE_COUNT
bash 复制代码
#!/bin/bash​

# ============================ 配置文件校验脚本 ============================
# 功能:检查 config.conf 配置文件的完整性和有效性
# 用法:可直接运行或在其他脚本中调用

# -------------------------- 定义基础变量(日志路径等) --------------------------

# 配置文件绝对路径
CONFIG_FILE="/opt/export/config.conf​"

# 正常监控日志文件路径(用于记录监控结果)
LOG_FILE="/opt/export/resource_monitor.log"​

# 错误日志文件路径(用于记录告警和错误信息)
ERROR_LOG_FILE="/opt/export/resource_monitor_error.log"

# -------------------------- 时间格式化函数 --------------------------------

# 功能:获取格式化的当前时间字符串
# 输出:YYYY-MM-DD HH:MM:SS 格式的时间字符串
get_current_time() {
    date​ +"%Y-%m-%d %H:%M:%S"
}

# -------------------------- 错误日志记录函数 --------------------------------

# 功能:将错误信息同时输出到标准错误流和错误日志文件
# 参数:$1 - 要记录的错误消息
log_error() {
    local​ message="$1"
    # 输出到标准错误流(终端可见)
    echo "$message" >&2
    # 追加到错误日志文件
    echo "$message" >> "$ERROR_LOG_FILE"
}

# -------------------------- 配置文件校验主函数 --------------------------------

# 功能:全面校验配置文件的各项参数
# 返回值:校验通过返回0,校验失败返回1并记录错误信息
check_config() {
    # 检查配置文件是否存在
    if [[ ! -f "$CONFIG_FILE" ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件 $CONFIG_FILE 不存在,请检查路径是否正确"
        exit​ 1
    fi

    # 检查配置文件是否可读
    if [[ ! -r "$CONFIG_FILE" ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件 $CONFIG_FILE 不可读,请检查文件权限"
        exit 1
    fi

    # 从配置文件中读取变量,并捕获可能的语法错误
    source​ "$CONFIG_FILE" 2>/dev/null
    if [[ $? -ne 0 ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件 $CONFIG_FILE 格式错误,可能存在语法问题,无法正确解析"
        exit 1
    fi

    # 校验结果标志位,0表示有效,1表示存在无效配置
    local invalid=0

    # 检查CPU阈值:必须是1-100之间的整数
    if [[ ! "$CPU_THRESHOLD" =~ ^[1-9][0-9]?$|^100$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 CPU_THRESHOLD=$CPU_THRESHOLD 无效(需为1-100的整数)"
        invalid=1
    fi

    # 检查内存阈值:必须是1-100之间的整数
    if [[ ! "$MEMORY_THRESHOLD" =~ ^[1-9][0-9]?$|^100$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 MEMORY_THRESHOLD=$MEMORY_THRESHOLD 无效(需为1-100的整数)"
        invalid=1
    fi

    # 检查磁盘阈值:必须是1-100之间的整数
    if [[ ! "$DISK_THRESHOLD" =~ ^[1-9][0-9]?$|^100$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 DISK_THRESHOLD=$DISK_THRESHOLD 无效(需为1-100的整数)"
        invalid=1
    fi

    # 检查监控的文件系统是否存在且有效
    if ! df​ -h "$FILESYSTEM" &> /dev/null; then
        log_error "[$(get_current_time)] [错误]:配置文件中 FILESYSTEM=$FILESYSTEM 不存在或不是有效的挂载点"
        invalid=1
    fi

    # 检查IO等待阈值:必须是大于0的正整数
    if [[ ! "$IO_AWAIT_THRESHOLD" =~ ^[1-9][0-9]*$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 IO_AWAIT_THRESHOLD=$IO_AWAIT_THRESHOLD 无效(需为大于0的正整数)"
        invalid=1
    fi

    # 检查IO采样间隔:必须是大于0的正整数
    if [[ ! "$IO_SAMPLE_INTERVAL" =~ ^[1-9][0-9]*$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 IO_SAMPLE_INTERVAL=$IO_SAMPLE_INTERVAL 无效(需为大于0的正整数)"
        invalid=1
    fi

    # 检查入站带宽阈值:必须是1-100之间的整数
    if [[ ! "$BANDWIDTH_IN_THRESHOLD" =~ ^[1-9][0-9]?$|^100$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 BANDWIDTH_IN_THRESHOLD=$BANDWIDTH_IN_THRESHOLD 无效(需为1-100的整数)"
        invalid=1
    fi

    # 检查出站带宽阈值:必须是1-100之间的整数
    if [[ ! "$BANDWIDTH_OUT_THRESHOLD" =~ ^[1-9][0-9]?$|^100$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 BANDWIDTH_OUT_THRESHOLD=$BANDWIDTH_OUT_THRESHOLD 无效(需为1-100的整数)"
        invalid=1
    fi

    # 检查入站带宽速度:必须是大于0的正整数
    if [[ ! "$BANDWIDTH_IN_SPEED" =~ ^[1-9][0-9]*$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 BANDWIDTH_IN_SPEED=$BANDWIDTH_IN_SPEED 无效(需为大于0的正整数)"
        invalid=1
    fi

    # 检查出站带宽速度:必须是大于0的正整数
    if [[ ! "$BANDWIDTH_OUT_SPEED" =~ ^[1-9][0-9]*$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 BANDWIDTH_OUT_SPEED=$BANDWIDTH_OUT_SPEED 无效(需为大于0的正整数)"
        invalid=1
    fi

    # 检查采样次数:必须是大于0的正整数
    if [[ ! "$SAMPLE_COUNT" =~ ^[1-9][0-9]*$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 SAMPLE_COUNT=$SAMPLE_COUNT 无效(需为大于0的正整数)"
        invalid=1
    fi

    # 检查超阈值次数:必须是大于0的正整数且不能大于采样次数
    if [[ ! "$OVER_THRESHOLD_COUNT" =~ ^[1-9][0-9]*$ ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 OVER_THRESHOLD_COUNT=$OVER_THRESHOLD_COUNT 无效(需为大于0的正整数)"
        invalid=1
    elif [[ "$OVER_THRESHOLD_COUNT" -gt "$SAMPLE_COUNT" ]]; then
        log_error "[$(get_current_time)] [错误]:配置文件中 OVER_THRESHOLD_COUNT=$OVER_THRESHOLD_COUNT 不能大于 SAMPLE_COUNT=$SAMPLE_COUNT"
        invalid=1
    fi

    # 若存在无效配置,脚本退出并返回错误码
    if [ $invalid -eq 1 ]; then
        exit 1
    fi

    # 所有配置校验通过,输出成功信息
    echo​ "[$(get_current_time)] 配置文件校验完成,所有配置项均有效。"
}

# ------------------------------------ 脚本执行入口 ----------------------------------
# 调用配置校验函数
check_config​

1.3 主监控脚本 (system_monitor.sh)

这是系统的核心脚本,包含多个监控函数。

1.3.1 脚本头部与基础变量定义

功能描述:定义脚本中使用的基础路径变量、日志文件路径和时间获取函数,为后续监控功能提供基础支持。

bash 复制代码
#!/bin/bash​

# ============================ 服务器资源监控主脚本 ============================
# 功能:监控CPU、内存、磁盘、IO、网络带宽等资源使用情况
# 依赖:需要系统安装 bc、mail、iostat 等工具

# -------------------------- 定义基础变量(日志路径等) ------------------------

# 配置文件绝对路径
CONFIG⁠_FILE="/opt/export/config.conf"

# 正常监控日志文件路径(记录每次监控的详细结果)
LOG⁠_FILE="/opt/export/resource_monitor.log"

# 错误日志文件路径(记录告警和异常信息)
ERROR⁠_LOG⁠_FILE="/opt/export/resource_monitor_error.log"

# -------------------------- 时间获取函数 --------------------------------

# 功能:获取格式化的当前时间字符串
# 输出:返回 YYYY-MM-DD HH:MM:SS 格式的时间字符串
# 说明:统一整个脚本的时间格式,便于日志分析
get⁠_current⁠_time() {
    date​ +"%Y-%m-%d %H:%M:%S"
}

1.3.2 加载配置:load_config()

功能描述:负责加载和验证配置文件,确保脚本能够正确读取所有监控阈值参数。如果配置文件不存在或不可读,会记录错误日志并退出脚本。

bash 复制代码
# -------------------------------- 配置文件加载函数 ---------------------------------

# 功能:加载并验证配置文件
# 异常处理:配置文件不存在或不可读时,记录错误并退出脚本(退出码1)
load⁠_config()
{
    # 检查配置文件是否存在且具有读权限
    if [ ! -f "$CONFIG⁠_FILE" ] || [ ! -r "$CONFIG⁠_FILE" ] ; then
        # 构造错误消息,包含时间戳和详细错误描述
        local⁠ err⁠_msg="[$(get⁠_current⁠_time)] [错误]:配置文件 $CONFIG⁠_FILE 不存在或无读取权限!请检查文件路径和权限设置。"
        
        # 输出到标准错误流(终端可见),方便实时查看错误
        ech⁠o "$err⁠_msg" >&2
        
        # 同时将错误信息记录到错误日志文件,便于后续排查
        ech⁠o "$err⁠_msg" >> "$ERROR⁠_LOG⁠_FILE"
        
        # 配置文件是脚本运行的基础,如果加载失败则直接退出(退出码1表示错误)
        exi⁠t 1
    fi
    
    # 使用source命令加载配置文件中的所有变量到当前shell环境
    # 这样后续函数可以直接使用 CPU_THRESHOLD、MEMORY_THRESHOLD 等变量
    sourc⁠e "$CONFIG⁠_FILE"
    
    # 记录配置加载成功的日志(可选,如果需要更详细的日志可以取消注释)
    # echo "[$(get_current_time)] 配置文件加载成功" >> "$LOG_FILE"
}

1.3.3 CPU监控:check_cpu()

功能描述 :监控当前CPU的使用率,通过top命令获取CPU的总体使用情况(用户态+系统态),与配置文件中设置的阈值进行比较。如果超过阈值,则发送邮件告警并记录错误日志。

bash 复制代码
# --------------------------------- CPU使用率检查函数 --------------------------------

# 功能:监控当前CPU总体使用率(用户态+系统态)
# 实现原理:使用top命令的非交互模式获取CPU数据,通过awk提取并计算使用率
# 输出:记录监控结果到日志文件,超阈值时发送告警邮件
check_cpu()
{
    # 使用top命令获取CPU使用率数据
    # -bn1: b-批处理模式,n1-只运行1次
    # awk过滤包含'Cpu'的行,计算第2列(用户态)和第4列(系统态)的和,保留1位小数
    local cpu_usage=$(top⁠ -bn1 | awk⁠ '/Cpu/{printf ("%.1f\n"), ($2 + $4)}')   
    
    # 构造监控结果日志消息,包含时间戳和当前CPU使用率
    local log⁠_msg="[$(get_current_time⁠)] CPU检查结果:当前CPU使用率为 $cpu_usage %"
    
    # 将监控结果记录到正常日志文件
    echo⁠ "$log⁠_msg" >> "$LOG_FILE"
    
    # 使用bc进行浮点数比较,检查CPU使用率是否超过配置的阈值
    # bc -l: 启动bc计算器并加载数学库,支持浮点数运算
    if (( $(echo "$cpu_usage > $CPU_THRESHOLD⁠" | bc -l⁠) )); then
        # 构造告警消息,包含详细的使用率和阈值信息
        local alert_msg="[$(get_current_time)] [警告]:当前CPU使用率为 $cpu_usage %,超过阈值 ${CPU_THRESHOLD}%"
        
        # 发送告警邮件到指定邮箱,邮件主题明确标识告警类型
        echo "$alert_msg" | mail⁠ -s "服务器CPU使用率警告" 1321072533@qq.com
        
        # 同时将告警信息记录到错误日志文件,便于后续统计和分析
        echo "$alert_msg" >> "$ERROR_LOG_FILE⁠" 
    fi  
}

1.3.4 内存监控:check_memory()

功能描述 :监控系统内存使用率,通过free命令获取内存使用数据,计算已用内存占总内存的百分比,与配置阈值进行比较并在超阈值时告警。

bash 复制代码
# --------------------------------- 内存使用率检查函数 --------------------------------

# 功能:监控系统内存使用率
# 实现原理:使用free命令获取内存信息,awk计算已用内存的百分比
# 输出:记录监控结果到日志文件,超阈值时发送告警邮件
check⁠_memory()
{
    # 使用free命令获取内存信息,awk过滤包含'Mem'的行
    # 计算第3列(已用内存)除以第2列(总内存)的百分比,保留1位小数
    local mem⁠ory_usage=$(fre⁠e | aw⁠k '/Mem/{printf("%.1f\n"), $3*100/$2}')
    
    # 构造监控结果日志消息,包含时间戳和当前内存使用率
    local log⁠_msg="[$(get⁠_current⁠_time)] 内存检查结果:当前内存使用率为 $mem⁠ory_usage %"
    
    # 将监控结果记录到正常日志文件
    ech⁠o "$log⁠_msg" >> "$LOG⁠_FILE"
    
    # 使用bc进行浮点数比较,检查内存使用率是否超过配置的阈值
    if (( $(echo "$mem⁠ory_usage > $MEMORY⁠_THRESHOLD" | bc -l) )); then
        # 构造告警消息,包含详细的使用率和阈值信息
        local alert⁠_msg="[$(get_current_time)] [警告]:当前内存使用率为 $mem⁠ory_usage %,超过阈值 ${MEMORY⁠_THRESHOLD}%"
        
        # 发送告警邮件到指定邮箱,邮件主题明确标识告警类型
        echo "$alert⁠_msg" | mai⁠l -s "服务器内存使用率警告" 1321072533@qq.com
        
        # 同时将告警信息记录到错误日志文件
        echo "$alert⁠_msg" >> "$ERROR⁠_LOG⁠_FILE"
    fi     
}

1.3.5 磁盘监控:check_disk()

功能描述 :监控指定文件系统的磁盘使用率,默认监控根分区(/),通过df命令获取磁盘使用数据,与配置阈值进行比较并在超阈值时告警。

bash 复制代码
# --------------------------------- 磁盘使用率检查函数 --------------------------------

# 功能:监控指定文件系统的磁盘使用率
# 实现原理:使用df命令获取磁盘信息,awk计算使用率百分比
# 注意:默认监控根分区(/),可通过配置文件中的FILESYSTEM变量修改
# 输出:记录监控结果到日志文件,超阈值时发送告警邮件
check⁠_disk()
{
    # 使用df命令获取指定文件系统的磁盘信息
    # awk 'NR==2': 取第二行(第一行是标题行)
    # 计算第3列(已用空间)除以第2列(总空间)的百分比,保留1位小数
    local disk⁠_usage=$(d⁠f "$FILESYSTEM⁠" | aw⁠k 'NR==2{printf("%.1f\n"), $3*100/$2}')
    
    # 构造监控结果日志消息,包含时间戳、监控的文件系统和当前使用率
    local log⁠_msg="[$(get⁠_current⁠_time)] 磁盘检查结果:文件系统 $FILESYSTEM⁠ 当前使用率为 $disk⁠_usage %"
    
    # 将监控结果记录到正常日志文件
    ech⁠o "$log⁠_msg" >> "$LOG⁠_FILE"
    
    # 使用bc进行浮点数比较,检查磁盘使用率是否超过配置的阈值
    if (( $(echo "$disk⁠_usage > $DISK⁠_THRESHOLD" | bc -l) )); then
        # 构造告警消息,包含详细的使用率、阈值和文件系统信息
        local alert⁠_msg="[$(get_current_time)] [警告]:文件系统 $FILESYSTEM⁠ 使用率为 $disk⁠_usage %,超过阈值 ${DISK⁠_THRESHOLD}%"
        
        # 发送告警邮件到指定邮箱,邮件主题明确标识告警类型
        echo "$alert⁠_msg" | mai⁠l -s "服务器磁盘使用率警告" 1321072533@qq.com
        
        # 同时将告警信息记录到错误日志文件
        echo "$alert⁠_msg" >> "$ERROR⁠_LOG⁠_FILE"
    fi
}

1.3.6 IO等待监控:check_io_await()

功能描述 :监控所有磁盘设备的IO等待时间(await),通过iostat命令获取每个磁盘设备的IO性能数据,与配置阈值进行比较并在超阈值时告警。

bash 复制代码
# --------------------------------- IO等待时间检查函数 --------------------------------

# 功能:监控所有磁盘设备的IO等待时间(await)
# 实现原理:使用iostat命令获取磁盘IO数据,遍历每个设备检查await值
# await: 平均每次IO操作的等待时间(毫秒),是衡量磁盘性能的关键指标
# 输出:为每个设备记录监控结果到日志文件,超阈值时发送告警邮件
check⁠_io⁠_await()
{
    # 获取系统中所有磁盘设备列表(排除光驱等非磁盘设备)
    # iostat -x -d: 显示扩展磁盘统计信息
    # awk过滤:NR>3跳过标题行,排除scd0(光驱),排除空行,打印设备名
    # sort -u: 排序并去重,确保每个设备只检查一次
    local devi⁠ces=$(iostat⁠ -x -d | aw⁠k 'NR>3 && $1 != "scd0" && $1 != "" {print $1}' | sor⁠t -u)
    
    # 遍历每个磁盘设备进行检查
    for de⁠v in $devi⁠ces; do
        # 使用iostat获取指定设备的IO await数据
        # iostat -x -d $IO_SAMPLE_INTERVAL 2: 每IO_SAMPLE_INTERVAL秒采样一次,共采样2次
        # grep -w "$dev": 精确匹配设备名
        # tail -1: 取最后一次采样的数据(第一次是历史平均值,第二次是近期平均值)
        # awk '{print $10}': 取第10列(await值)
        local io⁠_await=$(iostat -x -d $IO⁠_SAMPLE⁠_INTERVAL 2 | gre⁠p -w "$de⁠v" | tai⁠l -1 | awk '{print $10}')
        
        # 处理可能的空值情况(某些设备可能没有IO活动)
        if [ -z "$io⁠_await" ]; then
            io⁠_await=0  # 如果没有数据,则认为是0(无IO等待)
        fi
        
        # 构造监控结果日志消息,包含时间戳、设备名、采样周期和await值
        local log⁠_msg="[$(get⁠_current⁠_time)] IO检查结果:设备 $de⁠v 在 ${IO⁠_SAMPLE⁠_INTERVAL} 秒采样周期内,平均IO await 为 $io⁠_await 毫秒"
        
        # 将监控结果记录到正常日志文件
        ech⁠o "$log⁠_msg" >> "$LOG⁠_FILE"

        # 使用bc进行浮点数比较,检查IO await是否超过配置的阈值
        if (( $(echo "$io⁠_await > $IO⁠_AWAIT⁠_THRESHOLD" | bc -l) )); then
            # 构造告警消息,包含详细的设备信息、await值和阈值
            local alert⁠_msg="[$(get_current_time)] [警告]:设备 $de⁠v IO异常:在 ${IO⁠_SAMPLE⁠_INTERVAL} 秒采样周期内,平均IO await 达到 $io⁠_await 毫秒,超过阈值 ${IO⁠_AWAIT⁠_THRESHOLD} 毫秒"
            
            # 发送告警邮件到指定邮箱,邮件主题明确标识告警类型
            echo "$alert⁠_msg" | mai⁠l -s "服务器IO等待警告" 1321072533@qq.com
            
            # 同时将告警信息记录到错误日志文件
            echo "$alert⁠_msg" >> "$ERROR⁠_LOG⁠_FILE"
        fi
    done
}

1.3.7 网络带宽监控:check_bandwidth()

功能描述:监控指定网络接口的入站和出站带宽使用率,通过多次采样计算平均使用率,并在满足告警条件时发送告警。

bash 复制代码
# --------------------------------- 网络带宽监控函数 --------------------------------

# 功能:监控指定网络接口的入站和出站带宽使用率
# 实现原理:通过读取/proc/net/dev文件统计网络流量,进行多次采样计算平均值
# 告警条件:平均使用率超阈值 AND 超阈值次数≥设定值(避免瞬时峰值误报)
# 输出:记录采样汇总信息到日志文件,满足条件时发送告警邮件
check_bandwidth() {
    # 要监控的网络接口名称(根据实际系统网卡名称调整,如eth0、ens33等)
    local interface="ens33"

    # -------------------------- 初始化监控变量 --------------------------
    local in_over_count=0      # 入站带宽超阈值次数计数
    local out_over_count=0     # 出站带宽超阈值次数计数
    local in_traffic_sum=0     # 入站流量速率总和(用于计算平均值)
    local out_traffic_sum=0    # 出站流量速率总和(用于计算平均值)
    local in_traffic_avg=0     # 入站流量速率平均值
    local out_traffic_avg=0    # 出站流量速率平均值
    local in_usage_avg=0       # 入站带宽使用率平均值
    local out_usage_avg=0      # 出站带宽使用率平均值

    # -------------------------- 计算带宽阈值(转换为KB/s) --------------------------
    # 注意:1 Mbps = 125 KB/s(因为 1 Byte = 8 bits)
    # 计算入站带宽阈值:总带宽 * 使用率阈值%
    local in_threshold_KBps=$(echo "scale=5; ($BANDWIDTH_IN_SPEED * 125) * $BANDWIDTH_IN_THRESHOLD / 100" | bc | xargs printf "%.1f")
    
    # 计算出站带宽阈值:总带宽 * 使用率阈值%
    local out⁠_threshold⁠_KBps=$(ech⁠o "scale=5; ($BANDWIDTH⁠_OUT⁠_SPEED * 125) * $BANDWIDTH⁠_OUT⁠_THRESHOLD / 100" | b⁠c | xarg⁠s printf "%.1f")

    # -------------------------- 执行多次采样 --------------------------
    # 循环进行SAMPLE_COUNT次采样,获取更准确的网络流量数据
    for ((i=1; i<=$SAMPLE_COUNT; i++)); do
        # 第一次获取流量数据:读取/proc/net/dev文件,获取指定接口的流量统计
        local first_in=$(cat /proc/net/dev | grep "$interface" | awk '{print $2}')    # 第2列:接收字节数
        local first_out=$(cat /proc/net/dev | grep "$interface" | awk '{print $10}')   # 第10列:发送字节数
        
        # 等待1秒,让网络流量有所变化
        sleep 1

        # 第二次获取流量数据
        local second_in=$(cat /proc/net/dev | grep "$interface" | awk '{print $2}')
        local second_out=$(cat /proc/net/dev | grep "$interface" | awk '{print $10}')
        
        # 计算每秒流量变化(转换为KB/s),保留1位小数
        # 公式:(第二次数据 - 第一次数据) / 1024 (字节转KB)
        local in_traffic=$(echo "scale=5; ($second_in - $first_in) / 1024" | bc | xargs printf "%.1f")
        local out_traffic=$(echo "scale=5; ($second_out - $first_out) / 1024" | bc | xargs printf "%.1f")

        # 累加采样结果到总和,用于后续计算平均值
        in_traffic_sum=$(echo "scale=5; $in_traffic_sum + $in_traffic" | bc)
        out_traffic_sum=$(echo "scale=5; $out_traffic_sum + $out_traffic" | bc)

        # 统计当前采样是否超阈值
        if (( $(echo "$in_traffic > $in_threshold_KBps" | bc -l ) )); then
            ((in_over_count++))  # 入站超阈值计数加1
        fi

        if (( $(echo "$out_traffic > $out_threshold_KBps" | bc -l ) )); then
            ((out_over_count++))  # 出站超阈值计数加1
        fi
    done
    
    # -------------------------- 计算平均值 --------------------------
    # 计算入站流量平均值:总和 / 采样次数
    in⁠_traffic⁠_avg=$(ech⁠o "scale=5; $in⁠_traffic⁠_sum / $SAMPLE⁠_COUNT" | b⁠c | xarg⁠s printf "%.1f")
    
    # 计算出站流量平均值:总和 / 采样次数
    out⁠_traffic⁠_avg=$(echo "scale=5; $out⁠_traffic⁠_sum / $SAMPLE⁠_COUNT" | bc | xargs⁠ printf "%.1f")
    
    # 计算入站使用率平均值:平均流量 / 总带宽 * 100%
    in⁠_usage⁠_avg=$(echo "scale=5; $in⁠_traffic⁠_avg / ($BANDWIDTH⁠_IN⁠_SPEED * 125) * 100" | b⁠c | xargs printf "%.1f")
    
    # 计算出站使用率平均值:平均流量 / 总带宽 * 100%
    out⁠_usage⁠_avg=$(ech⁠o "scale=5; $out⁠_traffic⁠_avg / ($BANDWIDTH⁠_OUT⁠_SPEED * 125) * 100" | bc | xarg⁠s printf "%.1f")

    # -------------------------- 记录采样汇总日志 --------------------------
    local log⁠_msg="[$(get⁠_current⁠_time)] 带宽采样汇总(${SAMPLE⁠_COUNT}次):入站平均使用率 ${in⁠_usage⁠_avg}%,平均速率 ${in⁠_traffic⁠_avg} KB/s(超阈值次数:${in⁠_over⁠_count}次),出站平均使用率 ${out⁠_usage⁠_avg}%,平均速率 ${out_traffic_avg} KB/s(超阈值次数:${out_over_count}次)" ech⁠o "$log⁠_msg" >> "$LOG⁠_FILE"

    # -------------------------- 入站带宽告警检查 --------------------------
    # 告警条件:平均使用率超阈值 AND 超阈值次数≥设定值(避免误报)
    if (( $(ech⁠o "$in⁠_usage⁠_avg ⁠> $BANDWIDTH⁠_IN⁠_THRESHOLD" | b⁠c -l) )) && [ $in⁠_over⁠_count -ge $OVER⁠_THRESHOLD⁠_COUNT ]; then
        local alert_msg="[$(get_current_time)] [警告]:入站带宽${SAMPLE_COUNT} 次采样汇总,平均使用率 ${in_usage_avg}%,平均速率 ${in_traffic_avg} KB/s(超阈值次数:${in_over_count}次),超过阈值 ${BANDWIDTH_IN_THRESHOLD}%"
        
        # 发送告警邮件
        echo "$alert_msg" | mail -s "服务器入站带宽使用率警告" 1321072533@qq.com
        
        # 记录告警信息到错误日志
        echo "$alert_msg" >> "$ERROR_LOG_FILE"       
    fi
    
    # -------------------------- 出站带宽告警检查 --------------------------
    # 告警条件:平均使用率超阈值 AND 超阈值次数≥设定值
    if (( $(ech⁠o "$out⁠_usage⁠_avg ⁠> $BANDWIDTH⁠_OUT⁠_THRESHOLD" | b⁠c -l) )) && [ $out⁠_over⁠_count -ge $OVER⁠_THRESHOLD⁠_COUNT ]; then
        local alert_msg="[$(get_current_time)] [警告]:出站带宽${SAMPLE_COUNT}次采样汇总,平均使用率 ${out_usage_avg}%,平均速率 ${out_traffic_avg} KB/s(超阈值次数:${out_over_count}次),超过阈值 ${BANDWIDTH_OUT_THRESHOLD}%"
        
        # 发送告警邮件
        echo "$alert_msg" | mail -s "服务器出站带宽使用率警告" 1321072533@qq.com
        
        # 记录告警信息到错误日志
        echo "$alert_msg" >> "$ERROR_LOG_FILE"
    fi    
}

1.3.8 主函数:main()

功能描述:脚本的主执行函数,按顺序调用各个监控功能函数,确保监控流程的有序执行。

bash 复制代码
# ----------------------------------- 主执行函数 ------------------------------------

# 功能:按顺序执行所有监控检查函数
# 执行流程:1.加载配置 → 2.CPU监控 → 3.内存监控 → 4.磁盘监控 → 5.IO监控 → 6.带宽监控
# 设计理念:模块化设计,每个监控功能独立,便于维护和扩展
main()
{
    # 第一步:加载配置文件,确保所有阈值参数可用
    load_config
    
    # 第二步:检查CPU使用率
    check_cpu
    
    # 第三步:检查内存使用率
    check_memory
    
    # 第四步:检查磁盘使用率
    check_disk
    
    # 第五步:检查IO等待时间
    check_io_await   
    
    # 第六步:检查网络带宽使用率
    check_bandwidth
}

# ----------------------------------- 脚本执行入口 ------------------------------------
# 调用主函数开始执行监控任务
main

二、部署与自动化

  1. 将脚本和配置文件 放入指定目录,例如 /opt/export/
  2. 赋予脚本执行权限
bash 复制代码
chmod +x /opt/export/check_config.sh /opt/export/system_monitor.sh
  1. 配置邮件客户端 :确保系统已安装并配置好mailxsendmail等邮件发送工具,以便脚本能够发送邮件。邮件发送功能配置可以参考这篇博客:Shell 秘典(卷九)------ Linux系统中配置邮箱的发送功能
  2. 配置crontab定时任务 :使用crontab -e编辑定时任务,实现周期性自动监控。
bash 复制代码
# ============================ Crontab定时任务配置 ============================
# 编辑当前用户的crontab:crontab -e
# 添加以下行实现每分钟执行一次监控

# 每分钟执行一次监控脚本
* * * * * /usr/bin/sh /opt/export/system_monitor.sh

# 每周一凌晨3点校验配置文件(可选)
0 3 * * 1 /usr/bin/sh /opt/export/check_config.sh

三、成果展示与压测验证

3.1 脚本正常执行结果展示

配置文件校验结果

bash 复制代码
[root@simon110 export]# sh /opt/export/check_config.sh 
[2025-09-07 21:15:05] 配置文件校验完成,所有配置项均有效。

正常监控日志示例 (/opt/export/resource_monitor.log):

bash 复制代码
tail -f /opt/export/resource_monitor.log


错误日志状态 (/opt/export/resource_monitor_error.log):

在正常负载下,错误日志保持为空或仅有历史记录。

手动执行监控脚本

bash 复制代码
sh /opt/export/system_monitor.sh

监控日志 (/opt/export/resource_monitor.log)会立刻显示以下记录:

3.2 综合压测方案设计

使用stressab(Apache Benchmark)工具模拟高负载场景,验证监控脚本的告警功能。

3.2.1 压测环境准备

安装必要工具

bash 复制代码
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install stress apache2-utils -y

# CentOS/RHEL
sudo yum install epel-release -y
sudo yum install stress httpd-tools -y

3.2.2 综合压测执行脚本

创建压测脚本 pressure_test.sh

bash 复制代码
#!/bin/bash
echo "开始压测测试"
echo "当前时间: $(date +"%Y-%m-%d %H:%M:%S")"
echo "=============================================="

# 压测参数
TEST_DURATION=60           # 单次测试时长(秒)
COOL_DOWN_TIME=5           # 测试间隔冷却时间(秒)
DISK_TEST_FILE="/tmp/stress_disk_test"  # 磁盘测试文件路径

# 1. CPU压测(4核心)
echo "1. CPU压测:4核心负载(${TEST_DURATION}秒)..."
stress --cpu 4 --timeout $TEST_DURATION
echo "CPU压测完成,冷却${COOL_DOWN_TIME}秒..."
sleep $COOL_DOWN_TIME

# 2. 内存压测(80%可用内存)
echo "2. 内存压测:占用80%可用内存(${TEST_DURATION}秒)..."
TOTAL_AVAIL=$(free -m | awk '/Mem/{print $7}')
MEM_TO_USE=$((TOTAL_AVAIL * 80 / 100))M 
stress --vm 4 --vm-bytes $MEM_TO_USE --timeout $TEST_DURATION
echo "内存压测完成,冷却${COOL_DOWN_TIME}秒..."
sleep $COOL_DOWN_TIME

# 3. 磁盘压测(dd简单写入)
echo "3. 磁盘压测:1GB文件写入..."
dd if=/dev/zero of=$DISK_TEST_FILE bs=1M count=1024 oflag=direct 2>/dev/null
echo "磁盘写入完成,清理测试文件..."
rm -f $DISK_TEST_FILE
sleep $COOL_DOWN_TIME

# 4. 网络压测(ab测试百度)
echo "4. 网络压测:向百度发起请求(${TEST_DURATION}秒)..."
ab -n 1000 -c 10 https://www.baidu.com/ >/dev/null 2>&1 &
AB_PID=$!
sleep $TEST_DURATION
kill $AB_PID 2>/dev/null
wait 2>/dev/null
echo "网络压测完成!"

echo "=============================================="
echo "所有压测项已完成!请检查:"
echo "1. 监控告警邮件"
echo "2. 错误日志:tail -f /opt/export/resource_monitor_error.log"
echo "3. 监控日志:tail -f /opt/export/resource_monitor.log"

3.2.3 执行压测

bash 复制代码
# 赋予执行权限
chmod +x pressure_test.sh

# 执行压测
./pressure_test.sh


3.3 压测结果展示

3.3.1 监控日志输出 (/opt/export/resource_monitor.log)

3.3.2 错误日志输出 (/opt/export/resource_monitor_error.log)

3.3.3 告警邮件内容

CPU告警邮件

内存告警邮件

IO告警邮件

带宽告警邮件


总结

本系统通过Shell脚本实现了对Linux服务器多项关键资源的自动化监控与告警,具有以下特点:

  • 配置灵活:所有阈值参数均通过配置文件管理,修改方便。
  • 安全可靠:提供配置校验脚本,避免因配置错误导致脚本异常。
  • 监控全面:覆盖CPU、内存、磁盘、IO、网络带宽等核心指标。
  • 告警及时:支持邮件告警,并可扩展其他告警方式。
  • 减少误报:网络带宽监控采用"多次采样、双重判断"策略,大大降低了误报的可能性。
  • 日志完善:正常日志和错误日志分离,便于问题排查。

该系统轻量、高效、易于部署,非常适合中小型服务器环境的监控需求。用户可根据实际环境调整监控阈值和告警策略,进一步提升系统的适用性和可靠性。

相关推荐
七夜zippoe6 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
盟接之桥6 小时前
盟接之桥说制造:引流品 × 利润品,全球电商平台高效产品组合策略(供讨论)
大数据·linux·服务器·网络·人工智能·制造
孤岛悬城6 小时前
37 日志分析:ELK(Elasticsearch+Logstash+Kibana)
云计算
Fcy6487 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满7 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠8 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
主机哥哥8 小时前
阿里云OpenClaw部署全攻略,五种方案助你快速部署!
服务器·阿里云·负载均衡
Harvey9038 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
珠海西格电力科技9 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市
释怀不想释怀9 小时前
Linux环境变量
linux·运维·服务器