第4章:Ansible入门:架构与原理、Inventory管理、Ad-Hoc命令、Playbook基础语法

说实话,刚接触自动化运维那会儿,我试过不少工具。Puppet、Chef、SaltStack都折腾过。但最后让我真正觉得「嗯,就是它了」的,还是Ansible。为什么?因为它简单。不需要装客户端,不需要搞复杂的证书体系,只要SSH能通,就能干活。

这一章,咱们就从头捋一遍Ansible的核心东西。我会把我在生产环境里踩过的坑、总结的经验,都揉进去讲。

4.1 Ansible架构与原理

先看一张图,这是我画的一个Ansible核心架构图。你一看就明白它怎么工作的了。

控制节点 (Control Node) Ansible核心组件 Inventory | Modules | Plugins | Playbook (无Agent | 基于SSH | 幂等性) 被管理节点1 (SSH连接) 被管理节点2 (SSH连接) 被管理节点N (SSH连接) 无需安装Agent | 通过SSH执行模块 | 结果返回控制节点

Ansible的架构,说白了就一句话:控制节点通过SSH把模块推送到被管理节点上执行,执行完就撤,不留痕迹。这和那些需要装Agent的工具完全不一样。

我个人习惯把Ansible的核心原理总结成三点:

  • 无Agent架构:被管理节点只需要有Python环境和SSH服务。我刚开始用的时候,最担心的就是「没装Agent会不会出问题?」后来发现,这恰恰是它最稳的地方。
  • 幂等性:同一个Playbook跑一次和跑一百次,结果是一样的。我在项目中遇到过有人反复跑同一个任务,系统状态始终一致,这就是幂等性的好处。
  • 模块化:每个操作都封装成模块。比如copy模块负责文件传输,yum模块负责包管理。你想想看,这就像搭积木一样,组合起来就能完成复杂任务。

核心理解:Ansible本质上是一个「声明式」的配置管理工具。你告诉它「最终状态应该是什么样」,它自己会想办法达到那个状态。而不是像脚本那样,一步一步告诉它「先做A,再做B」。

4.2 Inventory管理

Inventory就是Ansible的「通讯录」。它告诉Ansible:你要管理哪些机器?这些机器怎么连接?

默认的Inventory文件是 /etc/ansible/hosts,但说实话,我从来不用那个路径。我习惯在每个项目目录下自己建一个 inventory 文件,这样不同项目之间不会互相干扰。

4.2.1 静态Inventory

最简单的Inventory长这样:

# inventory文件
web01 ansible_host=192.168.1.10 ansible_user=root
web02 ansible_host=192.168.1.11 ansible_user=root

[webservers]
web01
web02

[dbservers]
db01 ansible_host=192.168.1.20 ansible_user=admin

这里有几个要点:

  • ansible_host:指定IP地址。如果不写,默认用主机名当IP。
  • ansible_user:SSH登录用户。我曾经因为忘记指定用户,用root去连一个禁止root登录的机器,卡了半天。
  • 组名:用方括号定义组。比如 [webservers],之后就可以针对这个组执行操作。

小技巧:我习惯在Inventory里用变量来管理密码或密钥路径。比如 ansible_ssh_private_key_file=/path/to/key。这样就不用每次都在命令行里指定了。

4.2.2 动态Inventory

如果你的机器是动态变化的——比如云上的ECS实例,每次重启IP都会变——那静态Inventory就不够用了。这时候需要动态Inventory。

动态Inventory本质上是一个可执行脚本(Python、Shell都行),它返回JSON格式的机器列表。Ansible官方提供了AWS、阿里云等云厂商的插件。我当年在阿里云上管理几百台ECS时,就是用动态Inventory自动拉取机器列表的。

# 使用阿里云动态Inventory插件示例
# 需要安装:pip install ansible-alicloud
ansible-inventory -i alicloud.yml --list

嗯,这里要注意:动态Inventory的调试比静态的麻烦。我建议先在测试环境用 --list 参数看看返回的JSON结构对不对,再正式使用。

4.3 Ad-Hoc命令

Ad-Hoc命令,说白了就是「一次性命令」。你不想写Playbook,就想快速干一件事,比如重启服务、查看磁盘空间,那就用Ad-Hoc。

基本格式:

ansible <主机组> -m <模块名> -a <模块参数>

几个我常用的例子:

# 检查所有web服务器的连通性
ansible webservers -m ping

# 查看所有机器的磁盘使用情况
ansible all -m shell -a "df -h"

# 批量安装nginx
ansible webservers -m yum -a "name=nginx state=present"

# 复制配置文件
ansible webservers -m copy -a "src=/etc/nginx/nginx.conf dest=/etc/nginx/nginx.conf owner=root group=root mode=0644"

避坑指南:我曾经在生产环境上执行 ansible all -m shell -a "reboot",结果所有机器同时重启了。嗯,那场面……所以用Ad-Hoc执行危险操作时,一定要加 --limit 限制范围,或者先用 --check 做模拟运行。

Ad-Hoc命令虽然方便,但有个缺点:不可重复。你今天敲了一串命令把环境配好了,下周再来一次,可能就忘了当时怎么配的。这时候就需要Playbook了。

4.4 Playbook基础语法

Playbook是Ansible的「剧本」。它用YAML格式描述了一系列任务,告诉Ansible「先做什么,再做什么,最终要达到什么状态」。

4.4.1 YAML基础

写Playbook之前,得先懂YAML。YAML的规则很简单:

  • 用缩进表示层级关系(只能用空格,不能用Tab)
  • 键值对用 key: value 格式(冒号后面要加空格)
  • 列表用 - item 表示

我见过太多新手因为缩进问题卡半天。记住:YAML对缩进极其敏感。一个空格不对,整个Playbook就废了。

4.4.2 第一个Playbook

来看一个最简单的Playbook:

---
- name: 安装并启动Nginx
  hosts: webservers
  become: yes
  tasks:
    - name: 安装nginx
      yum:
        name: nginx
        state: present

    - name: 启动nginx服务
      service:
        name: nginx
        state: started
        enabled: yes

逐行解释一下:

  • ---:YAML文件的开头标记
  • name:Playbook的描述,方便你以后知道这个剧本是干嘛的
  • hosts:指定在哪些机器上执行
  • become: yes:提权执行(相当于sudo)。我习惯在需要root权限的任务里加上这个
  • tasks:任务列表,每个任务都有 name 和具体的模块调用

个人经验:我写Playbook时,每个任务都会写一个清晰的 name。为什么?因为当Playbook执行失败时,Ansible会告诉你「第几个任务失败了」,但不会告诉你「哪个name的任务失败了」。有了name,你一眼就能定位问题。

4.4.3 变量与模板

真实场景中,不同机器的配置可能不一样。比如Nginx的端口,测试环境用8080,生产环境用80。这时候就需要变量。

---
- name: 配置Nginx端口
  hosts: webservers
  vars:
    nginx_port: 8080
  tasks:
    - name: 使用模板配置nginx
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf

模板文件 nginx.conf.j2 里可以这样写:

server {
    listen {{ nginx_port }};
    server_name example.com;
    ...
}

变量可以用 {{ }} 引用。Ansible会把它替换成实际的值。我当年做多环境部署时,就是靠变量和模板,一套Playbook通吃开发、测试、生产三个环境。

4.4.4 条件与循环

有时候需要根据条件决定是否执行某个任务。比如只在CentOS 7上执行某个操作:

tasks:
  - name: 仅在CentOS 7上执行
    yum:
      name: some-package
    when: ansible_os_family == "RedHat" and ansible_distribution_major_version == "7"

循环也很常用。比如批量创建用户:

tasks:
  - name: 创建多个用户
    user:
      name: "{{ item }}"
      state: present
    loop:
      - alice
      - bob
      - charlie

你想想看,如果没有循环,你得写三个重复的任务。有了循环,一行搞定。

4.5 本章小结

这一章我们走了一遍Ansible的入门路线。从架构原理到Inventory管理,从Ad-Hoc命令到Playbook基础语法。说白了,Ansible的核心就两件事:知道要管哪些机器(Inventory),知道要做什么操作(Playbook)

我个人建议,刚开始学的时候,先别急着写复杂的Playbook。先用Ad-Hoc命令熟悉各个模块的用法,等摸熟了,再慢慢把命令组合成Playbook。就像学做饭一样,先学会切菜、炒菜,再学做满汉全席。

下一章我们会深入Playbook的高级用法,包括角色(Roles)、变量优先级、以及如何组织大型项目。但那是后话了,先把今天的内容消化掉。


专注资料整理