4. Ansible 基础:Inventory 文件编写、Ad-hoc 命令、Playbook 入门
说到自动化批量部署,Ansible 是我这几年用得最顺手的工具之一。它不需要装客户端,靠 SSH 就能干活,这对我们设备注册场景来说太合适了。你想想看,几百台新设备到手,总不能一台台手动配吧?
这一章,我会把 Ansible 最核心的三个东西讲透:Inventory(清单)、Ad-hoc(临时命令)和 Playbook(剧本)。这三个搞明白了,批量部署的大门就算正式推开了。
4.1 Inventory 文件编写
Inventory 说白了就是 Ansible 的“通讯录”。它告诉 Ansible:你要管哪些机器?它们怎么分组?连接参数是什么?
我个人习惯用 INI 格式写 Inventory,简单直观。当然 YAML 也行,但 INI 在命令行里改起来更快。
4.1.1 基本格式
[web_servers]
192.168.1.10
192.168.1.11
192.168.1.12
[db_servers]
192.168.1.20 ansible_user=root ansible_port=22
[all:vars]
ansible_ssh_private_key_file=/home/ops/.ssh/id_rsa
ansible_python_interpreter=/usr/bin/python3
这里有几个要点:
- [组名] 定义主机组,后面 Playbook 可以直接针对组操作
- ansible_user 指定 SSH 用户,我建议不要用 root,单独建一个 deploy 用户
- [all:vars] 是全局变量,所有主机都生效
inventories/ 文件夹里,按环境分:production、staging、testing。这样切换环境时只要改一个参数。
4.1.2 动态 Inventory
设备数量一多,手动维护静态文件就不现实了。这时候可以用动态 Inventory,从 CMDB 或云 API 拉取主机列表。
我曾经在一个项目里管理 2000+ 台设备,静态 Inventory 改一次要半小时。后来写了个 Python 脚本,直接从数据库拉数据,输出 JSON 格式给 Ansible,问题就解决了。
#!/usr/bin/env python3
import json
def main():
inventory = {
"web": {
"hosts": ["10.0.1.10", "10.0.1.11"],
"vars": {"http_port": 80}
},
"db": {
"hosts": ["10.0.2.20"],
"vars": {"db_port": 3306}
},
"_meta": {
"hostvars": {
"10.0.1.10": {"ansible_user": "deploy"},
"10.0.1.11": {"ansible_user": "deploy"}
}
}
}
print(json.dumps(inventory))
if __name__ == "__main__":
main()
_meta 字段存放主机变量。否则 Ansible 会报错。
4.2 Ad-hoc 命令
Ad-hoc 就是“临时命令”。你不想写 Playbook,就想快速执行一条命令,比如重启服务、检查磁盘、拷贝文件。这时候 Ad-hoc 最方便。
基本语法:
ansible <主机组> -m <模块> -a <参数>
4.2.1 常用模块实战
| 模块名 | 用途 | 示例 |
|---|---|---|
ping |
测试连通性 | ansible all -m ping |
command |
执行命令(默认) | ansible web -a "uptime" |
shell |
执行 shell 命令 | ansible db -m shell -a "free -m" |
copy |
拷贝文件 | ansible all -m copy -a "src=/etc/hosts dest=/tmp/" |
yum |
安装包 | ansible web -m yum -a "name=nginx state=present" |
service |
管理服务 | ansible web -m service -a "name=nginx state=started" |
举个例子,批量检查所有 web 服务器的磁盘使用率:
ansible web_servers -m shell -a "df -h | grep -E '^/dev'"
输出结果会按主机分组显示,一目了然。
--forks 参数控制并发数。默认 5 个并发,结果 200 台机器同时重启,监控告警直接炸了。后来我改成 ansible all -m service -a "name=nginx state=restarted" --forks 20,分批执行,稳多了。
4.3 Playbook 入门
Ad-hoc 适合“一次性操作”,但如果你要完成一个多步骤的任务——比如部署一个 Web 服务,需要安装包、改配置、启动服务、检查状态——那就得用 Playbook 了。
Playbook 是 YAML 格式的“剧本”,Ansible 按剧本一步步执行。
4.3.1 第一个 Playbook
我们写一个部署 Nginx 的 Playbook:
---
- name: 部署 Nginx Web 服务
hosts: web_servers
become: yes
vars:
nginx_port: 8080
tasks:
- name: 安装 Nginx
yum:
name: nginx
state: present
- name: 拷贝配置文件
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: 启动并启用 Nginx
service:
name: nginx
state: started
enabled: yes
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
这个 Playbook 做了三件事:
- 安装 Nginx 包
- 用模板引擎生成配置文件(
nginx.conf.j2是 Jinja2 模板) - 启动服务并设置开机自启
注意那个 notify 和 handlers。只有配置文件真的变了,才会触发重启。这叫“幂等性”——Ansible 的核心思想之一。
4.3.2 执行 Playbook
ansible-playbook -i inventories/production deploy_nginx.yml --check
我建议每次执行前都加 --check 做一次“干跑”,看看 Ansible 会改什么,不会真的执行。确认没问题了再跑正式命令:
ansible-playbook -i inventories/production deploy_nginx.yml -v
-v 是 verbose 模式,能看到详细输出。如果还不够,可以加 -vvv 甚至 -vvvv,调试时很有用。
pre_tasks 块,先检查目标主机的系统版本和内核参数,不符合条件的直接 fail 掉。这样不会跑到一半才发现环境不对。
4.4 本章知识体系
下面这张图把 Inventory、Ad-hoc、Playbook 的关系串起来了。你一看就明白:
4.5 实战建议
学 Ansible 最忌讳的就是“只看不练”。我建议你按这个顺序上手:
- 先写 Inventory:拿两台测试机,用静态 INI 格式写清楚
- 跑几个 Ad-hoc:试试
ping、command、copy,感受一下批量执行 - 抄一个 Playbook:就上面那个 Nginx 的例子,改改路径和端口,跑通它
- 加变量和模板:把配置文件里的硬编码改成变量,用 Jinja2 模板生成
--check 和 --diff 参数看清楚改动。我见过有人把整个生产环境的 Nginx 配置覆盖了,恢复花了两小时。
Ansible 的入门其实不难,难的是写出“优雅”的 Playbook——可读性强、幂等、易维护。这个我们后面章节会深入讲。先把基础打牢,后面自然水到渠成。