#005 · 2025-07-15✓ PASS
Ansible 自动化运维
RHEL 环境下 Ansible 自动化运维的完整入门指南,涵盖 Inventory 管理、Playbook 编写、变量与事实、循环与条件控制,从零掌握 IT 自动化。
Ansible 是一个开源的自动化平台,使用简单易读的 YAML 格式编写 Playbook,无需在被管理节点安装任何代理,通过 SSH 即可实现配置管理、应用部署和工作流编排。
一、自动化与 Ansible 概述
1.1 自动化与基础架构即代码(IaC)
大多数系统管理长期依赖于图形界面或命令行手动操作,这种做法存在诸多问题:
- 系统管理员容易跳过某个步骤或在某个步骤出错
- 对步骤执行是否正确或是否产生预期结果的验证通常有限
- 以手动方式管理多台服务器容易出现微小差异,给 IT 环境带来不一致性
基础架构即代码 (IaC) 让自动化语言来定义和描述所需的 IT 基础架构状态:
- 自动化语言表现为简单的文本文件,可使用版本控制系统轻松管理
- 开发人员定义所需配置,操作员查看更改并提供反馈
- 通过自动化重复确保系统处于期望状态
1.2 Ansible 特性
| 特性 | 说明 |
|---|---|
| Simple | Playbook 易读、易懂、易改,不需要特殊编码技能 |
| Powerful | 可部署应用、配置管理、工作流自动化和网络自动化 |
| Agentless | 使用 OpenSSH/WinRM 连接管理主机,无需在被管节点安装代理 |
| 跨平台 | 支持 Linux、Windows、UNIX、网络设备和容器 |
| 幂等性 | 可在相同主机上多次安全运行 Playbook,不会重复更改 |
| 版本控制 | 所有配置均为文本文件,可纳入 Git 等版本控制系统管理 |
1.3 Ansible 架构
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 清单概述
清单定义 Ansible 将要管理的一批主机,这些主机可以分配到组中,以方便管理。组可以包含子组,主机可以是多个组的成员。
默认的清单文件为 /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 从控制节点上的多个可能位置选择其配置文件(按优先级排序):
ANSIBLE_CONFIG环境变量指定的文件- 当前目录的
./ansible.cfg - 用户家目录的
~/.ansible.cfg /etc/ansible/ansible.cfg(默认)
Ansible 不会融合多个配置文件,只会使用选定的一个。
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 | 按 Linux 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 变量定义位置
变量可在三个基本范围级别定义:
| 范围 | 说明 | 优先级 |
|---|---|---|
| 全局范围 | 命令行或 Ansible 配置设置的变量 | 最高 |
| 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 }}"
当变量用作值的第一个元素时,必须使用引号。这可以防止 Ansible 将变量引用视为 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 事实是 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 管理
- 优先使用专用模块:比直接使用 shell/command 模块更可靠且幂等
- 命名规范:变量使用下划线 (
web_server),不用连字符 (web-server) - 目录变量管理:使用
group_vars和host_vars目录,而非在清单文件中混入变量 - 语法检查:运行前使用
--syntax-check验证 - 从小处开始:先用临时命令验证连通性,再编写 Playbook
- 使用 roles:当 Playbook 变得复杂时,使用 Ansible Roles 组织代码
- 敏感信息:使用
ansible-vault加密密码等敏感变量