Ansible: YAML语法与 Playbook写法解析
Ansible Playbook采用YAML语法编写,通过简洁的格式实现JSON风格的事件描述,是实现自动化运维的核心载体。在学习Playbook前,需先掌握YAML的基础语法与引用规则,再结合Playbook的结构与功能完成自动化配置。以下将按原内容逻辑,系统梳理YAML语法、Playbook组成、使用方法及实战示例。
一、YAML语法初步说明
在这里插入图片描述
文件标识
YAML文件可通过---
开头声明文件类型。即使省略---
,Ansible也能正常解析Playbook,但建议添加以保证规范性。注释规则
使用#
作为注释符,支持两种注释方式:- 整行注释:
# 这是一条整行注释,描述后续任务功能
- 行内注释:
name: install httpd # 行内注释:安装Apache服务
注释内容仅用于阅读,不会被Ansible执行。
- 整行注释:
字符串处理
YAML中字符串默认无需添加引号,即便包含空格、特殊符号(如-
、/
)也可直接书写。但引用变量时必须加引号,避免变量解析异常。布尔值格式
Playbook中布尔值(true/false)的解析分两种场景,需按规则区分使用:- 模块参数:布尔值作为字符串被Ansible解析,支持
yes/on/1/true/no/off/0/false
,例如yum
模块的update_cache: yes
。 - 非模块参数:布尔值由YAML解释器解析,遵循YAML原生语法,支持不区分大小写的
true/yes/on/y/false/no/off/n
,例如仓库配置的gpgcheck: no
、enabled: True
。
- 模块参数:布尔值作为字符串被Ansible解析,支持
二、Playbook的核心内容
每个Playbook由一个或多个“Play”组成,每个Play必须包含hosts
(目标主机)和tasks
(任务列表),是Playbook的执行核心:
- hosts:定义Inventory中待控制的主机/主机组(如
node1
、web_servers
),指定任务的执行范围。 - tasks:定义一系列任务列表,每个任务调用Ansible模块(如
yum
、file
)实现具体操作。
任务执行顺序:按列表顺序依次执行,所有被筛选的主机完成当前任务后,才会统一执行下一个任务。
需注意:hosts
指定的所有主机都会收到任务指令,但Ansible主控端会根据筛选规则(如主机组、变量条件)决定哪些主机实际执行任务,未被筛选的主机仅接收指令不执行操作。
三、YAML核心数据结构
YAML通过“字典”和“列表”两种数据结构描述信息,是Playbook配置的基础,所有任务、参数均基于这两种结构组合。
1. 字典(键值对)
字典又称散列、关联数组,用于描述“属性-值”对应关系,格式为key: value
(冒号后必须加空格,否则语法报错)。
常规写法(多行展开,适合参数较多的场景,易读性强):
name: svcrole svcservice: http svcport: 80
内嵌块格式(单行紧凑写法,适合参数较少的场景):
多个键值对用{}
包裹,键值对之间用“逗号+空格”分隔,例如:- {name: svcrole, svcservice: http, svcport: 80}
2. 列表(数组)
列表用于描述一组同类元素,格式为每个元素前加“短横线+空格”(-
),表示元素的并列关系。
- 常规写法(多行展开,适合元素较多的场景):
hosts: - server1 - server2
- 内嵌块格式(单行紧凑写法,适合元素较少的场景):
多个元素用[]
包裹,元素之间用“逗号+空格”分隔,例如:hosts: [server1, server2]
四、Playbook的使用方法
1. 基础执行命令
- 运行Playbook:
ansible-playbook 剧本文件名.yml
,例如ansible-playbook cy.yml
。 - 测试执行:
ansible-playbook -C 剧本文件名.yml
,-C
(Dry Run)表示模拟执行,仅输出执行结果预览,不实际修改目标主机配置,用于验证剧本语法与逻辑。
2. 依赖集合安装
若Playbook需使用特定模块(如community.general
中的模块),需先通过ansible-galaxy
安装对应集合,例如:
[student@master ansible]$ ansible-galaxy collection install http://ansible.example.com/materials/ansible-posix-1.5.1.tar.gz -p collections/
[student@master ansible]$ ansible-galaxy collection install http://ansible.example.com/materials/community-general-6.3.0.tar.gz -p collections/
-p collections/
指定集合安装路径,避免与系统默认集合冲突。
五、Playbook实战示例
示例1:部署Web服务(node1主机)
需求:在node1
上安装httpd、配置开机自启、创建软链接、下载首页文件、配置防火墙,确保Web服务可访问。
剧本文件(zhang3.yml
):
---
- name: http
hosts: node1
tasks:
- name: repo1 # 配置BaseOS仓库
yum_repository:
name: aa
description: aa1
baseurl: http://ansible.example.com/rhel9/BaseOS
enabled: yes
gpgcheck: yes
gpgkey: http://ansible.example.com/rhel9/RPM-GPG-KEY-redhat-release
- name: repo2 # 配置AppStream仓库
yum_repository:
name: cc
description: cc1
baseurl: http://ansible.example.com/rhel9/AppStream
enabled: yes
gpgcheck: yes
gpgkey: http://ansible.example.com/rhel9/RPM-GPG-KEY-redhat-release
- name: install httpd # 安装httpd
yum:
name: httpd
state: installed
- name: html # 创建测试网页文件
shell:
echo hello world > /var/www/html/index.html
- name: restart httpd # 重启httpd并设置开机自启
service:
name: httpd
state: restarted
enabled: yes
- name: link # 创建/var/www/html到/www的软链接
file:
src: /var/www/html
dest: /www
state: link
- name: get http # 下载首页文件到/www目录
get_url:
url: http://node1.example.com/index.html
dest: /www/
setype: httpd_sys_content_t
- name: firewall for http # 配置防火墙允许HTTP服务
firewalld:
service: http
state: enabled
permanent: yes
immediate: yes
运行playbook:
[student@master ansible]$ ansible-playbook zhang3.yml
PLAY [http] *************************************************************************
TASK [Gathering Facts] **************************************************************
ok: [node1]
TASK [repo1] ************************************************************************
ok: [node1]
TASK [repo2] ************************************************************************
ok: [node1]
TASK [install httpd] ****************************************************************
ok: [node1]
TASK [html] *************************************************************************
changed: [node1]
TASK [restart httpd] ****************************************************************
changed: [node1]
TASK [link] *************************************************************************
ok: [node1]
TASK [get http] *********************************************************************
ok: [node1]
TASK [firewall for http] ************************************************************
changed: [node1]
PLAY RECAP **************************************************************************
node1 : ok=9 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例2:notify与handlers用法(任务触发机制)
需求:创建用户user2
后,自动触发任务创建/tmp/cy1
文件(实现“完成A任务则执行B任务”的逻辑)。
剧本文件(test.yml
):
---
- name: this is a test playbook
hosts: node1
tasks:
- name: create user1 # 创建用户user1(无触发操作)
user:
name: user1
state: present
- name: create user2 # 创建用户user2(触发handlers)
user:
name: user2
state: present
notify: # 任务执行后,触发名为"file3"的handlers
- file3
handlers: # 定义handlers(仅被notify触发时执行)
- name: file3
file:
path: /tmp/cy1
state: touch
mode: 0644 # 设置文件权限:所有者读写,其他读
运行playbook:
[student@master ansible]$ ansible-playbook test.yml
PLAY [test] *************************************************************************
TASK [Gathering Facts] **************************************************************
ok: [node1]
TASK [create user1] *****************************************************************
ok: [node1]
TASK [create user2] *****************************************************************
ok: [node1]
PLAY RECAP **************************************************************************
node1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
handlers
特性:无论被notify
调用多少次,仅在当前Play的所有任务执行完毕后运行一次,避免重复操作。
示例3:用户管理与时间记录(galaxy.yml)
需求:仅在node1
执行,实现创建禁止登录的用户、记录系统时间、时间变化时创建文件。
剧本文件(galaxy.yml
):
---
- name: time
hosts: node1
tasks:
- name: create user # 创建用户aa(禁止登录,家目录/www)
user:
name: aa
shell: /sbin/nologin # 禁止用户登录系统
home: /www # 指定用户家目录
- name: create file # 在/www目录创建空文件html
file:
path: /www/html
state: touch # 创建空文件
- name: date # 将系统当前时间写入/www/html,时间变化时触发handlers
shell: date > /www/html # 执行date命令,将时间写入文件
notify:
- kk # 触发名为"kk"的handlers
handlers: # 定义handlers:时间变化时创建/tmp/kk
- name: kk
file:
path: /tmp/kk
state: touch
运行playbook:
[student@master ansible]$ ansible-playbook test.yml
PLAY [test] *************************************************************************
TASK [Gathering Facts] **************************************************************
ok: [node1]
TASK [create user1] *****************************************************************
ok: [node1]
TASK [create user2] *****************************************************************
ok: [node1]
PLAY RECAP **************************************************************************
node1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[student@master ansible]$ vim galaxy.yml
[student@master ansible]$ ansible-playbook galaxy.yml
PLAY [time] *************************************************************************
TASK [Gathering Facts] **************************************************************
ok: [node1]
TASK [create user] ******************************************************************
changed: [node1]
TASK [create file] ******************************************************************
changed: [node1]
TASK [date] *************************************************************************
changed: [node1]
RUNNING HANDLER [kk] ****************************************************************
changed: [node1]
PLAY RECAP **************************************************************************
node1 : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例4:tags用法(任务标签控制)
需求:给任务打标签,实现“指定执行部分任务”的功能,支持多标签、特殊标签。
1. 标签基础规则
一个任务可添加多个标签,用列表形式书写。
Ansible预置5个特殊标签,无需手动定义,直接使用:
特殊标签 功能说明 always
无论是否指定其他标签,该任务必执行 never
默认不执行,仅显式指定 -t never
时才执行tagged
ansible-playbook -t tagged 剧本.yml
:仅执行带标签的任务(排除never
)untagged
ansible-playbook -t untagged 剧本.yml
:仅执行不带标签的任务(含always
)all
执行所有任务,为默认行为,无需显式指定 多标签使用:执行多个标签的任务时,标签之间用“逗号+空格”分隔,例如
ansible-playbook --tags l1,l2 剧本.yml
。
2. 实战剧本(test2.yml)
---
- name: test playbook
hosts: node1
tasks:
- name: create user3 # 创建user3,标签l1、l2
user:
name: user3
state: present
tags:
- l1
- l2
- name: create user4 # 创建user4,标签l2、l3
user:
name: user4
state: present
tags:
- l2
- l3
- name: create user5 # 创建user5,标签l3、l4
user:
name: user5
state: present
tags:
- l3
- l4
- name: create user6 # 创建user6,标签always(必执行)
user:
name: user6
state: present
tags:
- always
- name: create user7 # 创建user6,标签never(默认不执行)
user:
name: user7
state: present
tags:
- never
运行playbook:
默认运行所有任务,never标签(create user7)默认不运行
[student@master ansible]$ ansible-playbook test2.yml
PLAY [test playbook] ****************************************************************
TASK [Gathering Facts] **************************************************************
ok: [node1]
TASK [create user3] *****************************************************************
ok: [node1]
TASK [create user4] *****************************************************************
ok: [node1]
TASK [create user5] *****************************************************************
ok: [node1]
TASK [create user6] *****************************************************************
ok: [node1]
PLAY RECAP **************************************************************************
node1 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3. 常用标签命令
# 1. 仅执行标签为l2的任务(create user3、create user4、record log)
[student@master ansible]$ ansible-playbook test2.yml -t l2
# 2. 跳过标签为l1的任务,执行其他任务(排除never)
[student@master ansible]$ ansible-playbook --skip-tags l1 test2.yml
# 3. 仅执行带标签的任务(排除never)
[student@master ansible]$ ansible-playbook --tags tagged test2.yml
# 4.仅执行带never标签的任务
[student@master ansible]$ ansible-playbook test2.yml -t never
# 5. 查看剧本中所有标签
[student@master ansible]$ ansible-playbook --list-tags test2.yml