Ansible批量更新多台服务器系统补丁
多台Linux服务器需要打补丁时,逐台登录执行命令既慢又容易漏。
用Ansible批量更新多台服务器系统补丁,可以把"登录、检查、升级、重启"这套动作一次性发到所有机器。
本文面向零基础用户,从Inventory配置讲到Playbook执行,最后给出验证方法,照着做就能完成一轮补丁更新。
开始之前:控制机、被管机和免密登录
Ansible采用无Agent架构,只需要一台控制机(安装Ansible的机器)和若干被管机(要打补丁的服务器)。
控制机通过SSH连接被管机执行任务,所以第一步是把SSH打通。
控制机上安装Ansible,不同发行版命令略有差异,建议以官方文档为准:
# Debian/Ubuntu
apt update && apt install -y ansible
# RHEL/CentOS/Rocky/AlmaLinux
dnf install -y epel-release && dnf install -y ansible
安装后确认版本:
ansible --version
接着配置免密登录。
如果控制机还没有密钥对,先生成:
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
把公钥分发到每台被管机(首次仍需输入密码):
ssh-copy-id root@192.168.1.101
多台机器可以写个循环,或者直接用Ansible的authorized_key模块。
免密成功后,用ssh root@192.168.1.101能直接进入,说明通道正常。
编写Inventory:告诉Ansible要管哪些机器
Inventory是主机清单文件,默认路径/etc/ansible/hosts,也可以自己指定。
建议在项目目录下建一个,方便版本管理:
[web]
192.168.1.101
192.168.1.102
[db]
192.168.1.201
[all:vars]
ansible_user=root
ansible_ssh_private_key_file=~/.ssh/id_ed25519
ansible_python_interpreter=/usr/bin/python3
ansible_python_interpreter建议显式指定,避免被管机上Python路径不一致导致模块执行失败。
保存为inventory.ini后,先做一次连通性测试:
ansible -i inventory.ini all -m ping
如果每台都返回pong,说明控制机和被管机之间的连接、认证、Python环境都正常。这一步是后续所有操作的前提,不要跳过。
用临时命令先做一次快速更新
如果只是想快速跑一轮补丁,不打算写成固定脚本,可以用ansible临时命令。
按被管机发行版选择包管理器。
Debian/Ubuntu系:
ansible -i inventory.ini all -m apt -a "update_cache=yes upgrade=dist" --become
RHEL/CentOS/Rocky/AlmaLinux系:
ansible -i inventory.ini all -m dnf -a "name=* state=latest" --become
--become表示提权执行,对应sudo。
如果被管机用普通用户登录,需要该用户有sudo权限,并在Inventory或命令行里提供--ask-become-pass。
临时命令适合验证流程是否跑得通,缺点是无法控制"更新前检查、更新后重启、失败重试"这类顺序逻辑。
真正生产环境更推荐Playbook。
写成Playbook:把补丁更新流程固定下来
Playbook是YAML格式的任务剧本,可以把多个步骤编排在一起。
下面这份适合Debian/Ubuntu系,包含更新缓存、升级、清理和条件重启:
---
- name: 批量更新系统补丁
hosts: all
become: true
serial: 2
tasks:
- name: 更新apt缓存
apt:
update_cache: yes
cache_valid_time: 3600
- name: 执行安全升级
apt:
upgrade: dist
register: upgrade_result
- name: 清理无用包
apt:
autoremove: yes
autoclean: yes
- name: 需要时重启
reboot:
msg: "Ansible触发的补丁更新重启"
reboot_timeout: 600
when: upgrade_result.changed
serial: 2表示一次只处理2台,避免所有机器同时重启导致服务整体中断。when: upgrade_result.changed保证没有实际更新时不触发重启。
RHEL系把apt模块换成dnf或yum即可:
- name: 执行系统升级
dnf:
name: "*"
state: latest
register: upgrade_result
保存为patch.yml后执行:
ansible-playbook -i inventory.ini patch.yml
执行过程中Ansible会输出每台机器的任务状态,ok表示无变化,changed表示有改动,failed表示出错。先在小批量机器上跑通,再扩大范围。
常见报错和处理思路
补丁更新过程中最常遇到的几类问题:
- SSH连接失败:先单独
ssh到目标机确认,再检查Inventory里的IP、端口、用户名和密钥路径。 - sudo权限不足:被管机用户需要在
/etc/sudoers或/etc/sudoers.d/里有对应权限,否则--become会失败。 - 包管理器被锁:报错常见"Could not get lock",说明有另一个apt/dnf进程在运行,等它结束或排查是否有自动更新任务。
- 内核更新后未重启:补丁装上了但内核还是旧的,需要重启才生效。Playbook里的
reboot任务就是处理这种情况。 - Python缺失:部分精简系统没有python3,Ansible无法执行模块,需要先装上。
遇到failed时,加上-vvv可以看详细输出:
ansible-playbook -i inventory.ini patch.yml -vvv
确认补丁是否真的生效
更新跑完不等于补丁生效,需要验证。
可以从几个角度检查:
- 查看内核版本:
ansible -i inventory.ini all -m shell -a "uname -r" - 列出可升级包:
ansible -i inventory.ini all -m shell -a "apt list --upgradable"(Debian系)或dnf check-update(RHEL系) - 检查是否需要重启:
ansible -i inventory.ini all -m shell -a "test -f /var/run/reboot-required && echo YES || echo NO"
如果可升级列表为空、且重启标记为NO,说明这一轮补丁已经装好。每台机器都确认一遍,比只看Playbook回显更可靠。
几个容易踩的坑
第一,不要在业务高峰期执行重启类更新,尤其是数据库和网关机器,serial参数要调小。
第二,区分安全更新和全量更新。
全量dist-upgrade可能带入新特性甚至不兼容变更,生产环境建议先评估,必要时只跑安全源。
第三,保留回滚准备。
重要机器更新前做快照或备份关键配置,出问题能快速恢复。
第四,Inventory里的主机名和实际一致。
用IP和主机名混用容易导致SSH指纹或证书校验失败,统一一种方式更省心。
多台服务器打补丁的核心思路就是:把登录、更新、重启、验证这几步交给Ansible统一执行。
先在测试机跑通Playbook,再分批推到生产,配合serial和when控制节奏,就能把重复劳动变成一次可复用的操作。