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
-
权限不足
- 现象:.err中出现Permission denied
- 处理:
bash ssh-copy-id ops@node01 # 或者配置 sudoers sudo visudo # 添加:ops ALL=(root) NOPASSWD:/bin/systemctl -
并发过高导致失败
- 现象:部分节点失败,重试成功
- 处理:
bash # 降低并发 PARALLEL=2 -
输出过大导致终端卡顿
- 处理:按主机落盘输出,必要时启用压缩
bash gzip -f /opt/ops/out/*.out
最佳实践:
- 先执行只读巡检,再进行变更
- 脚本内置确认与干跑模式(--dry-run)
- 关键路径操作配合告警与变更窗口
- 将操作记录与结果汇总到统一日志目录
练习:
1. 编写脚本将 /etc/hosts 分发到所有主机并校验 MD5。
2. 批量检查 sshd 服务状态,失败主机写入 retry.txt 并自动重试 2 次。
3. 扩展并发脚本,增加 --dry-run 参数,仅打印待执行命令而不执行。