# 自动化运维Day03:Ansible模块进阶(setup,debug),四种常用变量,进阶语法;Ansible Roles(角色)
一、实验环境
沿用前两天的环境
使用 template 模板机 克隆下方 4 台虚拟机,配置好 IP 地址后,通过 WindTerm 远程连接虚拟机,作为 Ansible 控制节点与被管理节点。

二、Ansible 模块进阶
2.1 setup 模块
- setup 模块 用于采集被管理主机的系统事实信息,这些信息被称为 Facts。
ansible_facts变量用于存储采集到的系统信息,供后续任务调用。- 每次执行 Playbook 时,默认第一个任务就是 Gathering Facts(收集事实信息)。

- 使用
setup模块可以手动查看或过滤这些 Facts 信息。
常用命令示例:
# 查看所有 Facts 信息
ansible test -m setup
192.168.8.11 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.1.11"
… 省略部分内容…
- 找出下列facts信息(有父子关系时使用.分隔)
- ansible_bios_version(版本号)
- ansible_memtotal_mb(内存)
- ansible_hostname(主机名)
# 过滤指定 Facts(使用 filter 参数)
ansible web1 -m setup -a "filter=ansible_bios_version" #过滤版本号
ansible web1 -m setup -a "filter=ansible_memtotal_mb" #过滤内存
ansible web1 -m setup -a "filter=ansible_hostname" #过滤主机名
常用 Facts 变量(有父子关系时使用 . 分隔调用):
ansible_bios_versionansible_memtotal_mbansible_hostnameansible_all_ipv4_addressesansible_ens160.ipv4.address
2.2 debug 模块
debug 模块 可以显示变量的值。debug取的值源自于与setup模块获取的Facts。主要用于辅助排错和验证变量内容。
常用参数:
msg:要输出的信息(引用变量时需要使用{{ }})
示例 Playbook:
---
- hosts: web1
tasks:
- debug:
msg: "主机名是:{{ ansible_hostname }}"
- debug:
msg: "总内存大小:{{ ansible_memtotal_mb }}"
补充:var模块也可以直接调用变量且不需要加双花括号,但是不能和字符串一起使用,只能单独调用字符串
三、Ansible 变量
Ansible 支持十几种定义变量的方式,本节介绍其中最常用的四种,并按优先级从高到低排序:
- Playbook 变量(在 Play 中使用
vars:定义, Play 级别的变量,单个play生效) - Inventory 变量(主机变量和组变量)
- 变量文件(通过
vars_files:加载的外部变量文件) - Host Facts 变量(由
setup模块收集的系统信息)
补充:详细的优先级顺序(了解):
1. 命令行 -e 变量(最高) 2. Playbook 中 vars: 定义的变量 3. vars_files: 加载的变量文件 4. role 中 vars/ 目录下的变量 5. Host Facts(setup 模块采集的变量) 6. Inventory 主机变量(host_vars/) 7. Inventory 组变量(group_vars/) 8. Playbook 中 include_vars 导入的变量 9. role 中 defaults/ 目录下的变量(最低)
3.1 Inventory 变量
Inventory 变量是在主机清单配置文件(hosts)中为特定主机(组)定义的变量。
hosts 文件示例:
[webserver]
web[1:2]
[db]
db1 myvar1="hello the world" myvar2="content" #为db1生成两个变量
#写在哪个主机后边,这个变量就属于谁
[cluster:children] #children代表下面是组成员
webserver
db
[webserver:vars] # 为webserver组定义变量,改组成员均可调用。vars 是固定关键字,代表下面的是变量
yourname="tina"
编写调用 Inventory 变量的 Playbook,执行playbook验证:
---
- hosts: db1
tasks:
- name: use inventory vars
shell: echo {{ myvar1 }} > /tmp/{{ myvar2 }} # 这里 {{}} 不在开头,所以不需要加双引号
- hosts: webserver
tasks:
- name: use inventory vars yourname
user:
name: "{{ yourname }}" # {{}} 开头时需要加双引号
3.2 Host Facts 变量
Host Facts 变量可以直接调用 setup 模块收集到的系统信息。
示例 Playbook:
---
- hosts: web1
tasks:
- name: Host Facts 变量应用
copy:
content: "{{ ansible_hostname }}:{{ ansible_bios_version }}"
dest: /tmp/facts.txt
3.3 Playbook 变量
在 Playbook 中使用 vars 关键字定义变量。
注意:仅在当前 play 内有效
示例 Playbook:
---
- hosts: web1
vars:
iname: heal
ipass: '123456' # 密码必须是字符串,需要加引号
tasks:
- name: Use variables create user.
user:
name: "{{ iname }}"
password: "{{ ipass | password_hash('sha512') }}"
3.4 变量文件
将变量单独定义在一个 YAML 文件中,在 Playbook 中通过 vars_files 调用。
变量文件 variables.yml:
注意格式为键值对
---
iname: cloud
ipass: '123456'
调用变量文件的 Playbook:
---
- hosts: web1
vars_files: variables.yml #调用YAML变量文件
tasks:
- name: create user.
user:
name: "{{ iname }}" # iname 来自变量文件
password: "{{ ipass | password_hash('sha512') }}" # ipass 来自变量文件
四、Ansible 进阶
4.1 template 模块
-
template 模块作用类似于copy,都可以拷贝文件。
-
copy模块只能复制静态文件。 -
template模块主要用于生成动态文件,并且可以调用文件中的变量
如果希望每个主机拷贝的文件内容不一样(例如包含各自的 IP 或主机名),就需要使用template模块。 -
template模块结合 Jinja2 模板引擎,可以实现动态内容生成。- Jinja2 表达式语法:
{{ 变量 }} - 之前在 Playbook 中调用变量,实际上也是 Jinja2 的功能。
template模块的主要作用是生成动态配置文件。
- Jinja2 表达式语法:
示例 1:为 webserver 组中的主机生成不同内容的首页
模板文件 index.html:
Welcome to {{ ansible_hostname }} on {{ ansible_ens160.ipv4.address }}.
# 模板文件中调用变量不需要双引号
Playbook:
---
- hosts: webserver
tasks:
- name: use template copy index.html to webserver.
template:
src: ~/ansible/template/index.html
dest: /usr/local/nginx/html/index.html
示例 2:使用自定义变量
- template 使用 .j2 后缀模板文件
- 例如:nginx.conf.j2
- 里面可以写 {{ 变量名 }}
模板文件 source.j2:
{{ welcome }} {{ iname }} ...
Playbook:
---
- hosts: webserver
vars:
welcome: 'hello' #为变量赋值
iname: 'jack' #为变量赋值
tasks:
- name: 使用template模块将文件复制到远程主机
template:
src: ~/ansible/template/source.j2 #调用文件,调用文件中的变量
dest: /tmp/
4.2 Ansible 高级语法(重要)
4.2.1 error 处理机制
默认情况下,Ansible 遇到错误会立即停止 Playbook 的执行。
忽略单个任务的错误:
---
- hosts: web1
tasks:
- name: start a service that does not exist.
service:
name: hehe
state: started
ignore_errors: true # 与模块对齐,则仅针对当前模块任务,忽略错误
- name: touch a file.
file:
path: /tmp/service.txt
state: touch
忽略整个play的错误:
---
- hosts: web1
ignore_errors: true # 当前 Play 内忽略错误
tasks:
- name: start a service that does not exist.
service:
name: hehe
state: started
- name: touch a file.
file:
path: /tmp/service.txt
state: touch
4.2.2 handlers
handlers是一种特殊的任务,只在被其他任务通过 notify 通知后(任务处于changed状态时),才在 Playbook 结束时执行一次,通常用于触发服务的重启或重载等收尾操作。
注意:即使被多次 notify,也只执行一次。
handlers 的特点:
- 仅当任务触发(
notify)handlers 时才执行(不触发不执行)。 - 多个任务 notify 同一个 handler 时,只会执行一次。
- 仅当任务的执行状态为
changed时,handlers 才会执行。 - handlers 任务在当前play内的所有其他 tasks 执行完毕后才执行。
注意:一个play只能有一个handlers(但一个剧本可以有多个)
示例:
---
- hosts: web1
tasks:
- name: 创建一个目录 #多次执行该任务,playbook状态将不再是changed
file:
path: /tmp/parents/subdir/
state: directory
notify: touch file # notify 后面的名称必须与 handlers 中的任务名称一致
#notify和模块处于同一层级
handlers: #handlers和tasks属于同一层级
- name: touch file
file:
path: /tmp/parents/subdir/new.txt
state: touch
注意:notify 后面的名称必须与 handlers 中的任务名称一致
细节补充:即使使用了ignore_errors: true,也不会影响handlers的触发条件
4.2.3 when 条件判断
使用 when 可以为任务添加执行条件,只有条件为真时才执行该任务。
常用比较操作符:==、!=、>、>=、<、<=
多个条件可以使用 and 或 or 连接。
注意:
when表达式中引用变量时不需要使用{{ }}。
示例 1:内存不足 700MB 时停止 NetworkManager 服务
---
- hosts: web1
tasks:
- name: 检查内存,内存不足 700MB 时停止 NetworkManager 服务
service:
name: NetworkManager
state: stopped
when: ansible_memfree_mb < 700
示例 2:判断操作系统为 Rocky 8 时创建文件(多行条件)
---
- hosts: web1
tasks:
- name: 判断操作系统为 Rocky 8 时创建文件
file:
path: /tmp/when.txt
state: touch
when: >
ansible_distribution == "Rocky" #注意:字符串要加双引号
and
ansible_distribution_major_version == "8"
4.2.4 block 任务块
使用 block 可以将多个任务组合成一个组,方便统一管理。
block 还支持 rescue 和 always 子句:
rescue:block 中任务执行失败时执行的补救任务。always:无论 block 是否成功,都会执行的任务。
基础 block 示例:
---
- hosts: db1
tasks:
- name: 定义一组任务
block:
- name: 安装 httpd
yum:
name: httpd
state: present
- name: start httpd
service:
name: httpd
state: started
when: ansible_distribution == "Rocky" #满足此条件时,执行block任务块
rescue + always 示例:
---
- hosts: web1
tasks:
- block:
- name: 任务块创建 test1.txt
file:
path: /tmp/test1.txt
state: touch
rescue:
- name: 任务块执行失败会创建 test2.txt
file:
path: /tmp/test2.txt
state: touch
always:
- name: 任何情况下都会创建 test3.txt
file:
path: /tmp/test3.txt
state: touch
直接运行剧本,会有test1.txt和test3.txt
将block改成: path: /tmp/abc/test1.txt,会有test2.txt和test3.txt
为什么当block报错整个任务不会停止?因为整个 block: + rescue: + always: 算 Ansible 的「一个任务单元」,单个任务失败不会让剧本停止,且rescue 就是专门用来捕获 block 里的失败,不让任务中断
4.2.5 loop 循环
当多个任务使用相同模块时,可以使用 loop 循环来避免重复编写。
简单列表循环:
---
- hosts: test
tasks:
- name: 借助loop循环依次创建school,legend,life目录
file:
path: /tmp/{{ item }} # item 是循环中的固定关键字
state: directory
loop: #loop中的所有元素会逐个丢给item
- School
- Legend
- Life
复杂字典列表循环:
---
- hosts: web1
tasks:
- name: 使用loop循环完成用户的批量创建,并设置密码
user:
name: "{{ item.iname }}" #注意,这里'tiem'属于固定格式
password: "{{ item.ipass | password_hash('sha512') }}"
loop: # 循环成员为字典
- { iname: 'term', ipass: '123456' }
- { iname: 'amy', ipass: '654321' }
这里item是固定格式,必须通过 item.元素 来调用定义的字典元素
五、Ansible Roles
5.1 Roles 概述
在实际生产环境中,随着功能增多,Playbook 文件会越来越多,且可能互相调用变量文件等,管理起来非常困难。
Ansible 从 1.2 版本开始支持 Roles。
Roles 是管理ansible文件的一种规范(目录结构),用于更好地组织和管理 Playbook、变量、模板、handlers 等文件。
ansible的角色(role)像是做软件开发一样。开发是做一堆配置文件,功能文件,然后再main文件中进行集成;role就像是把配置文件,资源文件放到对应目录,然后在剧本中集成/调用
5.2 Roles 规范的目录结构
| 目录 / 文件 | 作用说明 |
|---|---|
defaults/main.yml |
定义变量的默认值(优先级较低) |
files/ |
存放静态文件 |
handlers/main.yml |
定义 handlers |
meta/main.yml |
角色元数据(作者、版本、依赖等) |
README.md |
整个角色的说明文档 |
tasks/main.yml |
定义任务的核心文件 |
templates/ |
存放 Jinja2 动态模板文件 |
vars/main.yml |
定义变量(优先级较高) |
不同模块会自动到对应目录下查找数据。
标准的目录结构:
role_name/
├── defaults/ # 默认变量(优先级最低)
│ └── main.yml
├── vars/ # 角色变量(优先级高于 defaults)
│ └── main.yml
├── tasks/ # 主要任务列表(必须有)
│ └── main.yml
├── handlers/ # 处理器,用于触发服务重启等
│ └── main.yml
├── templates/ # Jinja2 模板文件(.j2)
│ └── *.j2
├── files/ # 静态文件,直接拷贝
│ └── *
├── meta/ # 角色元数据(依赖、作者、版本等)
│ └── main.yml
└── README.md # 角色说明文档(可选)
5.3 Roles 应用
5.3.1 创建 Role
使用 ansible-galaxy init <rolename> 命令可以创建、管理自己的roles
mkdir ~/ansible/roles
ansible-galaxy init ~/ansible/roles/issue
tree ~/ansible/roles/issue/
会发现生成了这样结构的目录:

5.3.2 修改 Role
- 定义issue文件的模板文件
模板文件 templates/issue.j2:
[root@control ansible]# vim ~/ansible/roles/issue/templates/issue.j2
This is the system {{ ansible_hostname }}
Today's date is: {{ ansible_date_time.date }}
Contact to {{ admin }}
定义变量文件 vars/main.yml:
[root@control ansible]# vim ~/ansible/roles/issue/vars/main.yml
---
admin: yoyo@test.com
任务文件 tasks/main.yml(Role 中的任务文件不需要写 tasks: 关键词):
Role的各个文件之间相互调用不需要写路径
[root@control ansible]#vim ~/ansible/roles/issue/tasks/main.yml
---
- name: delever issue file
template:
src: issue.j2
dest: /etc/issue
5.3.3 在 Playbook 中调用 Role
方法一:在role同级目录创建 Playbook 直接调用。
方法二:在 ansible.cfg 中指定 roles_path=路径。
[root@control ansible]# vim ~/ansible/ansible.cfg
[defaults]
remote_user = alice
inventory = ~/ansible/hosts
roles_path = ~/ansible/roles #指定Roles读取位置
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
编写playbook文件,通过roles关键词调用role
[root@control ansible]# cat ~/ansible/issue.yml
---
- hosts: web2
roles: #调用Role
- issue
# - role2 #支持加载多个Role
[root@control ansible]# ansible-playbook issue.yml
调用 Role 的 Playbook:
---
- hosts: web2
roles:
- issue
# - role2 # 支持同时加载多个 Role
六、总结
本节重点掌握内容:
- 特殊模块:
setup、debug、template - 变量管理:Inventory 变量、Host Facts 变量、Playbook 变量、变量文件
- 高级语法:error 处理机制(
ignore_errors)、handlers、when 条件判断、block 任务块、loop 循环 - Roles:角色作用、标准目录结构、角色创建与调用
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)