要在 30 台服务器上改一个 Nginx 配置,手工做法是打开 30 个终端窗口,每台都执行 vi、nginx -t、systemctl reload,耗时半小时且必然有漏改、改错的机器。更糟的是,当第 25 台改错导致服务起不来时,你还得一台台回滚。运维自动化的价值从来不只是"省时间",而是把"人肉操作的不确定性"换成"可重复、可验证、可回滚的流程"。但自动化不是一蹴而就的,它有清晰的演进层次。本文按"批量执行 → 脚本化 → 配置管理 → 任务编排 → 自愈闭环"五个阶段,讲清楚每个阶段该做什么、用什么工具、会遇到什么坑,帮你在合适的规模选择合适的技术方案。
一、自动化的五个层次,别跳级
很多团队一上来就想上 Kubernetes、上 GitOps,结果基础设施没准备好,反而更乱。正确的做法是按规模循序渐进。
| 层次 | 适用规模 | 典型做法 | 核心收益 | 主要风险 |
|---|---|---|---|---|
| L1 手工 | 1-3 台 | SSH 逐台操作 | 无成本 | 易漏、易错、不可复现 |
| L2 批量执行 | 3-30 台 | SSH 免密 + for 循环 / pssh | 分钟级完成批量操作 | 无幂等,失败难定位 |
| L3 脚本化 | 10-50 台 | 带判断与日志的 Bash/Python 脚本 | 可复现、有检查点 | 脚本质量依赖个人 |
| L4 配置管理 | 30-500 台 | Ansible / SaltStack / Puppet | 幂等、状态收敛、可审计 | 学习成本、需维护清单 |
| L5 编排与自愈 | 任意规模 | AWX / Rundeck / Jenkins / K8s Operator | 流程编排、自动恢复、审计留痕 | 平台自身也要维护 |
判断自己该在哪一层的标准很简单:当你开始用 Excel 记录"哪些机器改过了",就该升级了。另外,L2/L3 的能力即使在上了 Ansible 之后依然有用——排查问题时临时在几台机器上跑一条命令,比写 playbook 快得多。两者是互补而非替代关系。
二、第一层:SSH 免密 + 批量执行
批量执行的前提是打通 SSH 免密登录。做法是在控制机生成密钥对,把公钥分发到所有被管机器的 ~/.ssh/authorized_keys。
# 1. 在控制机生成专用密钥(不要复用个人密钥,也不要给空密码以外的场景用无密码密钥)
ssh-keygen -t ed25519 -f ~/.ssh/ops_key -C "ops-automation"
# 2. 批量分发公钥(ssh-copy-id 会处理目录权限,比手动 cat >> 可靠)
for ip in 10.0.0.11 10.0.0.12 10.0.0.13; do
ssh-copy-id -i ~/.ssh/ops_key.pub -o StrictHostKeyChecking=no root@$ip
done
# 3. 配置 ~/.ssh/config,免去每次指定密钥与参数
cat >> ~/.ssh/config <<'EOF'
Host ops-*
User root
IdentityFile ~/.ssh/ops_key
StrictHostKeyChecking no
ConnectTimeout 5
ServerAliveInterval 30
EOF
# 4. 最简批量执行:一条命令跑遍所有机器
for ip in $(cat hosts.txt); do
echo "===== $ip ====="
ssh -o ConnectTimeout=5 root@$ip 'uptime; df -h / | tail -1' 2>&1
done
上面的 for 循环是串行的,机器多了会很慢。两个改进方向:一是用 & 后台并发 + wait 等待;二是直接用专业工具 pssh(parallel-ssh)。
# 并发版:每台机器后台执行,输出落到独立文件,避免交错错乱 mkdir -p /tmp/outs for ip in $(cat hosts.txt); do ( ssh root@$ip 'uptime' > /tmp/outs/$ip.txt 2>&1 || echo "FAILED: $ip" >> /tmp/outs/failed.txt ) & done wait cat /tmp/outs/*.txt # 安装 pssh(CentOS: yum install pssh,Debian: apt install pssh) # -h 主机文件 -i 显示输出 -t 超时秒 -l 用户名 -p 并发数 pssh -h hosts.txt -l root -i -t 10 -p 20 'uptime' pssh -h hosts.txt -l root -i -t 10 -p 20 'yum install -y sysstat' # 批量传文件 pscp.pssh -h hosts.txt -l root -t 10 /etc/hosts /etc/hosts
批量执行有三个必须养成的习惯:第一,命令前先跑一次只读探测(如 uptime),确认连通性与机器清单正确;第二,控制并发数(-p 20),一次性对几百台机器同时建连接可能把控制机或网络打满;第三,ConnectTimeout 必须设,否则遇到一台死机会卡住整个循环,这是最常见的事故——脚本跑了 20 分钟没动静,最后发现卡在第一台不可达的机器上。
三、第二层:脚本化与幂等性
把一串命令写成脚本只是起点,真正让脚本可靠的特性叫幂等性(idempotence):无论执行几次,结果都一样,且不会因为重复执行而出错。对比一下两种写法:
# 非幂等:重复执行会追加多行,最终配置文件里出现 N 条相同记录 echo "10.0.0.11 node1" >> /etc/hosts # 幂等:先检查,已存在就跳过 grep -q "10.0.0.11 node1" /etc/hosts || echo "10.0.0.11 node1" >> /etc/hosts # 非幂等:重复执行会重复添加用户,第二次直接报错退出 useradd deploy # 幂等:先判断存在性 id deploy &>/dev/null || useradd deploy # 非幂等:每次都重启服务 systemctl restart nginx # 幂等:仅在配置确实变更时才 reload cp nginx.conf /etc/nginx/nginx.conf nginx -t && systemctl reload nginx
幂等性带来的直接好处是"可以放心地反复执行"。这一点在批量场景极其关键:100 台机器里有 7 台失败了,修好后重跑脚本,幂等脚本只会补齐那 7 台,非幂等脚本会把另外 93 台再折腾一遍。另外,脚本开头要加上严格模式:
#!/bin/bash
set -euo pipefail # -e 出错即退出;-u 未定义变量报错;-o pipefail 管道中任一环节失败即失败
LOG=/var/log/ops/deploy.log
mkdir -p "$(dirname "$LOG")"
exec > >(tee -a "$LOG") 2>&1 # 输出同时写日志,便于事后审计
HOSTS=$(cat /opt/scripts/hosts.txt)
FAILED=""
for ip in $HOSTS; do
echo "[$(date '+%F %T')] 开始处理 $ip"
if ssh -o ConnectTimeout=5 root@$ip 'bash -s' < /opt/scripts/task.sh; then
echo "[OK] $ip"
else
echo "[FAIL] $ip"; FAILED="$FAILED $ip"
fi
done
echo "==== 汇总 ===="
[ -n "$FAILED" ] && echo "失败主机:$FAILED" || echo "全部成功"
set -euo pipefail 是 Bash 脚本最重要的第一行。没有它,一条命令失败后脚本会继续往下跑,产生"部分成功"的诡异状态,而这种状态最难排查。最后的"汇总"输出也别省——批量操作结束时必须明确知道哪几台失败了。
四、第三层:任务编排
当操作不再是"一条命令",而是"一系列有依赖、有顺序、需要中途判断的步骤"时,就进入了编排层。一个典型的发布流程包含:拉取代码 → 备份当前版本 → 停服务 → 替换文件 → 检查配置 → 启服务 → 健康检查 → 失败回滚。这些步骤之间有条件分支(健康检查失败就回滚),串行与并行混合(先逐台灰度,再全量)。
轻量级编排可以用 Shell 函数 + 显式步骤编号实现,不引入额外依赖:
#!/bin/bash
# deploy.sh —— 带灰度与回滚的编排示例
set -euo pipefail
VERSION=$1
CANARY_HOSTS=(10.0.0.11) # 灰度机
ALL_HOSTS=($(cat /opt/scripts/hosts.txt)) # 全量机
step_backup() {
ssh root@$1 "cp -a /opt/app /opt/app.bak.\$(date +%s)"
}
step_deploy() {
ssh root@$1 "cd /opt/app && git fetch && git checkout $VERSION"
ssh root@$1 "nginx -t && systemctl reload nginx"
}
step_healthcheck() {
# 连续 5 次探测接口,全部 200 才算健康
for i in {1..5}; do
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "http://$1/health" || echo 000)
[ "$code" = "200" ] || return 1
sleep 2
done
return 0
}
step_rollback() {
ssh root@$1 "rm -rf /opt/app && mv /opt/app.bak.* /opt/app && systemctl reload nginx"
}
deploy_one() {
local h=$1
step_backup "$h" && step_deploy "$h"
if step_healthcheck "$h"; then echo "[OK] $h"; return 0
else echo "[FAIL] $h 开始回滚"; step_rollback "$h"; return 1; fi
}
echo "===== 阶段一:灰度 ====="
for h in "${CANARY_HOSTS[@]}"; do
deploy_one "$h" || { echo "灰度失败,终止发布"; exit 1; } # 灰度失败即中止,不波及全量
done
echo "===== 阶段二:全量 ====="
for h in "${ALL_HOSTS[@]}"; do deploy_one "$h" || true; done
这个脚本体现了编排的三个核心要素:分阶段(灰度先行、失败即终止)、每步可验证(健康检查)、失败可回滚(备份 + 恢复)。规模再大一些,可以换成专业编排平台:Ansible(配置管理兼编排,见同组专文)、Rundeck/AWX(带 Web 界面、权限控制、执行历史与审计)、Jenkins/GitLab CI(适合与代码仓库绑定的发布流水线)。选择标准是:需要权限管控与审计留痕就上 Rundeck/AWX;与研发流程强绑定就用 CI;纯粹的服务器配置管理用 Ansible。
五、第四层:从自动化到自愈闭环
自动化的最高形态不是"人触发脚本",而是"系统自己发现问题并修复"。一个自愈闭环包含四步:检测(监控/探针发现异常)→ 决策(判断属于哪类已知问题)→ 执行(调用对应修复动作)→ 验证(确认恢复,失败则升级为人工告警)。
最易落地的自愈场景有:服务进程退出(systemd 的 Restart=always 自动拉起,配合 StartLimitBurst=5 防止无限重启)、磁盘日志暴涨(检测到超过阈值自动触发 logrotate 与旧日志清理)、连接数打满(自动重启连接池异常的应用)、证书即将过期(自动 certbot 续期)。
# /etc/systemd/system/myapp.service —— 最典型的自愈配置 [Unit] Description=MyApp After=network.target [Service] ExecStart=/usr/local/bin/myapp Restart=always # 任何退出都重启 RestartSec=5s # 重启前等待,避免日志刷屏 StartLimitBurst=5 # 300 秒内最多重启 5 次 StartLimitIntervalSec=300 # 超过上限后进入 failed 并触发 OnFailure 告警 [Install] WantedBy=multi-user.target
自愈的边界要划清楚:只允许自动执行"确定安全、可回滚、影响面可控"的动作。删除数据、修改数据库结构、扩容缩容这类操作不要放进自愈,否则一个小误判会放大成重大事故。自愈动作全部执行后必须发通知,让人知道系统做了什么。
六、推进自动化的实施步骤
- 梳理资产与操作清单:列出服务器角色、常见重复操作(部署、改配置、清日志、加用户)及各自频率,按频率排优先级。
- 打基础:统一 SSH 免密、统一主机名与 hosts 解析、统一时间同步(chrony)、统一软件源,让后续批量操作有稳定前提。
- 建主机清单文件并纳入版本管理:把
hosts.txt或 Ansible inventory 提交到 Git,任何变更都留痕。 - 先把最高频的一件事脚本化(通常是部署或清日志),脚本必须
set -euo pipefail且幂等。 - 给脚本加日志与汇总输出,跑三遍确认重复执行无副作用。
- 引入编排:加备份、健康检查、回滚三要素,先灰度后全量。
- 引入 Ansible 做配置管理(30 台以上),把"机器应该是什么状态"写成代码。
- 最后做自愈:只挑选 2-3 个最安全、最高频的场景自动化,观察一个月稳定后再扩展。
小结
服务器运维自动化不是买一个平台就完事,而是一条明确的演进路径:批量执行解决"快",脚本化与幂等解决"可重复",配置管理解决"状态一致",编排解决"多步骤流程的可靠执行",自愈解决"无人值守"。每一层都建立在前一层之上,跳级必然踩坑。落地时记住几条铁律:批量操作一定设 ConnectTimeout 并控制并发;脚本一定开 set -euo pipefail 并保证幂等;编排一定包含备份、健康检查、回滚三件套;自愈只做确定安全的动作且必须留通知。避坑清单:一是用无超时控制的 SSH 循环导致整体卡死;二是非幂等脚本重跑把正常机器搞乱;三是没有灰度,一次性全量发布导致故障面瞬间扩大;四是自动化脚本本身不纳入版本管理,几个月后没人知道线上跑的是哪个版本;五是自动化覆盖了操作但没覆盖验证,执行完了也不知道到底成功没有。把验证和回滚设计进去,自动化才真正可靠。
关键词:运维自动化、批量执行命令、pssh、任务编排、幂等性、发布回滚
企业QQ咨询




