5.8.5 批量运维与远程执行脚本

批量运维与远程执行脚本用于在多台主机上统一执行命令、分发文件与采集信息。核心思路:维护主机清单,使用 SSH 无交互登录,结合并发控制、超时与重试、结果汇总与审计,实现可观测、可回溯的批量操作。

原理草图(流程与数据流):

文章图片

关键要素:
- 主机清单与分组:按环境/角色/业务分组,支持排除与临时追加
- 认证与安全:基于密钥、最小权限账户、命令白名单
- 并发与限流:控制并发数,避免雪崩与网络拥塞
- 结果收集:标准输出/错误输出分离,成功失败统计与重试
- 审计与回滚:命令记录、操作人标识、回滚脚本与验证

安装与准备(示例环境):

# 1) 安装必备工具
sudo yum -y install openssh-clients rsync

# 2) 生成密钥并分发
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
for h in node01 node02 node03; do
  ssh-copy-id -i ~/.ssh/id_ed25519.pub ops@$h
done

# 3) 主机清单文件
cat > hosts.txt <<'EOF'
ops@node01
ops@node02
ops@node03
EOF

示例一:并发执行命令并汇总结果(含命令解释)

#!/usr/bin/env bash
# 文件:/opt/ops/batch_cmd.sh
# 用法:/opt/ops/batch_cmd.sh "df -h"
HOSTS=/opt/ops/hosts.txt
CMD="$*"
PARALLEL=5
TIMEOUT=5

mkdir -p /opt/ops/out

run_one() {
  host="$1"
  # -o BatchMode=yes 禁止密码交互;-o ConnectTimeout 连接超时
  ssh -o BatchMode=yes -o ConnectTimeout=$TIMEOUT "$host" "$CMD" \
    > "/opt/ops/out/${host//\//_}.out" \
    2> "/opt/ops/out/${host//\//_}.err"
  echo "$host:$?"
}

export -f run_one
cat "$HOSTS" | xargs -n1 -P "$PARALLEL" -I{} bash -c 'run_one "$@"' _ {}

执行与预期效果:

/opt/ops/batch_cmd.sh "uptime"
# 预期:/opt/ops/out/ 下生成每台主机 .out/.err 文件,终端输出 host:0 表示成功

示例二:分发文件并校验(含MD5校验)

#!/usr/bin/env bash
# 文件:/opt/ops/push_conf.sh
HOSTS=/opt/ops/hosts.txt
SRC=/etc/app/app.conf
DST=/etc/app/app.conf
MD5_LOCAL=$(md5sum "$SRC" | awk '{print $1}')

while read -r host; do
  scp -q "$SRC" "$host:$DST" && \
  ssh "$host" "md5sum $DST | awk '{print \$1}'" | \
  awk -v h="$host" -v m="$MD5_LOCAL" '{print h,($1==m)?"OK":"FAIL"}'
done < "$HOSTS"

示例三:批量检查服务状态并生成重试列表

#!/usr/bin/env bash
# 文件:/opt/ops/check_service.sh
HOSTS=/opt/ops/hosts.txt
SERVICE=nginx
RETRY=/opt/ops/retry.txt

: > "$RETRY"
while read -r host; do
  if ssh "$host" "systemctl is-active $SERVICE" >/dev/null 2>&1; then
    echo "$host $SERVICE OK"
  else
    echo "$host $SERVICE FAIL"
    echo "$host" >> "$RETRY"
  fi
done < "$HOSTS"

echo "重试列表:$RETRY"

示例四:可观测的并发执行(带超时与重试)

#!/usr/bin/env bash
# 文件:/opt/ops/batch_retry.sh
HOSTS=/opt/ops/hosts.txt
CMD="hostname && date"
PARALLEL=5
TIMEOUT=5
RETRY=2

run_one() {
  host="$1"
  for i in $(seq 1 $RETRY); do
    ssh -o BatchMode=yes -o ConnectTimeout=$TIMEOUT "$host" "$CMD" \
      > "/opt/ops/out/${host//\//_}.out" \
      2> "/opt/ops/out/${host//\//_}.err" && \
      echo "$host:OK($i)" && return 0
    sleep 1
  done
  echo "$host:FAIL"
  return 1
}

export -f run_one
mkdir -p /opt/ops/out
cat "$HOSTS" | xargs -n1 -P "$PARALLEL" -I{} bash -c 'run_one "$@"' _ {}

命令与关键参数解释:
- ssh -o BatchMode=yes:禁用密码交互,避免脚本卡住
- ssh -o ConnectTimeout=5:连接超时秒数
- xargs -P 5:并发数为 5
- scp -q:安静模式,减少无关输出
- md5sum:校验文件一致性

常见错误与排错:
1. 连接失败/超时
- 现象:.err 中出现 Connection timed out
- 处理:
bash ssh -o ConnectTimeout=5 node01 ping -c 3 node01

  1. 权限不足
    - 现象:.err 中出现 Permission denied
    - 处理:
    bash ssh-copy-id ops@node01 # 或者配置 sudoers sudo visudo # 添加:ops ALL=(root) NOPASSWD:/bin/systemctl

  2. 并发过高导致失败
    - 现象:部分节点失败,重试成功
    - 处理:
    bash # 降低并发 PARALLEL=2

  3. 输出过大导致终端卡顿
    - 处理:按主机落盘输出,必要时启用压缩
    bash gzip -f /opt/ops/out/*.out

最佳实践:
- 先执行只读巡检,再进行变更
- 脚本内置确认与干跑模式(--dry-run)
- 关键路径操作配合告警与变更窗口
- 将操作记录与结果汇总到统一日志目录

练习:
1. 编写脚本将 /etc/hosts 分发到所有主机并校验 MD5。
2. 批量检查 sshd 服务状态,失败主机写入 retry.txt 并自动重试 2 次。
3. 扩展并发脚本,增加 --dry-run 参数,仅打印待执行命令而不执行。