公司从 10 台服务器扩张到 80 台,靠 for 循环加 SSH 已经撑不住了:改一次 Nginx 配置要写好几段判断、机器分组靠手工维护的文本、执行结果还得自己汇总。这时候就该引入 Ansible。Ansible 最大的优势是无 agent——被管机器上不需要装任何客户端,只要能 SSH 登录、有 Python 就行,控制机通过 SSH 把模块脚本推过去执行再取回结果。它用 YAML 描述"机器应该是什么状态",天然幂等,重复执行不会产生副作用。本文从主机清单(inventory)讲起,到临时命令(ad-hoc),再到 Playbook 的任务、处理器与变量,给你一条可以直接上手的完整路径。
一、先理解 Ansible 的四个核心概念
开始写命令之前,先把几个词对齐,否则看文档会一直懵。
- 控制节点(Control Node):装了 Ansible 的那台机器,你在这上面执行命令。Windows 不能做控制节点,用 Linux 或 macOS。
- 受管节点(Managed Node):被管理的服务器,只需 SSH + Python(现代发行版自带 Python 3)。
- Inventory(主机清单):定义"有哪些机器、如何分组、各有什么变量"的文件,默认
/etc/ansible/hosts。 - Module(模块):真正干活的最小单元,如
ping、copy、yum、service。Ansible 自带几千个模块,绝大多数需求不用写 Shell。 - Playbook:把多个模块调用按逻辑组织起来的 YAML 文件,是 Ansible 的主力形态。
与 SaltStack、Puppet 相比,Ansible 的取舍很清楚:优点是零 agent、学习曲线平缓、推送式执行即时生效;缺点是没有常驻 agent 就无法主动感知配置漂移(即有人手工改了机器配置,Ansible 不会自动发现并纠正,需要定时跑 playbook 来"收敛"),且机器数量到数千台时串行 SSH 会比较慢(可通过 forks 参数与 pipelining 优化)。
二、安装与 SSH 前置准备
# 控制机安装(CentOS/RHEL) yum install -y epel-release && yum install -y ansible # Debian/Ubuntu apt-get update && apt-get install -y ansible # 或使用 pip 安装最新版 pip3 install ansible ansible --version # 确认版本与配置文件路径(config file 一行) # 生成并分发 SSH 密钥 ssh-keygen -t ed25519 -f ~/.ssh/ansible_key -N '' for ip in 10.0.0.11 10.0.0.12 10.0.0.21; do ssh-copy-id -i ~/.ssh/ansible_key.pub root@$ip done # 受管机器需要有 Python(Ansible 靠它执行模块) # CentOS 7 若只有 python2,可临时指定:ansible_python_interpreter=/usr/bin/python2
Ansible 的配置文件查找顺序是:ANSIBLE_CONFIG 环境变量 → 当前目录 ansible.cfg → 家目录 ~/.ansible.cfg → /etc/ansible/ansible.cfg。强烈建议每个项目目录下放一份 ansible.cfg,这样配置随项目走,不会互相干扰。
# 项目目录下的 ansible.cfg [defaults] inventory = ./inventory.ini remote_user = root private_key_file = ~/.ssh/ansible_key host_key_checking = False # 关闭首次连接的指纹确认,批量场景必需 forks = 20 # 并发数,默认 5,机器多时调大 timeout = 10 log_path = ./ansible.log [ssh_connection] pipelining = True # 减少 SSH 往返,显著提速 ssh_args = -o ControlMaster=auto -o ControlPersist=60s
三、Inventory 主机清单:分组、变量与范围写法
Inventory 是 Ansible 的"资产表",支持 INI 和 YAML 两种格式,INI 更常用。方括号表示组名,组之间可以嵌套(用 :children)。
# inventory.ini # 1. 基础写法:IP 或可解析的主机名 10.0.0.11 10.0.0.12 # 2. 分组 [web] web01 ansible_host=10.0.0.11 web02 ansible_host=10.0.0.12 [db] db01 ansible_host=10.0.0.21 db02 ansible_host=10.0.0.22 # 3. 范围简写(等价于 10.0.0.11 ~ 10.0.0.20) [batch] 10.0.0.[11:20] # 4. 组嵌套:prod 包含 web 与 db 两个组 [prod:children] web db # 5. 组变量:[组名:vars] [web:vars] nginx_worker_processes=4 http_port=80 # 6. 主机变量(写在主机行后面) [cache] cache01 ansible_host=10.0.0.31 ansible_port=2222 ansible_user=ops # 7. 特殊连接参数 [jumper] jump01 ansible_host=10.0.0.99 ansible_python_interpreter=/usr/bin/python3
| 参数 | 作用 | 常见取值 |
|---|---|---|
| ansible_host | 实际连接地址 | IP 或域名 |
| ansible_port | SSH 端口 | 22(默认)、2222 等 |
| ansible_user | 登录用户 | root、ops |
| ansible_ssh_private_key_file | 私钥路径 | ~/.ssh/ansible_key |
| ansible_become | 是否提权 | true / false |
| ansible_python_interpreter | Python 路径 | /usr/bin/python3 |
常用命令验证清单是否正确:ansible-inventory --list 输出完整结构(JSON);ansible-inventory --graph 以树状展示分组关系;ansible all --list-hosts 列出匹配到的主机。改完清单先跑这三个命令确认,比直接执行任务安全。
四、Ad-hoc 临时命令:最快的上手方式
Ad-hoc 指"临时敲一条命令就执行",适合快速查看状态、做一次性变更。语法是 ansible <目标> -m <模块> -a '<参数>',不写 -m 时默认是 command 模块。
# 连通性测试:Ansible 的"Hello World",成功返回 pong ansible all -m ping ansible web -m ping # 查看所有机器的系统信息(facts) ansible all -m setup | head -50 ansible web -m setup -a 'filter=ansible_distribution*' # 远程执行命令:command 模块不经过 shell,不支持管道与变量 ansible all -m command -a 'uptime' # 需要管道、重定向、变量时用 shell 模块 ansible all -m shell -a 'df -h / | tail -1' ansible web -m shell -a 'ps aux --sort=-%cpu | head -5' # 批量分发文件 ansible web -m copy -a 'src=/opt/conf/nginx.conf dest=/etc/nginx/nginx.conf backup=yes' # 批量装包 ansible web -m yum -a 'name=sysstat state=present' # RHEL/CentOS ansible web -m apt -a 'name=sysstat state=present' # Debian/Ubuntu # 批量管理服务 ansible web -m service -a 'name=nginx state=reloaded' ansible web -m systemd -a 'name=nginx state=restarted enabled=yes' # 批量建用户 ansible all -m user -a 'name=deploy shell=/bin/bash groups=wheel append=yes' # 串行执行,一次只跑 1 台(滚动操作,避免服务同时中断) ansible web -m shell -a 'systemctl restart nginx' --forks 1 # 只在一台上执行(滚动更新常用) ansible web -m shell -a 'uptime' --limit web01
command 与 shell 的区别是新手必踩的坑:command 更安全更幂等,但不解析 shell 语法,写 df -h | tail 会报错;shell 走 /bin/sh,支持管道但要注意幂等性。能用专用模块(copy、yum、service、user)就别用 shell,因为专用模块自带幂等判断和变更检测,shell 每次执行都会报告 changed。
五、Playbook:把操作写成可复用的代码
Playbook 是 Ansible 的真正形态。一个 Playbook 包含一到多个 play,每个 play 由 hosts(作用目标)、vars(变量)、tasks(任务列表)、handlers(触发器)组成。下面是一个部署 Nginx 的完整示例。
# site.yml
---
- name: 部署并配置 Nginx
hosts: web # 对应 inventory 里的组
become: yes # 提权到 root(等价 sudo)
gather_facts: yes # 收集系统信息,后面可用 ansible_distribution 等变量
serial: 2 # 每次只在 2 台机器上执行,实现滚动发布
vars:
http_port: 80
worker_processes: 4
tasks:
- name: 安装 Nginx
yum: # Debian 系改用 apt
name: nginx
state: present
tags: install
- name: 分发主配置文件
template: # template 支持 Jinja2 变量渲染,copy 不支持
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c %s' # 写入前先校验,校验失败不替换
notify: reload nginx # 文件发生变更时触发 handler
tags: config
- name: 创建站点目录
file:
path: /data/www
state: directory
owner: nginx
mode: '0755'
- name: 启动并设置开机自启
systemd:
name: nginx
state: started
enabled: yes
tags: service
- name: 健康检查
uri:
url: "http://{{ inventory_hostname }}:{{ http_port }}/health"
status_code: 200
timeout: 5
register: health
retries: 3
delay: 5
until: health.status == 200
handlers:
- name: reload nginx
systemd:
name: nginx
state: reloaded
这个 Playbook 里有几个关键设计值得学习:become: yes 提权;serial: 2 实现滚动,避免一次性重启所有 Web 导致服务中断;validate 参数让配置文件在正式替换前先过一遍语法检查(%s 会被替换成临时文件路径),这是防止"配置写错导致服务起不来"的最佳实践;notify + handlers 保证只有在配置文件真的发生变更时才 reload,没变更就不动——这正是幂等性的体现;until/retries/delay 实现带重试的健康检查。
对应的模板文件 templates/nginx.conf.j2 可以这样写,Jinja2 的 {{ }} 会被变量替换:
user nginx;
worker_processes {{ worker_processes }};
# 也可用 facts 自动适配:worker_processes {{ ansible_processor_vcpus }};
events {
worker_connections 1024;
}
http {
server {
listen {{ http_port }};
server_name {{ inventory_hostname }};
root /data/www;
access_log /var/log/nginx/access.log main;
}
}
执行与调试命令:
# 语法检查 ansible-playbook site.yml --syntax-check # 模拟执行(不真正改动,预测会发生什么变化) ansible-playbook site.yml --check # 查看会作用于哪些主机 ansible-playbook site.yml --list-hosts ansible-playbook site.yml --list-tasks # 只执行打了某个 tag 的任务 ansible-playbook site.yml --tags config # 从指定任务开始执行(跳过前面的) ansible-playbook site.yml --start-at-task "启动并设置开机自启" # 限制只跑一台,用于灰度验证 ansible-playbook site.yml --limit web01 # 交互式确认每一步(高危操作推荐) ansible-playbook site.yml --step # 加密敏感变量(如密码) ansible-vault create secrets.yml ansible-playbook site.yml --ask-vault-pass
六、常用模块速查
| 场景 | 模块 | 关键参数 |
|---|---|---|
| 连通测试 | ping | 无参数 |
| 执行命令 | command / shell | cmd、chdir、creates |
| 分发文件 | copy / template | src、dest、mode、backup |
| 改文件行 | lineinfile | path、regexp、line |
| 装包 | yum / apt / dnf | name、state=present/latest |
| 服务管理 | systemd / service | name、state、enabled |
| 用户组 | user / group | name、shell、groups |
| 定时任务 | cron | name、minute、job |
| 目录文件 | file | path、state、mode、owner |
| 下载 | get_url / unarchive | url、dest |
| HTTP 探测 | uri | url、status_code |
| 拉取信息 | setup | filter |
七、变量优先级与常见排错
Ansible 里同一个变量可以在多处定义,优先级从高到低大致是:命令行 --extra-vars > playbook 中的 vars > inventory 主机变量 > inventory 组变量 > group_vars/ 与 host_vars/ 目录文件 > role 的 defaults/。理解优先级能避免"改了变量却不生效"的困惑。推荐的组织方式是:把不常改的默认值放 role 的 defaults/main.yml,把环境相关的差异放 group_vars/prod.yml、group_vars/test.yml,把真正敏感的信息用 ansible-vault 加密存放。用 ansible-inventory --host web01 可以直接查看某台机器最终生效的变量全集,排错时非常有用。
常见报错与处理:
UNREACHABLE! Failed to connect:SSH 不通。检查密钥权限(~/.ssh700、私钥 600)、用户是否存在、端口是否正确。FAILED! => "msg": "Missing sudo password":提权失败。在受管机的/etc/sudoers里给该用户加NOPASSWD:ALL,或用--ask-become-pass交互输入。Aborting, target uses selinux but python bindings aren't installed:缺少libselinux-python,用 raw 模块先装上:ansible all -m raw -a 'yum install -y libselinux-python'。/usr/bin/python: not found:Python 路径不对,在 inventory 里指定ansible_python_interpreter=/usr/bin/python3。- 任务一直
changed而非ok:说明用了无幂等判断的模块(典型是 shell),改用专用模块或在命令后加changed_when条件。
调试时加上 -v(-vvv 会打印完整 SSH 过程,-vvvv 包含模块内部细节),绝大多数问题在 -vvv 输出里都能找到直接原因。另外 ansible-lint 工具可以静态检查 playbook 的写法规范,在团队协作时值得接入 CI。
八、落地实施步骤
- 在控制机安装 Ansible,生成专用 SSH 密钥并分发到所有受管机,确认
ssh root@主机免密可登。 - 建立项目目录,写入
ansible.cfg(含 inventory 路径、private_key_file、host_key_checking=False、forks)。 - 编写
inventory.ini,按角色分组(web/db/cache)并做组嵌套,用ansible-inventory --graph检查结构。 - 跑
ansible all -m ping确认全绿;不通的机器用-vvv看详细 SSH 报错。 - 先用 ad-hoc 完成一件小事(如批量安装 sysstat),熟悉模块参数。
- 把最高频的操作写成 Playbook,务必加上
validate校验与handlers。 - 用
--check和--limit 单台验证,确认无误后再全量。 - 把整个项目目录提交到 Git,inventory 与 playbook 都纳入版本管理,敏感信息用
ansible-vault加密。 - 配置定时执行(cron 或 AWX)做配置收敛,防止人工改动造成漂移。
小结
Ansible 批量管理服务器的核心就三件事:inventory 定义资产、ad-hoc 处理临时需求、playbook 描述目标状态。上手最快的路径是"先 ansible -m ping 跑通,再用 ad-hoc 做几次批量操作,最后把高频操作沉淀成 playbook"。避坑清单:一是忘记关 host_key_checking 导致新机器首次连接卡住;二是 command 模块里写管道(应改用 shell);三是所有操作都用 shell 而不用专用模块,失去幂等与变更检测;四是配置文件分发不加 validate,一旦写错所有机器同时起不来;五是 playbook 不加 serial,一次性重启全部机器导致业务中断;六是 inventory 和 playbook 不进 Git,几个月后没人知道线上配置究竟是什么。把这六点规避掉,Ansible 就能稳定支撑上百台服务器的日常运维。
关键词:Ansible教程、批量管理服务器、inventory主机清单、ansible -m ping、Playbook配置、handlers
企业QQ咨询




