适用场景:几十到几百台 Linux 服务器批量下发配置/二进制/公钥、禁用冲突服务、做变更前 dry-run 验证
环境说明:控制端直接调用 ansible,受控端走 SSH 免密
核心认知:Ansible 不是"批敲命令",是"先想清楚影响范围 → 灰度验证 → 再全量推"
一、背景:为什么要有这套习惯
运维到一定规模,手工 SSH 一台台登录,就有三个问题:
- 慢:50 台机器改一个配置,登到手酸
- 不一致:A 机器改了 B 机器漏了,排查时怀疑人生
- 不敢动:没演练过,怕一条命令把生产网络干挂
Ansible 的价值不在"能批量执行",而在它能让你先模拟、再灰度、最后全量------这套节奏才是生产环境敢用的原因。
二、控制端环境准备
直接用系统安装的 Ansible 即可:
bash
ansible --version
项目目录建议这样组织:
ansible-project/
├── ansible.cfg
├── inventory/
│ ├── gray # 灰度机组
│ ├── test_gpu # 测试 GPU 组
│ ├── now_gpu # 当前批次 GPU
│ ├── disk_gpu # 磁盘巡检组
│ ├── dk_test # 驱动测试组
│ ├── remain_gpu # 剩余 GPU 组
│ └── hosts.ini # 总表
├── playbooks/
│ ├── push_conf.yml
│ ├── disable_nm.yml
│ └── push_pubkey.yml
└── files/
├── my.conf
└── my_binary
ansible.cfg 最小可用:
bash
[defaults]
inventory = ./inventory/gray
remote_user = root
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
forks = 50
⚠️
host_key_checking = False只在内网可信环境用,公网机器建议先把指纹写进 known_hosts。
三、第一步永远是连通性巡检
任何批量操作前,先 ping 一遍,把不可达的机器捞出来:
全量连通性检查
bash
ansible -i inventory/gray All -m ping
把不可达机器单独落盘,后面单独处理
bash
ansible -i inventory/gray All -m ping | grep 'UNREACHABLE!' | awk '{print $1}' > unreachable_hosts.txt
看下有多少台不通
bash
wc -l unreachable_hosts.txt
为什么这步不能省:
- 免密没配好的机器,后面 playbook 跑一半报错,输出里混着成功失败很难看。
- 新扩容机器没登过,known_hosts 没指纹,直接跑会卡住。
- 先把不可达的摘出来单修,再跑全量,心里有数。
四、灰度分批:多套 inventory 是核心
别一上来全量推。按风险等级分 inventory:
| inventory | 用途 | 机器规模 |
|---|---|---|
gray |
灰度验证,先推 3~5 台 | 极小 |
test_gpu |
测试集群,验证业务兼容 | 小 |
now_gpu |
当前批次生产机 | 中 |
disk_gpu |
只做磁盘类变更的分组 | 按需求 |
dk_test |
驱动/内核类变更前验证 | 小 |
remain_gpu |
前几批跑完后的剩余全量 | 大 |
执行节奏:
- 先跑灰度
bash
ansible-playbook -i inventory/gray push_conf.yml --check
ansible-playbook -i inventory/gray push_conf.yml
- 再跑测试集群
bash
ansible-playbook -i inventory/test_gpu push_conf.yml
- 确认无异常,跑当前批次
bash
ansible-playbook -i inventory/now_gpu push_conf.yml
- 最后剩余全量
bash
ansible-playbook -i inventory/remain_gpu push_conf.yml
五、变更前先 dry-run(这条最值钱)
Ansible 的 --check(简写 -C)模式不会在远端做任何实际修改,支持 check mode 的模块会报告"将会发生什么变化",不支持的模块则不动作也不报。
5.1 基本用法
只模拟,不修改任何内容
bash
ansible-playbook -i inventory/now_gpu push_conf.yml --check
配合 --diff 查看具体修改了哪几行
bash
ansible-playbook -i inventory/now_gpu push_conf.yml --check --diff
只验证单台主机
bash
ansible-playbook -i inventory/now_gpu push_conf.yml --check --diff --limit 192.168.1.11
5.2 为什么这个习惯能救命
- 配置文件路径写反了 → dry-run 阶段就能看到目标路径不对
- 模板变量没渲染 → diff 一看就知道
- 会把服务重启错节点 →
--limit先扔一台上试 - 条件判断依赖上一步结果 → check mode 是模拟,复杂条件链还是要上灰度机真跑一次
💡 经验法则:任何 playbook 上线前,不管多简单,先
--check再--limit单台,最后才全量。这条习惯比会写多少 module 都重要。
5.3 Check Mode 的边界(必须知道))
- 不是所有 Module 都支持 Check Mode,不支持的就什么都不做也不报报- 依赖"上一步先创建目录再写文件"的剧本,Dry-Run 可能报不出真实错误误
- 所以
--check代替不了灰度机真跑,两者是互补关系
六、常见批量任务实战
6.1 批量下发配置文件
Ad-hoc 方式式
bash
ansible -i inventory/now_gpu All -m copy \
-a "src=./files/my.conf dest=/etc/myapp/my.conf mode=0644 owner=root group=root backup=yes" \
--become
Playbook 方式(推荐,可复用))
push_conf.yml
• name: push my.conf
hosts: All
become: yes
tasks:
◦ name: backup and copy conf
ansible.builtin.copy:
src: ../files/my.conf
dest: /etc/myapp/my.conf
mode: '0644'
owner: root
group: root
backup: yes
notify: reload myapp
handlers:
◦ name: reload myapp
ansible.builtin.systemd:
name: myapp
state: reloaded
backup=yes / backup: yes 一定加上------改挂了还能退回到 Ansible 自动备份的那个时间戳文件。
6.2 批量下发 RPM / 二进制制
推 RPM 包包
bash
ansible -i inventory/now_gpu All -m copy \
-a "src=./packages/xxx.rpm dest=/tmp/xxx.rpm" --become
推完再装
bash
ansible -i inventory/now_gpu All -m yum \
-a "name=/tmp/xxx.rpm state=present" --become
推二进制到 /usr/local/bin
bash
ansible -i inventory/now_gpu All -m copy \
-a "src=./files/my_binary dest=/usr/local/bin/my_binary mode=0755 owner=root group=root" \
--become
6.3 批量下发公钥(给新机器开免密)
控制端先有公钥。
用 authorized_key 模块,比手动追安全得多。多
push_pubkey.yml
• name: ensure ansible control pubkey present
hosts: All
become: yes
tasks:
◦ name: add control pubkey
ansible.posix.authorized_key:
user: root
state: present
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
6.4 批量执行巡检命令
查看所有机器的 uptimee
bash
ansible -i inventory/now_gpu All -m command -a "uptime"
查看磁盘盘
bash
ansible -i inventory/disk_gpu All -m shell -a "df -h grep -E 'Use
90%'"
查看某个进程是否存在在
bash
ansible -i inventory/now_gpu All -m shell -a "ps aux grep mmnetverify
grep -v grep"
运行自研校验脚本本
bash
ansible -i inventory/now_gpu All -m command -a "/usr/local/bin/netverify"
七、真实场景:批量禁用 NetworkManager
7.1 为什么要禁
在跑分布式文件系统 / 高性能网络 / 多网卡绑定的服务器上,NetworkManager 和传统的 network 服务容易抢网络接口的管理权,常见表现:
- IP 配置被 NM 覆盖
- 接口频繁 up/down
- 路由表被改、DNS 被重置
- 重启后网络起不来,报
RTNETLINK answers: File exists
这类环境里通常选关掉 NM,让传统 network 或 systemd-networkd 接管。
7.2 批量禁用 playbook
disable_nm.yml
• name: disable NetworkManager for stable networking
hosts: All
become: yes
tasks:
◦ name: stop NetworkManager now
ansible.builtin.systemd:
name: NetworkManager
state: stopped
ignore_errors: yes
◦ name: disable NetworkManager on boot
ansible.builtin.systemd:
name: NetworkManager
enabled: no
masked: no
ignore_errors: yes
◦ name: make sure network service is up
ansible.builtin.systemd:
name: network
state: started
enabled: yes
ignore_errors: yes
7.3 执行顺序(关键)
- 先 dry-run
bash
ansible-playbook -i inventory/gray disable_nm.yml --check
- 灰度机真跑,重启验证网络不丢
bash
ansible-playbook -i inventory/gray disable_nm.yml
ssh 192.168.1.10 "reboot" # 观察能不能正常起来、IP 在不在
- 再推全量
bash
ansible-playbook -i inventory/now_gpu disable_nm.yml
⚠️ 关 NM 前一定确认:受控端有其他管理方式能拿回网络(比如带外 BMC / 控制台)。纯 SSH 关 NM 如果配置不对,重启后可能连不上。
八、幂等:Ansible 和批量 Shell 脚本的本质区别别
写 Ansible 最忌写成这样:
反例:非幂等,跑几次加几行
• name: add sudo rule
shell: echo "myuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
正确写法:
• name: ensure sudo rule present
ansible.builtin.lineinfile:
path: /etc/sudoers.d/myuser
line: "myuser ALL=(ALL) NOPASSWD:ALL"
create: yes
mode: '0440'
validate: "visudo -cf %s"
判断标准:同一个 playbook 跑第二遍,changed 数应为 0。不为 0 说明剧本有逻辑漏洞。
九、踩坑清单
| 现象 | 根因 | 解决 |
|---|---|---|
UNREACHABLE! 大量报错 |
免密没配 / 指纹没录 / 用户不对 | 先捞不可达清单单修,别硬推全量 |
--check 显示 ok,真跑却报错 |
有 task 不支持 check mode,或依赖前一步建目录 | 必须再上灰度机真跑一次 |
| 改完配置服务没生效 | 没写 handler / 没 notify | 用 notify + handlers 触发 reload |
| 第二次跑还是 changed | 剧本非幂等 | 换用 lineinfile / template 等声明式模块 |
| 关了 NetworkManager 重启断网 | 传统 network 没配好或接口没释放 | 先留 BMC 通道,灰度机重启验证 |
| 批量执行特别慢 | forks 太小 / SSH 超时 | 调大 forks=50,设 timeout=30 |
| 敏感变量明文提交到 git | 没用 ansible-vault | 密钥类统一 ansible-vault 加密 |
| 新机器 Python 找不到 | 没装 python3 或解释器路径不对 | inventory 里加 ansible_python_interpreter=/usr/bin/python3 |
十、小结
Ansible 批量运维的生产级节奏就五步:
- 先 ping:把不可达机器捞出来单独处理
- 分 inventory:灰度 / 测试 / 当前批 / 剩余全量,分层推进
- 必 dry-run :
--check --diff --limit单台先验证,再去掉 --check 真跑 - 写幂等:用 module 声明终态,别用 shell 硬怼
- 高危操作留后路 :
backup=yes、关 NM 前留 BMC、回滚命令先想好
Ansible 入门看的是 module 怎么写,成熟看的是变更纪律------先想清楚搞砸了怎么退回来,再动手推全量。
有问题的在评论区贴你的 playbook 和 --check 输出,我看到会回。