Skip to content

系统监控脚本

本节实现一个完整的系统资源监控脚本,能够实时采集 CPU、内存、磁盘和网络信息,并在超出阈值时发出告警。

功能设计

  • 监控 CPU 使用率
  • 监控内存使用率
  • 监控磁盘使用率
  • 监控关键进程存活
  • 超阈值发送告警邮件或写日志
  • 支持定时循环运行

完整脚本

bash
#!/usr/bin/env bash
#
# 系统监控脚本
# 用法:./monitor.sh [--interval 秒数] [--log 日志文件]
#

set -euo pipefail

# ============================================================
# 配置区
# ============================================================
INTERVAL=60                          # 监控间隔(秒)
LOG_FILE="/var/log/system_monitor.log"
ALERT_EMAIL="admin@example.com"

# 告警阈值
CPU_THRESHOLD=80      # CPU 使用率阈值(%)
MEM_THRESHOLD=85      # 内存使用率阈值(%)
DISK_THRESHOLD=90     # 磁盘使用率阈值(%)

# 需要监控的进程
WATCH_PROCESSES=("nginx" "mysql" "redis-server")

# ============================================================
# 工具函数
# ============================================================
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

alert() {
    local message="$1"
    log "⚠️ 告警:$message"
    # 发送邮件(需要配置 mailutils)
    # echo "$message" | mail -s "系统告警 - $(hostname)" "$ALERT_EMAIL"
}

# ============================================================
# 监控函数
# ============================================================

# 获取 CPU 使用率
get_cpu_usage() {
    # 采样两次取平均值
    local cpu1 cpu2
    cpu1=$(grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$3+$4+$5)} END {print int(usage)}')
    sleep 1
    cpu2=$(grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$3+$4+$5)} END {print int(usage)}')
    echo $(( (cpu1 + cpu2) / 2 ))
}

# 获取内存使用率
get_mem_usage() {
    free | awk 'NR==2 {printf "%.0f", $3*100/$2}'
}

# 获取磁盘使用率(根分区)
get_disk_usage() {
    df / | awk 'NR==2 {print $5}' | tr -d '%'
}

# 检查进程是否存活
check_process() {
    local proc="$1"
    pgrep -x "$proc" > /dev/null 2>&1
}

# 监控 CPU
monitor_cpu() {
    local usage
    usage=$(get_cpu_usage)
    log "CPU 使用率:${usage}%"
    if (( usage > CPU_THRESHOLD )); then
        alert "CPU 使用率过高:${usage}%(阈值:${CPU_THRESHOLD}%)"
        # 输出 CPU 占用 TOP 5 进程
        log "CPU TOP 5 进程:"
        ps aux --sort=-%cpu | awk 'NR<=6 {print}' | tee -a "$LOG_FILE"
    fi
}

# 监控内存
monitor_memory() {
    local usage total used free_mem
    usage=$(get_mem_usage)
    total=$(free -m | awk 'NR==2 {print $2}')
    used=$(free -m | awk 'NR==2 {print $3}')
    free_mem=$(free -m | awk 'NR==2 {print $4}')

    log "内存:总计 ${total}MB | 使用 ${used}MB | 空闲 ${free_mem}MB | 使用率 ${usage}%"

    if (( usage > MEM_THRESHOLD )); then
        alert "内存使用率过高:${usage}%(阈值:${MEM_THRESHOLD}%)"
        log "内存 TOP 5 进程:"
        ps aux --sort=-%mem | awk 'NR<=6 {print}' | tee -a "$LOG_FILE"
    fi
}

# 监控磁盘
monitor_disk() {
    log "磁盘使用情况:"
    df -h | grep -v tmpfs | tee -a "$LOG_FILE"

    # 检查每个挂载点
    while IFS= read -r line; do
        local usage mount
        usage=$(echo "$line" | awk '{print $5}' | tr -d '%')
        mount=$(echo "$line" | awk '{print $6}')
        if (( usage > DISK_THRESHOLD )); then
            alert "磁盘分区 $mount 使用率过高:${usage}%(阈值:${DISK_THRESHOLD}%)"
        fi
    done < <(df | grep -v tmpfs | tail -n +2)
}

# 监控关键进程
monitor_processes() {
    for proc in "${WATCH_PROCESSES[@]}"; do
        if check_process "$proc"; then
            log "进程 $proc:运行中 ✓"
        else
            alert "进程 $proc 未运行!"
            # 可以在此添加自动重启逻辑
            # systemctl restart "$proc"
        fi
    done
}

# 系统负载
monitor_load() {
    local load1 load5 load15 cpu_cores
    read -r load1 load5 load15 _ < /proc/loadavg
    cpu_cores=$(nproc)
    log "系统负载:1分钟=${load1} 5分钟=${load5} 15分钟=${load15}(CPU核心数:${cpu_cores})"
}

# ============================================================
# 主循环
# ============================================================
main() {
    log "==================================================="
    log "系统监控启动 | 主机:$(hostname) | 间隔:${INTERVAL}秒"
    log "==================================================="

    while true; do
        log "--- 开始巡检 ---"
        monitor_cpu
        monitor_memory
        monitor_disk
        monitor_processes
        monitor_load
        log "--- 巡检完成 ---"
        sleep "$INTERVAL"
    done
}

# 处理参数
while [[ $# -gt 0 ]]; do
    case "$1" in
        --interval) INTERVAL="$2"; shift 2 ;;
        --log)      LOG_FILE="$2"; shift 2 ;;
        *) echo "未知参数:$1"; exit 1 ;;
    esac
done

main

使用方法

bash
# 赋予执行权限
chmod +x monitor.sh

# 直接运行(默认每 60 秒监控一次)
./monitor.sh

# 自定义间隔和日志文件
./monitor.sh --interval 30 --log /tmp/monitor.log

# 后台运行
nohup ./monitor.sh > /dev/null 2>&1 &

# 配合 cron 使用(每 5 分钟运行一次)
# */5 * * * * /path/to/monitor.sh

扩展思路

  • 集成钉钉/企业微信 Webhook 发送告警消息
  • 将监控数据写入 InfluxDB 并用 Grafana 展示
  • 增加网络流量监控(/proc/net/dev
  • 增加 TCP 连接数监控(ss -s