结论: 一套可用的服务器批量管理方案由五部分组成——统一的主机清单(inventory)与分组、免密且可审计的接入通道(密钥分发 + 堡垒机)、并行执行引擎(Ansible 或 pssh)、配置一致性校验机制、以及灰度执行与回滚设计。缺少任何一环,批量操作都会从"效率工具"变成"批量事故制造机"。
引言:批量管理的风险与收益是同比例的
批量管理的收益很明显:一条命令在 100 台机器上生效,把 3 小时的工作压缩到 30 秒。但风险同样是 100 倍的——一条写错的命令也会在 100 台机器上生效,把一次小失误放大成全站故障。
我见过最典型的批量事故是:某人想清理 /tmp 下的旧文件,写成 rm -rf /tmp/old 却在某台机器上变量未展开变成 rm -rf /tmp/;还有人批量下发 sudoers 配置,语法错误导致所有机器的 sudo 全部失效,最后只能一台台走救援模式。这些事故的共同点不是工具用错了,而是缺少"批量操作的约束机制"。
所以本文讲的不只是"怎么批量执行",而是完整方案:从主机清单与分组开始(管谁),到接入通道(怎么连),到执行引擎(怎么跑),到一致性校验(跑完对不对),再到灰度与回滚(错了怎么办)。文中的命令以 Rocky Linux 9 / Ubuntu 24.04 LTS 为例,截至 2026 年 Ansible、pssh、堡垒机仍是主流选择。
一、主机清单与分组:批量管理的地基
批量管理的第一步不是装工具,而是建立权威的主机清单。没有清单,一切批量操作都是"凭记忆敲 IP",出错只是时间问题。
1.1 inventory 的分层设计
Ansible 的 inventory 支持"分组 + 变量 + 嵌套",足以表达绝大多数生产拓扑:
# inventory/prod.ini
# 按角色分组
[web]
web[01:20] ansible_host=10.0.1.{{ 10 + groups.web.index(inventory_hostname) }}上面这种动态写法可读性差,生产环境更推荐显式列举 + 按环境/角色双维度分组:
# inventory/prod.ini
[web]
web01 ansible_host=10.0.1.11
web02 ansible_host=10.0.1.12
web03 ansible_host=10.0.1.13
[app]
app01 ansible_host=10.0.2.11
app02 ansible_host=10.0.2.12
[db]
db01 ansible_host=10.0.3.11
db02 ansible_host=10.0.3.12
[cache]
cache01 ansible_host=10.0.4.11
# 按地域二次分组(用于灰度)
[web_az1]
web01
web02
[web_az2]
web03
# 组嵌套:prod 包含所有生产机器
[prod:children]
web
app
db
cache
# 生产通用变量
[prod:vars]
ansible_user=opsuser
ansible_port=2222
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_common_args='-o StrictHostKeyChecking=accept-new'
# 数据库组专属变量
[db:vars]
mysql_port=3306
backup_retention_days=7分组的三个维度建议同时具备:按角色(web/app/db)、按环境(prod/staging/test)、按地域或可用区(az1/az2,用于分批)。这样灰度时可以用 --limit web_az1 精确控制爆炸半径。
1.2 清单的维护方式
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
静态文件(inventory.ini) | 服务器数量稳定、变更少 | 简单直观,易 review | 需手工维护,易与实际脱节 |
| 动态 inventory 脚本 | 云环境、弹性伸缩 | 自动发现新增机器 | 需开发,调试成本 |
| CMDB 系统 | 中大型团队 | 权威数据源,可关联业务信息 | 建设成本高 |
| 云厂商 API 插件 | 全量用云主机 | 零维护,实时准确 | 混合云场景覆盖不足 |
关键建议:无论用哪种方式,inventory 必须进 Git 并做 code review。 清单本身是基础设施的一部分,改错一个 IP 就可能导致操作打偏到别的机器。
二、接入通道:密钥分发与堡垒机
2.1 免密 SSH 的批量分发
批量执行的前提是控制端能免密登录所有机器。分发公钥的做法:
# 1. 在控制端生成专用密钥(不要用个人密钥)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_ansible -N '' -C "ansible-control"
# 2. 批量分发(首次需输入密码,用 sshpass 批量完成)
dnf install -y sshpass # Ubuntu: apt install -y sshpass
while read -r host; do
sshpass -p '<初始密码>' ssh-copy-id \
-i ~/.ssh/id_ed25519_ansible.pub \
-o StrictHostKeyChecking=no -o ConnectTimeout=5 \
opsuser@"$host"
done < hosts.txt
# 3. 验证连通性
ansible all -m ping -i inventory/prod.ini --private-key ~/.ssh/id_ed25519_ansible# 4. 目标机上的 sudo 免密配置(控制批量操作能否执行特权命令)
# /etc/sudoers.d/opsuser (务必用 visudo 语法校验)
cat > /etc/sudoers.d/opsuser <<'EOF'
opsuser ALL=(ALL) NOPASSWD:/usr/bin/systemctl,/usr/bin/dnf,/usr/bin/apt,/usr/sbin/nginx
EOF
chmod 440 /etc/sudoers.d/opsuser
visudo -cf /etc/sudoers.d/opsuser # 校验语法,配错会导致 sudo 全废安全权衡:NOPASSWD:ALL 方便但风险极高(拿到 opsuser 就等于 root)。建议按命令白名单授权,只放行确实需要的命令。
2.2 堡垒机(跳板机)架构
服务器数量超过 20 台、或涉及多人协作与合规要求时,应引入堡垒机(Bastion Host / Jump Server)。
| 方案类型 | 代表 | 特点 | 适用 |
|---|---|---|---|
| 开源堡垒机 | JumpServer、Teleport | 支持录屏审计、权限审批、资产分组 | 中小企业首选,中文支持好 |
| 云厂商堡垒机 | 各家云控制台产品 | 托管、免运维、与云账号打通 | 全量上云且预算允许 |
| SSH ProxyJump | 原生 SSH -J | 零成本、无审计能力 | 临时、小规模 |
| VPN + 直连 | WireGuard/OpenVPN | 网络层收敛 | 与堡垒机互补 |
原生 SSH 的跳板写法(小规模可用):
# ~/.ssh/config
Host bastion
HostName 203.0.113.10
User opsuser
Port 2222
IdentityFile ~/.ssh/id_ed25519
Host web* app* db*
User opsuser
Port 2222
IdentityFile ~/.ssh/id_ed25519
ProxyJump bastion
# 之后可直接 ssh web01,自动经堡垒机跳转
ssh web01堡垒机带来的三个不可替代的价值:统一入口(生产机不再对公网开放 SSH)、操作审计(录屏与命令留痕,满足合规要求)、权限管控(谁能访问哪台机器、需要谁审批)。这三点在等保合规与离职审计时是硬需求。
三、执行引擎:Ansible 与 pssh 的分工
3.1 Ansible:复杂编排与幂等变更
适合"下发配置、装软件、改状态"这类需要幂等和可重复的操作。
# 批量执行(ad-hoc)
ansible web -m shell -a "nginx -v" -i inventory/prod.ini
ansible all -m shell -a "uptime" -i inventory/prod.ini -o # -o 单行输出,便于对比
# 批量分发文件
ansible web -m copy -a "src=./hosts dest=/etc/hosts mode=0644 backup=yes" -i inventory/prod.ini --become
# 批量执行脚本(先 copy 再执行,比直接 script 模块更可控)
ansible web -m script -a "./check_disk.sh" -i inventory/prod.ini
# 收集结果并保存
ansible all -m shell -a "df -h /" -i inventory/prod.ini -o > /tmp/df_report.txt3.2 pssh / pdsh:极简的并行执行
Ansible 启动有固定开销(建立连接、收集 facts),对"只在 200 台机器上跑一条命令看看"这种场景,pssh 更轻快。
# 安装
dnf install -y pssh # 包名在 RHEL 系为 pssh,命令为 pssh/pscp/pslurp
# Ubuntu: apt install -y pssh
# 准备主机文件(格式:user@host:port)
cat > hosts_pssh.txt <<'EOF'
opsuser@10.0.1.11:2222
opsuser@10.0.1.12:2222
opsuser@10.0.1.13:2222
EOF
# 并行执行(-i 交互输入密码,-l 用户,-h 主机文件,-t 超时,-p 并发数)
pssh -h hosts_pssh.txt -l opsuser -i -t 30 -p 20 "uptime"
pssh -h hosts_pssh.txt -l opsuser -i -t 60 -p 10 "systemctl restart nginx"
# 并行上传
pscp -h hosts_pssh.txt -l opsuser ./nginx.conf /tmp/nginx.conf
# 并行下载(收集各机日志)
pslurp -h hosts_pssh.txt -l opsuser -L /tmp/collected /var/log/messages messages
# 输出目录控制(-o 保存输出到目录,便于事后逐台查看)
pssh -h hosts_pssh.txt -l opsuser -o /tmp/pssh_out -t 30 "df -h | grep '/$'"
ls /tmp/pssh_out# pdsh 用法(另一个常见选择,支持 genders 分组)
pdsh -w ssh:opsuser@web[01-20] "uptime"
pdsh -g web "hostname; uptime" # 需配置 genders 文件两者的分工建议:
| 场景 | 推荐工具 | 原因 |
|---|---|---|
| 装软件、下发配置、改状态 | Ansible | 幂等、可重复、有 --check 预览 |
| 快速在几百台上看一条命令 | pssh | 启动快、无 facts 收集开销 |
| 需要结构化收集结果(JSON) | Ansible | 输出可编程处理 |
| 临时批量重启服务 | pssh + 分批 | 简单直接 |
| 需要长期维护、进 Git | Ansible | 版本化、可 review |
四、配置一致性:批量管理的核心目标
批量管理的终极目标不是"能批量执行",而是所有机器配置一致。一致性需要可度量。
4.1 一致性校验方法
# 1. 批量收集关键配置指纹并对比
ansible all -m shell -a "md5sum /etc/nginx/nginx.conf /etc/ssh/sshd_config /etc/my.cnf 2>/dev/null" \
-i inventory/prod.ini -o | sort
# 2. 用 Ansible 的 diff 模式发现漂移(不实际修改)
ansible-playbook site.yml -i inventory/prod.ini --check --diff --limit web
# 3. 批量对比软件版本
ansible all -m shell -a "rpm -q nginx mysql-server 2>/dev/null || dpkg -l | grep -E 'nginx|mysql' | awk '{print \$2, \$3}'" \
-i inventory/prod.ini -o
# 4. 批量检查关键基线项
ansible all -m shell -a "systemctl is-enabled nginx; ss -lnt | grep -c ':80'; getenforce" \
-i inventory/prod.ini -o更好的做法是把一致性检查写成定期任务,输出成一张表:
| 检查项 | 期望值 | 实际差异机器 | 处理方式 |
|---|---|---|---|
| nginx.conf md5 | 与 Git 中一致 | web07 | 重跑 playbook 收敛 |
| Nginx 版本 | 1.26.x | 无 | — |
| sshd PermitRootLogin | no | web12, app03 | 重跑加固 playbook |
| /etc/hosts 条目 | 8 条 | db02(7 条) | 手工确认后收敛 |
4.2 收敛策略:定期重跑 vs 只变更时跑
- 只变更时跑(推模式常态):改了 playbook 才执行。优点是安全可控,缺点是期间发生的手工改动不会被发现。
- 定期收敛(推荐):配置一个定时任务(如每天凌晨)以
--check模式跑一遍并输出差异报告;对非核心配置可开启自动收敛(不加--check,直接让 Ansible 把状态拉回)。
# crontab:每天 03:00 检查配置漂移并邮件报告
0 3 * * * cd /opt/ansible && ansible-playbook site.yml -i inventory/prod.ini --check --diff 2>&1 | mail -s "配置漂移日报 $(date +\%F)" ops@example.com注意:自动收敛(不加 --check)有风险——如果有人手工改了配置且未同步 playbook,收敛会静默覆盖掉他的改动。建议先跑报告模式,确认差异来源后再决定是否自动收敛。
五、批量改密与密钥轮换
批量改密是常见的合规要求(如等保要求口令定期更换),也是最容易搞砸的操作之一。
5.1 批量改密的安全做法
# 生成随机密码(每台不同,避免一密通用)
openssl rand -base64 16
# Ansible 批量改密(用 vault 保管密码变量)
ansible-vault create vars/passwords.yml
# 内容示例:
# new_password_web: "<随机强口令>"# change_password.yml
---
- name: 批量修改用户口令
hosts: all
become: yes
vars_files:
- vars/passwords.yml
tasks:
- name: 修改 opsuser 口令
ansible.builtin.user:
name: opsuser
password: "{{ new_password | password_hash('sha512') }}"
update_password: always
- name: 强制下次登录修改(可选)
ansible.builtin.command: "chage -d 0 opsuser"# 执行(先灰度一台,确认能登录后再全量)
ansible-playbook change_password.yml -i inventory/prod.ini --limit web01 --ask-vault-pass
# 验证新密码可登录后再全量
ansible-playbook change_password.yml -i inventory/prod.ini --ask-vault-pass批量改密的三条铁律:一是先灰度一台并验证新密码能登录,再全量;二是保留一个已登录的会话不退出,防止全部锁死;三是密码必须各不相同并存入密码管理器或 Vault,一密通用等于一次泄露全盘失守。
5.2 SSH 密钥轮换
# 1. 生成新密钥对
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_new -N '' -C "rotated-$(date +%F)"
# 2. 先"追加"新公钥(不要先删旧的,避免失败后连不上)
ansible all -m authorized_key -a "user=opsuser key='{{ lookup('file', '~/.ssh/id_ed25519_new.pub') }}' state=present" \
-i inventory/prod.ini --become
# 3. 验证新密钥可登录
ansible all -m ping -i inventory/prod.ini --private-key ~/.ssh/id_ed25519_new
# 4. 确认无误后再删除旧公钥
ansible all -m authorized_key -a "user=opsuser key='{{ lookup('file', '~/.ssh/id_ed25519_old.pub') }}' state=absent" \
-i inventory/prod.ini --become这个"先加后删"的顺序是密钥轮换的标准做法,能确保在任意步骤失败时都有可用的登录通道。
5.3 清理失控的 authorized_keys
# 批量审计:列出所有机器上的 root 与 opsuser 公钥,找出未授权的
ansible all -m shell -a "echo '--- root ---'; cat /root/.ssh/authorized_keys 2>/dev/null; \
echo '--- opsuser ---'; cat /home/opsuser/.ssh/authorized_keys 2>/dev/null" \
-i inventory/prod.ini -o > /tmp/keys_audit.txt
# 与授权清单对比,找出多余公钥后精确删除
ansible web07 -m authorized_key -a "user=root key='ssh-ed25519 AAAA...旧公钥...' state=absent" --become六、灰度执行与回滚:批量操作的安全带
这是批量管理方案里最重要、也最容易被省略的一节。
6.1 灰度策略
| 策略 | 做法 | 适用 |
|---|---|---|
| 单机灰度 | --limit web01 | 所有变更的第一步 |
| 按组灰度 | --limit web_az1 → --limit web_az2 | 有可用区划分时 |
| 按百分比 | --forks 控制并发 + 分批 inventory | 大批量 |
| 金丝雀 | 指定 1 台作为金丝雀,观察 30 分钟 | 核心业务变更 |
# 标准灰度流程
# 第 1 步:预览
ansible-playbook site.yml -i inventory/prod.ini --check --diff
# 第 2 步:单机灰度
ansible-playbook site.yml -i inventory/prod.ini --limit web01
# 第 3 步:验证(业务健康检查 + 日志)
ansible web01 -m shell -a "systemctl is-active nginx && curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/healthz" -i inventory/prod.ini
ansible web01 -m shell -a "journalctl -u nginx --since '5 min ago' --no-pager | tail -20" -i inventory/prod.ini
# 第 4 步:观察 10~30 分钟后,按组分批全量
ansible-playbook site.yml -i inventory/prod.ini --limit web_az1
sleep 300
ansible-playbook site.yml -i inventory/prod.ini --limit web_az26.2 回滚设计
回滚能力必须在变更前就准备好,而不是出事后现想。三类回滚手段:
# 1. 配置回滚:下发时自动备份(Ansible copy/template 模块的 backup 参数)
- name: 下发配置并自动备份
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
backup: yes # 生成 nginx.conf.<时间戳>~ 备份文件
validate: 'nginx -t -c %s'# 2. Git 回滚:playbook 版本化,出问题 revert 后重跑
git revert HEAD && ansible-playbook site.yml -i inventory/prod.ini
# 3. 整机回滚:云快照/镜像(最彻底,但耗时最长)
# 变更前的标准动作:对将变更的机器打快照回滚的检查清单(变更前的准备项):
- [ ] 变更前是否已备份原配置文件?
- [ ] 回滚脚本是否已写好并测试过?
- [ ] 回滚预计耗时是否在可接受范围(对比 RTO)?
- [ ] 数据库变更是否有对应的向下兼容方案(回滚代码但数据结构已变,是最难的情况)?
- [ ] 谁有权决定回滚?决策链路是否明确?
6.3 批次控制与失败中止
# playbook 中加入批次与失败阈值控制
- hosts: web
serial: 2 # 每批 2 台,一批失败则中止后续批次
max_fail_percentage: 10
any_errors_fatal: true
become: yes
tasks:
- name: 变更任务
ansible.builtin.template:
src: app.conf.j2
dest: /etc/app/app.conf
notify: restart app
post_tasks:
- name: 健康检查
ansible.builtin.uri:
url: http://127.0.0.1:8080/healthz
status_code: 200
retries: 5
delay: 10
register: health
until: health.status == 200serial: 2 + max_fail_percentage + 健康检查的 until 重试,这三者的组合能让批量变更具备"自动熔断"能力——第一批出问题就不再继续,把爆炸半径限制在最小。
企业QQ咨询




