Ansible 是开源自动化平台:用 YAML 写 Playbook,被管节点无需装代理,通过 SSH 完成配置管理、应用部署与工作流编排。
一、自动化与 Ansible 概述
1.1 自动化与基础架构即代码(IaC)
手动操作(图形界面或命令行)的问题:容易跳过或做错步骤、对执行结果验证有限、多台服务器易出现微小差异导致环境不一致。基础架构即代码 (IaC) 用自动化语言定义所需的 IT 基础架构状态:配置是简单文本文件(可纳入版本控制),开发人员定义配置、操作员审阅反馈,通过自动化重复确保系统处于期望状态。
1.2 Ansible 特性
| 特性 | 说明 |
|---|---|
| Simple | Playbook 易读易改,无需编码技能 |
| Powerful | 应用部署、配置管理、工作流与网络自动化 |
| Agentless | 用 OpenSSH/WinRM 连接,被管节点无需装代理 |
| 跨平台 | Linux、Windows、UNIX、网络设备与容器 |
| 幂等性 | 同一主机可重复安全运行 Playbook,不会重复更改 |
| 版本控制 | 配置均为文本文件,可纳入 Git |
1.3 Ansible 架构
两类计算机:
| 角色 | 说明 |
|---|---|
| 控制节点 (Control Node) | 安装 Ansible,维护全部组件与配置 |
| 受管主机 (Managed Hosts) | 在 Inventory 中列出,通过 SSH 接收任务 |
工作流程:管理员写 Playbook(YAML 格式)→ 控制节点 SSH 连接受管主机 → 推送 Ansible 模块(Python 小程序)→ 执行后模块自动删除。
二、部署 Ansible
2.1 安装
# 安装 Ansible
dnf install ansible -y
# 查看版本信息
ansible --version
# ansible [core 2.13.3]
# config file = /etc/ansible/ansible.cfg
# configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
2.2 安装额外模块
# 安装 Ansible Galaxy 集合
ansible-galaxy collection install ansible.posix
三、Inventory 清单管理
3.1 清单概述
清单定义被管理的批主机;可分组、组可嵌套,一台主机可属多组。
默认清单文件 /etc/ansible/hosts。
3.2 INI 格式清单
基本规则:
#开头的行是注释- 空行忽略
- 组和主机使用
[name]来定义 - 可以使用主机名或 IP 地址
- 一台主机可属于多个组
# 没有指定组的主机要写在所有组的最前面
green.example.com
blue.example.com
192.168.100.1
# webservers 组
[webservers]
alpha.example.org
beta.example.org
192.168.1.100
www[001:006].example.com
# dbservers 组
[dbservers]
db01.intranet.mydomain.net
db02.intranet.mydomain.net
10.25.1.56
# test 组包含其他组(组嵌套)
[test:children]
webservers
dbservers
3.3 创建清单文件
# 配置本地解析
cat /etc/hosts
# 192.168.115.128 web1 web1.example.com
# 192.168.115.133 web2 web2.example.com
# 创建自定义清单
cat inventory
# [webserver]
# web1.example.com
# web2.example.com
#
# [dbserver]
# 192.168.115.128
# 192.168.115.133
#
# [test:children]
# webserver
# dbserver
# 查看清单结构
ansible-inventory --graph -i inventory
# @all:
# |--@test:
# | |--@dbserver:
# | | |--192.168.115.128
# | | |--192.168.115.133
# | |--@webserver:
# | | |--web1.example.com
# | | |--web2.example.com
# |--@ungrouped:
3.4 内置主机组
| 组名 | 说明 |
|---|---|
all | 清单中明确列出的每个主机 |
ungrouped | 清单中列出但不属于任何组的主机 |
四、ansible.cfg 配置文件
4.1 配置文件优先级
配置文件按以下优先级选择(只取其一):
ANSIBLE_CONFIG环境变量指定的文件- 当前目录的
./ansible.cfg - 用户家目录的
~/.ansible.cfg /etc/ansible/ansible.cfg(默认)
不融合多个配置文件,只用选定的一个。
4.2 常用配置项
[defaults]
inventory = ./inventory # 清单文件路径
remote_user = root # SSH 远程用户
ask_pass = false # 是否提示输入 SSH 密码
[privilege_escalation]
become = true # 是否自动提权
become_method = sudo # 提权方法
become_user = root # 提权目标用户
become_ask_pass = false # 是否提示输入 sudo 密码
4.3 生成示例配置
# 生成禁用的默认配置
ansible-config init --disabled > ansible2.cfg
# 生成所有配置项
ansible-config init --disabled -t all > ansible3.cfg
五、临时命令 (Ad-Hoc Commands)
5.1 基本格式
ansible host-pattern -m module [-a 'module arguments'] [-i inventory]
5.2 常用模块
| 模块类别 | 模块 | 用途 |
|---|---|---|
| 文件模块 | copy | 复制文件到远程 |
file | 文件属性与权限 | |
lineinfile | 管理文件中的行 | |
| 软件包模块 | package | 通用包管理 |
yum | YUM 包管理 | |
dnf | DNF 包管理 | |
pip | Python 包管理 | |
| 系统模块 | firewalld | 防火墙管理 |
reboot | 重启系统 | |
service | 服务管理 | |
user | 用户管理 | |
group | 组管理 | |
| 网络模块 | get_url | 下载文件 |
nmcli | NetworkManager 管理 | |
uri | HTTP 请求 |
5.3 临时命令示例
# 测试连通性
ansible all -m ping
# 创建用户
ansible -m user -a 'name=haha uid=4000 state=present' web1.example.com
# 执行 shell 命令
ansible -m shell -a 'id haha' web1.example.com
# 安装软件包
ansible webserver -m dnf -a 'name=httpd state=present'
5.4 执行命令的三种模块
| 模块 | 说明 |
|---|---|
shell | 按 Shell 方式运行,支持管道与重定向 |
command | 单纯执行命令,不支持管道 |
raw | 目标无 Python 环境时使用 |
尽量少用这三个模块:专用模块多为幂等且可追踪更改。
六、Playbook 编写
6.1 YAML 基础
YAML 两条规则:同层级数据元素缩进量必须相同;子项缩进量必须大于父项。
只能用空格缩进,不能用制表符。
Vim 配置:autocmd FileType yaml setlocal ai ts=2 sw=2 et
6.2 YAML 语法速查
# 注释
# This is a YAML comment
# 字符串(通常不需要引号)
this is a string
'this is a string'
"this is yet another string"
# 多行字符串(保留换行)
include_newlines: |
Example Company
123 Main Street
Atlanta, GA 30303
# 多行字符串(折叠为单行)
fold_newlines: >
This is an example
of a long string,
that will become
a single sentence once folded.
# 字典(缩进格式)
name: svcrole
svcservice: httpd
svcport: 80
# 字典(内联格式)
{name: svcrole, svcservice: httpd, svcport: 80}
# 列表(短横线格式)
hosts:
- servera
- serverb
- serverc
# 列表(内联格式)
hosts: [servera, serverb, serverc]
6.3 Playbook 基本结构
---
- name: play 名称
hosts: 目标主机或组
tasks:
- name: 任务描述
module_name:
param1: value1
param2: value2
6.4 简单 Playbook 示例
---
- name: create user
hosts: web1.example.com
tasks:
- name: create user haha
user:
name: haha
uid: 4000
state: present
6.5 多任务 Playbook
---
- name: configure web server
hosts: webservers
tasks:
- name: web server is enabled
service:
name: httpd
enabled: true
- name: ntp server is enabled
service:
name: chronyd
enabled: true
- name: postfix is enabled
service:
name: postfix
enabled: true
6.6 多 Play 的 Playbook
---
- name: first play
hosts: web1.example.com
tasks:
- name: install httpd
dnf:
name: httpd
state: present
- name: start httpd
service:
name: httpd
enabled: true
- name: second play
hosts: 192.168.115.128
tasks:
- name: start mariadb
service:
name: mariadb
enabled: true
6.7 运行 Playbook
# 检查语法
ansible-playbook --syntax-check site.yml
# 运行 Playbook
ansible-playbook user.yml
# 增加详细程度
ansible-playbook user.yml -v # 显示任务结果
ansible-playbook user.yml -vv # 显示任务结果和配置
ansible-playbook user.yml -vvv # 包含连接信息
ansible-playbook user.yml -vvvv # 包含额外详细选项
七、变量管理
7.1 变量定义位置
变量分三个范围级别:
| 范围 | 说明 | 优先级 |
|---|---|---|
| 全局范围 | 命令行或配置里设置的变量 | 最高 |
| Play 范围 | 在 play 和相关结构中设置的变量 | 中 |
| 主机范围 | 清单、事实收集或注册任务设置的变量 | 最低 |
7.2 在 Playbook 中定义变量
- hosts: all
vars:
user: joe
home: /home/joe
7.3 使用外部变量文件
# Playbook 中引用
- hosts: all
vars_files:
- vars/users.yml
# vars/users.yml 内容
user: joe
home: /home/joe
7.4 在任务中使用变量
vars:
user: joe
tasks:
- name: Create the user {{ user }}
user:
name: "{{ user }}"
变量作为值的第一个元素时必须加引号,否则会被当作 YAML 字典开头。
7.5 主机变量和组变量(推荐做法)
推荐做法:建 group_vars 和 host_vars 目录:
project/
├── group_vars
│ ├── datacenter1
│ ├── datacenter2
│ └── datacenters
├── host_vars
│ ├── demo1.example.com
│ ├── demo2.example.com
│ ├── demo3.example.com
│ └── demo4.example.com
└── inventory
# group_vars/datacenters
package: httpd
# group_vars/datacenter1(覆盖父组变量)
package: httpd
# group_vars/datacenter2
package: apache
# host_vars/demo1.example.com(最高优先级)
package: httpd
7.6 从命令行覆盖变量
ansible-playbook main.yml -e "package=apache"
7.7 使用注册变量捕获命令输出
---
- name: Install a package and prints the result
hosts: all
tasks:
- name: Install the package
dnf:
name: httpd
state: installed
register: install_result
- debug:
var: install_result
八、Ansible Facts(事实)
8.1 事实概述
事实是 Ansible 自动检测到的受管主机变量;每个 play 首个任务前会先跑 setup 模块收集。
| 事实 | 变量 |
|---|---|
| 短主机名 | ansible_facts['hostname'] |
| 完全限定域名 | ansible_facts['fqdn'] |
| 主要 IPv4 地址 | ansible_facts['default_ipv4']['address'] |
| 所有网络接口名称 | ansible_facts['interfaces'] |
| DNS 服务器列表 | ansible_facts['dns']['nameservers'] |
| 内核版本 | ansible_facts['kernel'] |
8.2 在 Playbook 中使用事实
---
- hosts: all
tasks:
- name: Print various Ansible facts
debug:
msg: >
The default IPv4 address of {{ ansible_facts.fqdn }}
is {{ ansible_facts.default_ipv4.address }}
8.3 关闭事实收集
---
- name: This play gathers no facts automatically
hosts: large_farm
gather_facts: no
九、循环与条件
9.1 简单循环
- name: Postfix and Dovecot are running
service:
name: "{{ item }}"
state: started
loop:
- postfix
- dovecot
9.2 使用变量列表循环
vars:
mail_services:
- postfix
- dovecot
tasks:
- name: Postfix and Dovecot are running
service:
name: "{{ item }}"
state: started
loop: "{{ mail_services }}"
9.3 循环散列/字典列表
- name: Users exist and are in the correct groups
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- name: jane
groups: wheel
- name: joe
groups: root
9.4 when 条件语句
| 操作 | 示例 |
|---|---|
| 等于(字符串) | ansible_machine == "x86_64" |
| 等于(数字) | max_memory == 512 |
| 小于 | min_memory < 128 |
| 大于 | min_memory > 256 |
| 不等于 | min_memory != 512 |
| 变量存在 | min_memory is defined |
| 变量不存在 | min_memory is not defined |
| 布尔变量为 true | memory_available |
| 布尔变量为 false | not memory_available |
| 值在列表中 | ansible_distribution in supported_distros |
9.5 when 使用示例
# 条件安装
- name: "{{ my_service }} package is installed"
dnf:
name: "{{ my_service }}"
when: my_service is defined
# 布尔条件
- name: httpd package is installed
dnf:
name: httpd
when: run_my_task
# 判断发行版
- name: Install httpd using dnf
dnf:
name: httpd
state: present
when: ansible_distribution in supported_distros
9.6 组合条件
# OR 条件
when: ansible_distribution == "Redhat" or ansible_distribution == "Fedora"
# AND 条件
when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"
# 列表形式(默认 AND)
when:
- ansible_distribution_version == "7.5"
- ansible_kernel == "3.10.0-327.el7.x86_64"
# 括号分组
when: >
( ansible_distribution == "Redhat" and
ansible_distribution_major_version == "7" )
or
( ansible_distribution == "Fedora" and
ansible_distribution_major_version == "28" )
9.7 组合循环与条件
# 仅在根分区有足够空间时安装
- name: install mariadb-server if enough space on root
dnf:
name: mariadb-server
state: latest
loop: "{{ ansible_mounts }}"
when: item.mount == "/" and item.size_available > 300000000
十、最佳实践
- 版本控制与命名:Playbook、清单、配置纳入 Git;变量用下划线(
web_server)不用连字符(web-server) - 变量与密钥:用
group_vars/host_vars目录,别混进清单文件;敏感变量用ansible-vault加密 - 运行纪律:优先专用模块(比 shell/command 更可靠且幂等);先用临时命令验连通性再写 Playbook;运行前
--syntax-check;复杂时用 Roles 组织