当管理的服务器从几台扩展到几十台甚至上百台时,手工登录每台机器修改配置文件的方式就彻底行不通了。不仅耗时耗力,还极易出现某台机器漏改、改错的状况,而且事后很难追溯到底是谁在什么时间改了什么。Ansible正是为解决这类问题而生的批量配置管理工具,它通过SSH协议连接目标主机,将配置逻辑写成可版本化的Playbook,一次执行即可完成全量主机的配置下发。本文结合实际运维经验,详细介绍如何用Ansible搭建一套可靠、可复用的批量配置管理体系。

Ansible的核心工作原理与安装初始化
Ansible与Puppet、SaltStack等工具最大的区别在于它是无Agent架构。目标主机上不需要安装任何额外软件,只要满足两个条件即可:一是控制节点能够通过SSH连接到目标主机,二是目标主机上装有Python解释器(绝大多数Linux发行版自带)。控制节点在执行任务时,会把对应的模块代码通过SSH推送到目标主机,在目标主机本地执行完毕后将结果拉回来并删除临时文件。这种架构让Ansible的部署成本几乎为零,特别适合快速上手和临时性批量操作。
在控制节点上安装Ansible非常简单,以CentOS为例可以直接通过yum安装:
# 安装EPEL源后直接安装Ansible yum install -y epel-release yum install -y ansible # 验证安装结果 ansible --version
安装完成后,第一步要做的是打通SSH免密登录。用ssh-keygen生成密钥对,再通过ssh-copy-id把公钥分发到各台目标主机。这个环节经常被新手忽略,如果每台主机都需要输密码,批量执行就失去了意义。密钥分发完成后,可以用ansible all -m ping做连通性测试,全部返回pong即表示环境就绪。
Inventory主机清单的编写技巧
Inventory是Ansible管理主机的核心配置,默认位于/etc/ansible/hosts。一个组织良好的Inventory不仅列出了主机地址,更体现了业务的分组逻辑。建议按业务功能和环境维度进行分组,例如把Web服务器、数据库服务器分开,同时区分生产环境和测试环境,这样在执行任务时可以精确控制影响范围。下面是一个典型的Inventory示例:
# 按业务功能分组 [web] 192.168.1.11 192.168.1.12 [db] 192.168.1.21 # 按环境分组,引用其他组的成员 [prod:children] web db # 定义组变量,优先级高于全局变量 [web:vars] ansible_user=root ansible_port=22
除了静态文件,Ansible还支持动态Inventory。当主机由云平台管理、地址经常变化时,可以编写脚本从云厂商API拉取主机列表,Ansible会在每次执行前自动调用该脚本获取最新的主机信息。这在混合云场景下非常实用,避免了手工维护清单带来的滞后问题。另外, Inventory中的变量可以分层定义:全局变量放在group_vars和host_vars目录中,组变量针对一组主机生效,主机变量优先级最高。合理的变量分层能让Playbook更加通用。
Playbook与常用模块的配置实践
Playbook是Ansible的灵魂,它用YAML格式描述了一系列要在目标主机上执行的任务。配置管理的核心思路是声明式:你告诉Ansible期望的目标状态,而不是具体的操作步骤。例如用copy模块下发一个配置文件,如果文件内容与目标主机一致,Ansible不会做任何变更,直接返回ok;只有存在差异时才会覆盖并返回changed。这种幂等性设计保证了Playbook可以反复执行而不会产生副作用。
下面是一个修改SSH配置并重启服务的完整Playbook示例:
---
- name: 批量加固SSH配置
hosts: web
become: yes
vars:
ssh_port: 2222
tasks:
- name: 修改SSH监听端口
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?Port '
line: 'Port {{ ssh_port }}'
notify: restart sshd
- name: 禁止root远程登录
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PermitRootLogin '
line: 'PermitRootLogin no'
notify: restart sshd
- name: 确保firewalld放行新端口
firewalld:
port: '{{ ssh_port }}/tcp'
permanent: yes
immediate: yes
state: enabled
handlers:
- name: restart sshd
service:
name: sshd
state: restarted这段Playbook中有几个值得注意的细节。首先是lineinfile模块配合regexp参数,可以精确匹配并替换配置文件中的某一行,即使该行被注释掉也能正确处理。其次是notify与handlers的配合:只有当任务真正产生了变更(返回changed),才会触发handler中定义的重启动作。如果配置文件本来就符合预期,服务不会被无谓地重启,这对生产环境的稳定性至关重要。多个任务触发同一个handler时,重启也只会执行一次。
日常配置管理中还有几个高频模块需要掌握:template模块使用Jinja2模板渲染配置文件,适合内容需要动态计算的复杂配置;file模块管理文件属性、创建目录或软链接;user和group模块管理账号;package模块则屏蔽了yum和apt的差异,实现了跨发行版的软件包安装。选择模块时优先考虑专用模块而不是shell或command,因为前者天生具备幂等性判断,而后者每次执行都会被判定为变更。
用Roles组织复杂配置与执行控制
当配置项越来越多,把所有任务塞进一个Playbook会让文件臃肿到难以维护。Ansible提供了Roles机制,按照固定目录结构拆分任务、变量、模板和处理器。一个典型的Role包含tasks、handlers、templates、vars、defaults等目录,各司其职。以部署Nginx为例,可以拆成common、nginx、webapp三个Role,不同的服务组合引用不同的Role,复用性大幅提升。
# 使用ansible-galaxy初始化Role目录结构 ansible-galaxy init roles/nginx # 在Playbook中引用多个Role # roles: # - common # - nginx
执行控制方面有几个实用技巧。一是善用--check参数做模拟执行,Ansible会预测每个任务是否会产生变更但不真正落地,这对上线前的变更评估非常有价值。二是使用--tags只执行带特定标签的任务,在大型Playbook中可以快速完成单项调整。三是通过--limit限定主机范围,先在一台机器上灰度验证,确认无误后再全量执行。对于特别敏感的操作,还可以开启--step单步执行模式,每个任务执行前人工确认。
最后要强调配置管理的可追溯性。把Inventory、Playbook和Roles全部纳入Git版本管理,任何配置变更都通过提交代码、评审后再执行的方式落地,配合ansible-playbook执行时的详细输出日志,就能完整回答谁在什么时间改了哪些主机的什么配置这个问题。这套流程搭好之后,批量配置管理就从高风险的手工操作转变为可控、可审计、可回滚的自动化流水线,运维效率自然会有质的提升。