Skip to content

16|日常运维脚本实例

前面学的语法、工具、错误处理,组合起来能写实用的运维脚本。本篇给几个真实会用到的脚本——巡检、备份、批量操作。每个脚本都按上一篇的生产脚本结构来写,能直接用或稍改即用。

一、巡检脚本

收集一组机器的基本状态,汇总成报告。这是值班最常见的"快速看一眼"工具。

bash
#!/usr/bin/env bash
set -euo pipefail

hosts=${1:-web-01 web-02 db-01}
report="/tmp/inspect-$(date +%Y%m%d-%H%M%S).txt"

log() { echo "[$(date '+%F %T')] $*"; }

log "开始巡检,目标: ${hosts}"

{
    echo "=== 巡检报告 $(date) ==="
    echo

    for host in $hosts; do
        echo "--- $host ---"
        if ssh -o ConnectTimeout=5 "$host" "
            echo \"主机名: \$(hostname)\";
            echo \"运行时间: \$(uptime)\";
            echo \"磁盘:\";
            df -h | grep -vE 'tmpfs|devtmpfs';
            echo \"内存:\";
            free -h;
            echo \"失败服务:\";
            systemctl --failed --no-pager 2>/dev/null || echo 无;
        " 2>/dev/null; then
            echo "  (采集完成)"
        else
            echo "  !! 连不上 $host"
        fi
        echo
    done
} | tee "$report"

log "报告已生成: $report"

要点:

  • set -euo pipefail 安全开头
  • 远程命令用 ConnectTimeout=5 防止卡死
  • tee 既显示又存文件
  • 连不上的机器不中断,记 !! 标记
  • 远程命令里的 $() 要转义成 \$(...),让远程 shell 展开

二、日志备份脚本

备份应用日志,按日期归档,保留最近 N 天。

bash
#!/usr/bin/env bash
set -euo pipefail

src_dir=${1:?用法: $0 <日志目录> [保留天数]}
keep_days=${2:-30}
backup_dir="/data/backup/logs"
date=$(date +%F)

log() { echo "[$(date '+%F %T')] $*" >&2; }

[[ -d "$src_dir" ]] || { log "错误: $src_dir 不是目录"; exit 1; }
mkdir -p "$backup_dir"

archive="${backup_dir}/$(basename "$src_dir")-${date}.tar.gz"

log "备份 $src_dir$archive"
if tar -czf "$archive" -C "$(dirname "$src_dir")" "$(basename "$src_dir")"; then
    log "备份成功: $(du -h "$archive" | cut -f1)"
else
    log "备份失败"
    exit 1
fi

# 清理过期备份
log "清理 ${keep_days} 天前的备份"
find "$backup_dir" -name "*.tar.gz" -mtime +"$keep_days" -delete
log "剩余备份: $(ls "$backup_dir" | wc -l) 个"

要点:

  • 参数校验 ${1:?...} 和目录检查
  • tar -C 目录 文件名 避免绝对路径导致 tar 报错
  • 备份成功后用 du 显示大小
  • find -mtime +N -delete 清理过期文件,放 cron 里定期跑

放进 cron 每天跑:

bash
0 2 * * * /usr/local/sbin/backup-logs.sh /var/log/myapp 30

三、批量执行脚本

对一组机器执行同一命令,收集结果。

bash
#!/usr/bin/env bash
set -euo pipefail

cmd=${1:?用法: $0 <命令> [主机...]}
shift
hosts=("$@")

[[ ${#hosts[@]} -gt 0 ]] || { echo "错误: 没有指定主机" >&2; exit 1; }

ok=0
fail=0
for host in "${hosts[@]}"; do
    if ssh -o ConnectTimeout=5 "$host" "$cmd" </dev/null; then
        echo "[OK] $host"
        ((ok++))
    else
        echo "[FAIL] $host" >&2
        ((fail++))
    fi
done

echo "成功 $ok 台,失败 $fail 台"
[[ $fail -eq 0 ]] || exit 1

要点:

  • 命令和主机分离,第一个参数是命令,剩下是主机
  • </dev/null 防止 ssh 卡在等输入
  • 逐台执行,成功失败分别计数
  • 有失败时退出码非 0,让调用方能判断
  • 输出 [OK]/[FAIL] 前缀,方便 grep

用法:

bash
./batch-exec.sh "systemctl restart nginx" web-01 web-02 web-03
./batch-exec.sh "df -h /" $(cat hosts.txt)

四、磁盘告警检查

检查磁盘使用率,超过阈值发告警。可放 cron 定期跑。

bash
#!/usr/bin/env bash
set -euo pipefail

threshold=${1:-80}
alert_email=${ALERT_EMAIL:-}

log() { logger -t disk-check "$*"; echo "$*" >&2; }

while read -r _ _ usage _ mount; do
    usage=${usage%\%}
    [[ "$usage" =~ ^[0-9]+$ ]] || continue
    [[ "$mount" =~ ^(tmpfs|devtmpfs|/dev/)$ ]] && continue

    if (( usage >= threshold )); then
        log "告警: $mount 使用率 ${usage}% 超过 ${threshold}%"
        [[ -n "$alert_email" ]] && \
            echo "$mount 使用率 ${usage}%" | mail -s "磁盘告警 $mount" "$alert_email"
    fi
done < <(df -h)

log "检查完成"

要点:

  • df -h 输出逐行处理,read 拆字段
  • usage%\% 用参数扩展去掉百分号
  • 跳过 tmpfs 等非真实磁盘
  • logger 写进 syslog 方便集中查
  • 可选邮件告警(环境变量控制)

放进 cron 每 10 分钟检查:

bash
*/10 * * * * ALERT_EMAIL=ops@xxx /usr/local/sbin/disk-check.sh 85

五、服务健康检查

检查一组服务是否正常,异常就尝试重启。

bash
#!/usr/bin/env bash
set -euo pipefail

services=(nginx mysqld redis)
log() { echo "[$(date '+%F %T')] $*"; }

for svc in "${services[@]}"; do
    if systemctl is-active --quiet "$svc"; then
        log "$svc 正常"
    else
        log "$svc 异常,尝试重启" >&2
        if systemctl restart "$svc"; then
            sleep 2
            if systemctl is-active --quiet "$svc"; then
                log "$svc 重启成功"
            else
                log "$svc 重启后仍异常,需人工介入" >&2
            fi
        else
            log "$svc 重启失败" >&2
        fi
    fi
done

要点:

  • 服务列表用数组,增删改一处
  • systemctl is-active --quiet 静默判断状态
  • 重启后再检查一次,确认真的恢复了
  • 重启失败不放弃,记录后继续检查下一个

六、脚本通用要点

这几个脚本有共同的模式:

  1. shebang + set:每个脚本开头都是 #!/usr/bin/env bash + set -euo pipefail
  2. 参数校验:必填参数用 ${1:?...},目录文件用 [ -d ] 检查
  3. 日志函数:定义 log() 函数统一输出格式
  4. 错误进 stderr>&2 让错误和正常输出分开
  5. 退出码:成功 0,失败非 0,让 cron 和调用方能判断
  6. 可配置:阈值、保留天数用参数或环境变量,不写死

写自己的运维脚本时套这个模式,脚本能跑、能维护、出错能查。

学会了之后

巡检、备份、批量执行、告警、健康检查——这几个脚本覆盖了日常运维的高频需求。把它们改成自己的环境(主机列表、目录路径、服务名),就能直接用。模式记住:安全开头、参数校验、日志函数、错误处理、退出码。

下一篇讲一个更复杂的实战——用 Shell 写幂等的初始化脚本。