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] 是全局变量,所有主机都生效
我的习惯:我会把 Inventory 文件放在项目目录下的 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()
注意:动态 Inventory 脚本必须输出 JSON 到标准输出,并且要有 _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 做了三件事:

  1. 安装 Nginx 包
  2. 用模板引擎生成配置文件(nginx.conf.j2 是 Jinja2 模板)
  3. 启动服务并设置开机自启

注意那个 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,调试时很有用。

我的习惯:我会在 Playbook 开头加一个 pre_tasks 块,先检查目标主机的系统版本和内核参数,不符合条件的直接 fail 掉。这样不会跑到一半才发现环境不对。

4.4 本章知识体系

下面这张图把 Inventory、Ad-hoc、Playbook 的关系串起来了。你一看就明白:

Ansible 核心三件套 Inventory 清单 Ad-hoc 命令 Playbook 剧本 静态 INI 动态 JSON ping/command copy/service tasks/handlers 模板/变量 核心工作流 1. 定义 Inventory:告诉 Ansible 管哪些机器 2. 快速验证:用 Ad-hoc 测试连通性和基本操作 3. 编写 Playbook:把重复操作写成可复用的剧本 4. 持续执行:配合 CI/CD 实现自动化部署 5. 幂等保障:多次执行结果一致,不会搞乱系统

4.5 实战建议

学 Ansible 最忌讳的就是“只看不练”。我建议你按这个顺序上手:

  1. 先写 Inventory:拿两台测试机,用静态 INI 格式写清楚
  2. 跑几个 Ad-hoc:试试 ping、command、copy,感受一下批量执行
  3. 抄一个 Playbook:就上面那个 Nginx 的例子,改改路径和端口,跑通它
  4. 加变量和模板:把配置文件里的硬编码改成变量,用 Jinja2 模板生成
重要提醒:千万不要在生产环境直接跑不熟悉的 Playbook。先在测试环境验证,加 --check 和 --diff 参数看清楚改动。我见过有人把整个生产环境的 Nginx 配置覆盖了,恢复花了两小时。

Ansible 的入门其实不难,难的是写出“优雅”的 Playbook——可读性强、幂等、易维护。这个我们后面章节会深入讲。先把基础打牢,后面自然水到渠成。