0Pricing
Linux Command Line & Bash Scripting Mastery · 课时

构建系统健康检查与告警脚本

收集负载、内存和磁盘指标,并从定时运行的脚本触发基于阈值的告警。

构建系统健康检查与告警脚本 是 CoddyKit 上的免费 Linux Command Line & Bash Scripting Mastery 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Linux Command Line & Bash Scripting Mastery 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。

系统健康检查为何重要

生产服务器可能会在不易察觉的情况下逐渐出现问题。CPU 峰值、内存泄漏和磁盘空间耗尽都会导致服务中断——但前提是没有人及时发现。系统健康检查脚本可以自动完成监控闭环:收集指标、与阈值比较,并在用户受到影响之前触发告警。

  • 通过 cron 定期执行,每隔几分钟运行一次,无需人工关注
  • 生成一致且带时间戳的输出,适合进行日志聚合
  • 基于阈值的逻辑可以让告警保持有效——并非每次短暂波动都会通知值班团队

在本课中,您将从零开始逐层构建一个适用于生产环境的健康检查脚本,涵盖平均负载、内存压力和磁盘使用率。

获取平均负载

Linux 通过 /proc/loadavg 和 uptime 命令提供 1 分钟、5 分钟和 15 分钟的平均负载。对于脚本而言,/proc/loadavg 是最简洁可靠的数据源——不会受到语言环境影响,不同发行版之间的解析方式也没有差异。

下面的代码片段读取 1 分钟平均负载,并将其存储在变量中,以便与阈值进行比较。cut 提取第一个字段;awk 去除小数部分,以便使用 bc 进行浮点运算后完成整数比较。

#!/usr/bin/env bash
# Read 1-minute load average from /proc/loadavg
LOAD_RAW=$(cut -d' ' -f1 /proc/loadavg)
echo "Raw load average: $LOAD_RAW"

# Number of CPU cores — used to normalise load
CPU_CORES=$(nproc)
echo "CPU cores: $CPU_CORES"

# Compute load percentage (load / cores * 100) using bc
LOAD_PCT=$(echo "scale=2; $LOAD_RAW / $CPU_CORES * 100" | bc)
echo "Load %: $LOAD_PCT"

使用浮点数进行阈值比较

Bash 原生无法比较浮点数——[ 1.5 -gt 1.2 ] 会抛出错误。惯用的解决方案有两种:

  • bc——根据比较表达式输出 1(真)或 0(假)
  • awk——可以在处理流程中计算浮点条件

使用 bc 可以让逻辑更易读,也更容易测试。模式 $(echo "$A > $B" | bc) 在条件成立时返回 1,您可以使用 [ ... -eq 1 ] 对其进行判断。

#!/usr/bin/env bash
LOAD_RAW=$(cut -d' ' -f1 /proc/loadavg)
CPU_CORES=$(nproc)
THRESHOLD=80  # alert when load % exceeds 80%

LOAD_PCT=$(echo "scale=2; $LOAD_RAW / $CPU_CORES * 100" | bc)

# bc returns 1 if the expression is true
if [ "$(echo "$LOAD_PCT > $THRESHOLD" | bc)" -eq 1 ]; then
    echo "ALERT: Load is ${LOAD_PCT}% (threshold ${THRESHOLD}%)"
else
    echo "OK: Load is ${LOAD_PCT}%"
fi

收集内存指标

/proc/meminfo 是 Linux 上内存统计信息的权威来源。主要字段包括:

  • MemTotal——以 kB 为单位的物理 RAM 总量
  • MemAvailable——无需进行交换即可用于新分配的估计可用 kB 数,比 MemFree 更有参考价值

使用带模式匹配的 awk 是提取这些值的最简洁方式。将 MemAvailable 除以 MemTotal,再用 100 减去结果,即可得到已用内存百分比,该数值用于触发告警阈值。

#!/usr/bin/env bash
# Extract memory figures from /proc/meminfo (values in kB)
MEM_TOTAL=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
MEM_AVAIL=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)

# Used memory percentage
MEM_USED_PCT=$(echo "scale=2; (1 - $MEM_AVAIL / $MEM_TOTAL) * 100" | bc)

echo "Total RAM : ${MEM_TOTAL} kB"
echo "Available : ${MEM_AVAIL} kB"
echo "Used      : ${MEM_USED_PCT}%"

收集磁盘使用率指标

df 命令会报告文件系统使用情况。对于脚本而言,以下两个标志非常重要:

  • -h——以便于人阅读的形式显示大小(仅用于显示;算术运算中应避免使用)
  • --output=pcent,target——输出便于程序解析的列(GNU coreutils)

遍历所有已挂载的文件系统,可以让脚本标记任何空间严重不足的分区,而不仅仅是 /。在进行整数比较前,使用 tr -d '%' 去除 % 符号。

#!/usr/bin/env bash
DISK_THRESHOLD=85

# Skip header line with tail -n +2
# --output=pcent,target gives "85% /var" style lines
df --output=pcent,target | tail -n +2 | while read -r USED_PCT MOUNT; do
    # Remove the % sign for arithmetic
    USED_INT=${USED_PCT//%/}

    if [ "$USED_INT" -ge "$DISK_THRESHOLD" ]; then
        echo "ALERT: Disk $MOUNT is ${USED_PCT} full"
    else
        echo "OK   : Disk $MOUNT is ${USED_PCT} full"
    fi
done

带时间戳的结构化告警输出

没有时间戳的告警消息在日志文件或电子邮件报告中几乎没有用处。统一的前缀可以让使用 grep 或日志传输代理解析日志变得非常简单。

在脚本开头定义一个简短的告警函数。该函数会添加 ISO-8601 时间戳、严重性级别和检查名称。所有告警都会通过 tee 同时写入 stdout 和日志文件。

  • date -u +"%Y-%m-%dT%H:%M:%SZ"——与语言环境无关的 UTC 时间戳
  • 将 ALERT 写入 stderr,将 OK 写入 stdout,以便在处理流程中将重要信号与噪声分开
#!/usr/bin/env bash
LOG_FILE="/var/log/healthcheck.log"

alert() {
    local LEVEL="$1"   # OK | WARN | ALERT
    local CHECK="$2"
    local MSG="$3"
    local TS
    TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
    local LINE="[$TS] [$LEVEL] [$CHECK] $MSG"

    if [ "$LEVEL" = "ALERT" ]; then
        echo "$LINE" | tee -a "$LOG_FILE" >&2
    else
        echo "$LINE" | tee -a "$LOG_FILE"
    fi
}

# Usage examples
alert "OK"    "DISK"  "/ is 42% full"
alert "ALERT" "DISK"  "/var is 91% full"

使用 mail 和 sendmail 发送电子邮件告警

服务器上最简单的告警机制,是通过本地 MTA(postfix、sendmail 或 msmtp)发送电子邮件。mail 命令(来自 mailutils 或 bsd-mailx)可以用一行命令完成邮件编写和发送。

  • -s——主题行
  • 通过 stdin 传递正文
  • 对于没有本地 MTA 的服务器,请将 mail 替换为调用事务性电子邮件 API 的 curl 命令

请使用去重锁保护发送操作,避免某个嘈杂的状况向收件箱发送大量重复告警。

#!/usr/bin/env bash
ALERT_EMAIL="ops@example.com"
LOCK_DIR="/tmp/healthcheck_locks"
mkdir -p "$LOCK_DIR"

send_alert() {
    local CHECK="$1"
    local MSG="$2"
    local LOCK="$LOCK_DIR/${CHECK}.lock"

    # Only send if no lock exists (prevents repeated emails within the hour)
    if [ ! -f "$LOCK" ]; then
        echo "$MSG" | mail -s "[ALERT] $CHECK on $(hostname)" "$ALERT_EMAIL"
        touch "$LOCK"
        # Lock expires after 1 hour via cron or find+delete
        echo "Alert sent for $CHECK"
    else
        echo "Alert suppressed for $CHECK (lock active)"
    fi
}

send_alert "HIGH_LOAD" "Load average exceeded 80% on $(hostname) at $(date)"

组合完整的健康检查脚本

现在,将负载、内存和磁盘这三项检查组合到一个完整统一的脚本中,并在顶部设置可配置的阈值。这是生产环境系统管理自动化中常用的模式:

  • 在顶部声明常量,便于调整参数而无需修改逻辑
  • 将每项检查封装在独立函数中,提升可读性并便于进行单元测试
  • 由 main 函数统一协调各项调用
  • 如果触发了任意警报则返回退出代码 1,否则返回 0——这样脚本就能与 Nagios/Icinga 等监控框架组合使用
#!/usr/bin/env bash
set -euo pipefail

# ── Thresholds ───────────────────────────────────────────
LOAD_THRESHOLD=80   # percent of CPU capacity
MEM_THRESHOLD=90    # percent used
DISK_THRESHOLD=85   # percent used
ALERT_EMAIL="ops@example.com"
LOG_FILE="/var/log/healthcheck.log"
ALERT_FIRED=0

# ── Helpers ──────────────────────────────────────────────
ts()    { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
log()   { echo "[$(ts)] $*" | tee -a "$LOG_FILE"; }
alert() { log "ALERT: $*"; echo "$*" | mail -s "[ALERT] $(hostname)" "$ALERT_EMAIL" 2>/dev/null; ALERT_FIRED=1; }

# ── Checks ───────────────────────────────────────────────
check_load() {
    local raw cores pct
    raw=$(cut -d' ' -f1 /proc/loadavg)
    cores=$(nproc)
    pct=$(echo "scale=2; $raw / $cores * 100" | bc)
    if [ "$(echo "$pct > $LOAD_THRESHOLD" | bc)" -eq 1 ]; then
        alert "Load ${pct}% exceeds ${LOAD_THRESHOLD}%"
    else
        log "OK load=${pct}%"
    fi
}

check_memory() {
    local total avail pct
    total=$(awk '/^MemTotal:/    {print $2}' /proc/meminfo)
    avail=$(awk '/^MemAvailable:/{print $2}' /proc/meminfo)
    pct=$(echo "scale=2; (1 - $avail / $total) * 100" | bc)
    if [ "$(echo "$pct > $MEM_THRESHOLD" | bc)" -eq 1 ]; then
        alert "Memory ${pct}% used (threshold ${MEM_THRESHOLD}%)"
    else
        log "OK memory=${pct}%"
    fi
}

check_disk() {
    df --output=pcent,target | tail -n +2 | while read -r used mnt; do
        local pct_int=${used//%/}
        if [ "$pct_int" -ge "$DISK_THRESHOLD" ]; then
            alert "Disk $mnt at ${used}"
        else
            log "OK disk $mnt=${used}"
        fi
    done
}

main() {
    log "=== Health check START ==="
    check_load
    check_memory
    check_disk
    log "=== Health check END (alerts=$ALERT_FIRED) ==="
    exit "$ALERT_FIRED"
}

main

使用 Cron 调度

健康检查脚本只有在自动运行时才能发挥价值。cron 是 Unix 系统的标准调度程序。请编辑系统范围的 crontab,或编辑 /etc/cron.d/ 中的专用文件来调度脚本。

  • 每 5 分钟运行一次:*/5 * * * *
  • 在 cron 中始终使用绝对路径——cron 环境中的 $PATH 很精简
  • 重定向输出,避免 cron 每次运行都发送邮件:>> /var/log/healthcheck.log 2>&1
  • 在 crontab 顶部使用 MAILTO="",关闭 cron 自带的邮件通知
# /etc/cron.d/healthcheck
# Run the health check every 5 minutes as root
MAILTO=""
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

*/5 * * * * root /usr/local/sbin/healthcheck.sh >> /var/log/healthcheck.log 2>&1

使用冷却锁防止警报风暴

当某个阈值持续超标时,简单脚本会每 5 分钟触发一次警报——工程师还来不及响应,就可能收到几十封邮件。冷却锁可以在可配置的时间窗口内抑制重复警报。

具体模式如下:首次触发警报时写入锁文件;只要该文件的更新时间距现在尚未超过冷却时长,就跳过后续警报;使用 find 配合 -mmin 原子地检查文件的存续时间,无需进行日期计算。

#!/usr/bin/env bash
LOCK_DIR="/tmp/hc_locks"
COOLDOWN_MIN=60  # suppress repeat alerts for 60 minutes
mkdir -p "$LOCK_DIR"

should_alert() {
    local check="$1"
    local lock="$LOCK_DIR/${check}.lock"

    if [ ! -f "$lock" ]; then
        # No lock — allow alert and create lock
        touch "$lock"
        return 0  # true: send alert
    fi

    # Lock exists — check if it is older than the cooldown
    # find returns the filename only if it's OLDER than COOLDOWN_MIN
    local expired
    expired=$(find "$lock" -mmin +"$COOLDOWN_MIN" 2>/dev/null)

    if [ -n "$expired" ]; then
        touch "$lock"  # refresh lock timestamp
        return 0       # cooldown expired — allow alert
    fi

    return 1  # still within cooldown — suppress
}

# Usage
if should_alert "HIGH_LOAD"; then
    echo "Sending load alert..."
    # mail -s "..." ops@example.com <<< "Load too high"
else
    echo "Load alert suppressed (cooldown active)"
fi

测试和验证您的健康检查脚本

部署前,请通过以下三种方式验证脚本:

  • 语法检查:bash -n healthcheck.sh 会捕获解析错误,但不会执行脚本
  • 跟踪模式:bash -x healthcheck.sh 会在每条命令执行时将其打印出来,对调试非常有帮助
  • 覆盖阈值:暂时将阈值调低到接近零,使脚本在健康主机上也触发警报,从而确认整个警报流程正常工作

对于邮件流程,请在测试期间使用 MOCK_MAIL 标志将 mail 重定向到日志文件:

#!/usr/bin/env bash
# Smoke-test the alert path without sending real email
MOCK_MAIL=true
ALERT_EMAIL="ops@example.com"

send_mail() {
    local subject="$1"
    local body="$2"
    if [ "$MOCK_MAIL" = true ]; then
        echo "[MOCK MAIL] To: $ALERT_EMAIL | Subject: $subject"
        echo "[MOCK MAIL] Body: $body"
    else
        echo "$body" | mail -s "$subject" "$ALERT_EMAIL"
    fi
}

# Override threshold to guarantee an alert fires
LOAD_THRESHOLD=0   # Any load will exceed 0%
LOAD_RAW=$(cut -d' ' -f1 /proc/loadavg)
CPU_CORES=$(nproc)
PCT=$(echo "scale=2; $LOAD_RAW / $CPU_CORES * 100" | bc)

if [ "$(echo "$PCT > $LOAD_THRESHOLD" | bc)" -eq 1 ]; then
    send_mail "[ALERT] Load on $(hostname)" "Load is ${PCT}%"
fi

知识检查:冷却策略

请考虑以下场景:您的健康检查 cron 任务每 5 分钟运行一次。/var 的磁盘使用率超过 85%,并持续了 3 个小时。您希望值班工程师每小时收到一次警报,而不是每 5 分钟收到一次。哪种实现策略最合适?

课程回顾:系统健康检查脚本

在本课中,您构建了一个完整、可用于生产环境的系统健康检查与警报流水线。以下是需要牢记的关键原则:

  • 事实来源:从 /proc/loadavg 和 /proc/meminfo 读取指标——它们稳定、不受区域设置影响,并且每台 Linux 主机都提供这些文件
  • 浮点运算:使用 bc 进行浮点阈值比较;BASH 的整数比较(-gt)只能处理整数
  • 遍历磁盘:使用 df --output=pcent,target 检查每个已挂载的文件系统,而不只是 /
  • 结构化日志记录:在每行前添加 UTC 时间戳和严重级别,以便使用 grep 进行筛选,并顺利发送到集中式日志系统
  • 冷却锁:使用 find -mmin 检查的锁文件可以防止警报风暴,而无需更改 cron 调度
  • 可组合性:触发任意警报时以代码 1 退出,使脚本能够与 Nagios、Icinga 或其他监控框架集成
  • 测试:使用 bash -n 进行语法检查,使用 bash -x 进行跟踪调试,并使用 MOCK_MAIL 标志在健康主机上验证警报流程

通过 /etc/cron.d/ 调度完成的脚本,您的基础设施就能持续进行自我监控,并且只在阈值明显超标时发出警报。

常见问题解答

「构建系统健康检查与告警脚本」课时是免费的吗?

是的 — 「构建系统健康检查与告警脚本」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Linux Command Line & Bash Scripting Mastery 课程的其余内容,请升级到 CoddyKit PRO。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。

「构建系统健康检查与告警脚本」这节课中我会学到什么?

收集负载、内存和磁盘指标,并从定时运行的脚本触发基于阈值的告警。 你通过在浏览器中直接运行的动手代码来练习 Linux Command Line & Bash Scripting Mastery,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Linux Command Line & Bash Scripting Mastery 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Linux Command Line & Bash Scripting Mastery 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「构建系统健康检查与告警脚本」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Linux Command Line & Bash Scripting Mastery 课中编写并运行代码吗?

能。每节 Linux Command Line & Bash Scripting Mastery 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 自动化用户与用户组配置
  2. 控制 systemd 服务并编写单元文件
  3. 磁盘、文件系统与挂载自动化
  4. 构建系统健康检查与告警脚本
← 返回 Linux Command Line & Bash Scripting Mastery