如何控制Ansible Playbook的执行顺序运行选定的剧本资源
Posted 山河已无恙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何控制Ansible Playbook的执行顺序运行选定的剧本资源相关的知识,希望对你有一定的参考价值。
写在前面
- 和小伙伴们分享一些
Ansible
中Playbook
执行顺序控制的手段以及运行选定的任务的方法 - 不知道小伙伴们有么有遇到这样的情况
- 一些运维场景,
Github
中找了很棒的剧本或者角色,但是只需要其中的一部分 - 一般情况下只能重新编辑(注释或者删掉)剧本处理,往往需要多次调整剧本,很麻烦
- 亦或是一个有角色的剧本,你希望先执行任务,在执行角色(默认角色总是先执行)
- 亦或是某些剧本你希望脱离编写顺序执行,自定义执行顺序
- 亦或是你希望同时通知多个
handler
,处理程序被通知后立即执行,而不是等role、tasks
执行完统一执行等等 - 上面的问题都有解决办法,但是
Ansible
本身提供了很多更优的解决方法,通过博文内容一起来学习下,涉及内容:- 通过标记
tags
仅运行标有特定标签的任务,或者从特定的任务开始执行Playbook
- 通过
include_role && import_role
作为任,控制角色执行顺序 - 通过
pre_task || post_task
控制任务执行前后的回调处理 - 通过
listen
来监听多个handlers
- 通过
meta: flush_handlers
立即运行通知的handlers
- 通过标记
- 食用方式
- 了解
Ansible
基础知识 - 可以编写
Ansible Playbook
、role
- 了解
role
构成,剧本常见指令(语法)
- 了解
- 理解不足小伙伴帮忙指正
博文使用的ansibler
版本
$ansible --version
ansible 2.8.0rc1
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/student/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = 3.6.8 (default, Apr 3 2019, 17:26:03) [GCC 8.2.1 20180905 (Red Hat 8.2.1-3)]
$
如果我会发光,就不必害怕黑暗。——王小波
对 Ansible 剧本资源打标签
在处理大型或复杂的剧本时,如果只希望运行部分剧本或部分任务
。可以将标签
应用于可能要跳过或运行的特定资源。
通过标签来标记资源
,在资源上使用tags关键字
,然后是要应用的标记列表。在Ansible
中tags
标记可用于下列资源:
- 每个
任务
,这是使用标签的最常见方式之一。 - 整个
剧本
,在剧本级别使用标签指令。 - 标记
include_tasks
任务。include_tasks
加载的所有任务都与此标签关联。 角色
,角色中的所有任务都与此标签关联。任务块
,块中的所有任务都与此标签关联。
看一个Demo,上面的标记依次来看体验下。在这之前,先准备一个角色,角色做一个echo的动作,
$ansible-galaxy init tag_role --init-path=roles
- tag_role was created successfully
$ansible-galaxy list | grep tag
- tag_role, (unknown version)
$cat roles/tag_role/tasks/main.yml
---
# tasks file for tag_role
- name: tags roles
shell: echo 'tasks for tag_role'
编写一个剧本,在不同剧本资源执行块打上标签
---
- name: tags Demo 1
hosts: servera
# 标记整个剧本
tags:
- play-tag-1
roles:
- role: tag_role
# 标记角色
tags:
- role-tags
tasks:
- name: task 1 tag
shell: echo 'tags to task 1'
# 标记每个任务
tags:
- task-tags-1
- name: include or import a tasks file
include_tasks:
file: tasks_file
# 标记include_tasks任务
tags:
- include-import
- block:
- name: task 1 in block
shell: echo 'task 1 in block'
- name: task 2 in block
shell: echo 'task 2 in block'
# 标记任务块
tags:
- block-tags
- name: tags Demo 2
hosts: servera
# 标记整个剧本
tags:
- play-tag-2
tasks:
- name: task 2 tag
shell: echo 'tags to task 2'
tags:
- task-tag-2
执行上面编写剧本,默认情况下打了标签,如果没有显示的指令或者设置特殊的标签,剧本默认依旧按照没打标签的顺序执行
$ansible-playbook tags.yaml
PLAY [tags Demo 1] ************************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [servera]
TASK [tag_role : tags roles] **************************************************************************
changed: [servera]
TASK [task 1 tag] *************************************************************************************
changed: [servera]
TASK [include or import a tasks file] ****************************************************************
included: /home/student/DO447/labs/task-execution/tasks_file for servera
TASK [task 1] *****************************************************************************************
changed: [servera]
TASK [task 2] *****************************************************************************************
changed: [servera]
TASK [task 1 in block] ********************************************************************************
changed: [servera]
TASK [task 2 in block] ********************************************************************************
changed: [servera]
PLAY [tags Demo 2] ************************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [servera]
TASK [task 2 tag] *************************************************************************************
changed: [servera]
PLAY RECAP ********************************************************************************************
servera : ok=10 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
下面来看下如何管理标记资源,选择执行剧本资源
管理标记的资源
要列出 Playbook 中的所有标记,使用--list-tags
选项
$ansible-playbook tags.yaml --list-tags
playbook: tags.yaml
play #1 (servera): tags Demo 1 TAGS: [play-tag-1]
TASK TAGS: [block-tags, include-import, play-tag-1, role-tags, task-tags-1]
play #2 (servera): tags Demo 2 TAGS: [play-tag-2]
TASK TAGS: [play-tag-2, task-tag-2]
$
可以看到,上面的剧本的标签构成:
- 剧本
tags Demo 1
包含标签TASK TAGS: [block-tags, include-import, play-tag-1, role-tags, task-tags-1]
- 剧本
tags Demo 2
包含标签play-tag-2, task-tag-2
当希望运行特定的剧本资源时,给对应的资源标记打标签,然后使用ansible-playbook
运行playbook
时,添加--tags
选项来筛选 playbook 仅运行带有特定标签的play 或任务
。
$ansible-playbook tags.yaml --tags=play-tag-2
PLAY [tags Demo 1] *************************************************************************************************
PLAY [tags Demo 2] *************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [servera]
TASK [task 2 tag] **************************************************************************************************
changed: [servera]
PLAY RECAP *********************************************************************************************************
servera : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
运行指令里添加了 --tags=play-tag-2
,即只运行剧本tags Demo 2
,当需要运行多个标签时,之间逗号隔开
$ansible-playbook tags.yaml --tags=block-tags,role-tags
PLAY [tags Demo 1] *************************************************************************************************
TASK [tag_role : tags roles] ***************************************************************************************
changed: [servera]
TASK [task 1 in block] *********************************************************************************************
changed: [servera]
TASK [task 2 in block] *********************************************************************************************
changed: [servera]
PLAY [tags Demo 2] *************************************************************************************************
PLAY RECAP *********************************************************************************************************
servera : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
当希望运行大多数剧本资源,个别剧本资源不运行,可以在运行ansible-playbook
命令时,使用--skip-tags
选项跳过带有特定标签的任务。
$ansible-playbook tags.yaml --list-tags
playbook: tags.yaml
play #1 (servera): tags Demo 1 TAGS: [play-tag-1]
TASK TAGS: [block-tags, include-import, play-tag-1, role-tags, task-tags-1]
play #2 (servera): tags Demo 2 TAGS: [play-tag-2]
TASK TAGS: [play-tag-2, task-tag-2]
$ansible-playbook tags.yaml --skip-tags play-tag-1
PLAY [tags Demo 1] *************************************************************************************************
PLAY [tags Demo 2] *************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [servera]
TASK [task 2 tag] **************************************************************************************************
changed: [servera]
PLAY RECAP *********************************************************************************************************
servera : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
特殊的标签
如果有些剧本资源,你希望它始终运行,或是希望它始终不运行,即使在你使用tags、skip-tags
指定标签的情况下,Ansible 这两种场景中提供了特殊标记:
always
:带有 always 标记的资源始终都会运行,除非明确指定--skip-tags always
选项。never
:带有 never 特殊标记的资源不会运行,除非明确指定--tags never
选项。
看一个Demo
$cat tags-all.yaml
---
- name: tags Demo 1
hosts: servera
tags:
- play-tag-1
- never
roles:
- role: tag_role
tags:
- role-tags
tasks:
- name: task 1 tag
shell: echo 'tags to task 1'
tags:
- task-tags-1
- name: include or import a tasks file
include_tasks:
file: tasks_file
tags:
- include-import
- block:
- name: task 1 in block
shell: echo 'task 1 in block'
- name: task 2 in block
shell: echo 'task 2 in block'
tags:
- block-tags
- name: tags Demo 2
hosts: servera
tags:
- always
tasks:
- name: task 2 tag
shell: echo 'tags to task 2'
可以看到剧本1设置never
标签,所以默认总不会执行,剧本2设置always
,所以默认总会执行
$ansible-playbook tags-all.yaml
PLAY [tags Demo 1] *************************************************************************************************
PLAY [tags Demo 2] *************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [servera]
TASK [task 2 tag] **************************************************************************************************
changed: [servera]
PLAY RECAP *********************************************************************************************************
servera : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
执行输出可以看到,剧本1没有执行,剧本2默认执行,这里,可能有小伙伴会说,如果我希望打标签的资源全部执行或者全部不执行,但是我的标签太多了,都写上很麻烦,况且我还有一些没有打标签的任务,我应该如何处理,Ansible
在这些场景中提供了一些指令参数。
命令行指定标签时的特定参数:
tagged
标记将运行任何带有显式标记的资源untagged
标记将运行不带有显式标记的资源all
参数将包括 Play 中的所有任务,无论是否带有标记,这是默认行为。
在来看看Demo
$cat tags-all.yaml
---
- name: tags Demo 1
hosts: servera
tags:
- play-tag-1
roles:
- role: tag_role
tags:
- role-tags
tasks:
- name: task 1 tag
shell: echo 'tags to task 1'
tags:
- task-tags-1
- name: include or import a tasks file
include_tasks:
file: tasks_file
tags:
- include-import
- block:
- name: task 1 in block
shell: echo 'task 1 in block'
- name: task 2 in block
shell: echo 'task 2 in block'
tags:
- block-tags
- name: tags Demo 2
hosts: servera
tasks:
- name: task 2 tag
shell: echo 'tags to task 2'
$
tagged 标记
将运行任何带有显式标记的资源,会发现,剧本 tags Demo 2
的task 2 tag
任务没有标签,所以没有执行
$ansible-playbook tags-all.yaml --tags=tagged
PLAY [tags Demo 1] *************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [servera]
TASK [tag_role : tags roles] ***************************************************************************************
changed: [servera]
TASK [task 1 tag] **************************************************************************************************
changed: [servera]
TASK [include or import a tasks file] *****************************************************************************
included: /home/student/DO447/labs/task-execution/tasks_file for servera
TASK [task 1] ******************************************************************************************************
changed: [servera]
TASK [task 2] ******************************************************************************************************
changed: [servera]
TASK [task 1 in block] *********************************************************************************************
changed: [servera]
TASK [task 2 in block] *********************************************************************************************
changed: [servera]
PLAY [tags Demo 2] *************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [servera]
PLAY RECAP *********************************************************************************************************
servera : ok=9 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
untagged 标记
将运行不带有显式标记的资源,会发现只有剧本 tags Demo 2
的task 2 tag
任务执行了,因为他没有标签
$ansible-playbook tags-all.yaml --tags=untagged
PLAY [tags Demo 1] *************************************************************************************************
PLAY [tags Demo 2] *************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************
ok: [servera]
TASK [task 2 tag] **************************************************************************************************
changed: [servera]
PLAY RECAP *********************************************************************************************************
servera : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
控制任务执行
角色最先执行
在Playbook
中,Ansible
始终先执行角色中的任务,然后执行在tasks
部分下定义的任务,来看一个Demo
$cat deploy_apache_demo.yml
---
- name: Ensure Apache is deployed
hosts: all
gather_facts: no
tasks:
- name: Open the firewall
firewalld:
service: http
permanent: yes
state: enabled
roles:
- role: apache
上面的执行,可以看到执行数据顺序为,先执行firewall
角色,然后执行apache
角色,最后执行的是 Open the firewall
,
PLAY [Ensure Apache is deployed] *********************************************************************
.....
TASK [firewall : Ensure Firewall Sources Configuration] **********************************************
.....
TASK [apache : Ensure httpd packages are installed] **************************************************
.....
TASK [apache : Ensure SELinux allows httpd connections to a remote database] *************************
.....
TASK [apache : Ensure httpd service is started and enabled] ******************************************
.....
TASK [Open the firewall] *****************************************************************************
.....
PLAY RECAP *******************************************************************************************
剧本里没有定义firewall,为什么会执行,是因为apache角色依赖了他,可以在apache角色的meta目录的maia.ymal 文件下面看到,它依赖了firewall角色
$cat roles/apache/meta/main.yml | grep -C 2 firewall
dependencies:
- name: firewall
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.
$
所以不管剧本编写顺序如何,同一剧本中执行顺序为,依赖角色要在当前角色之前执行,当前角色role要在调用剧本任务task之前执行。
为了剧本的可读性,一般情况下,剧本任务是写在角色后面的,整个书写顺序也就是执行顺序。
那么,如果希望在角色执行前执行任务,应该如何处理,有两种方法
- 其一是使用task钩子,类似生命周期中的回调函数一样,
- 另一钟方法,即下面提到的,使用
import或者include
,关于这两个动作,小伙伴们一定不陌生,前端常见的模板引擎一般都会涉及到。不同的动作,实相同的功能,但是原理是不同的
import 或 include 作为任务的角色
Ansible的最新版本允许将角色作为任务来包含或导入
,而不是使用play中的角色部分
。通过这样的方式,可以使剧本按照编写的顺序执行,而不是先执行角色的方式
。
- 优点是可以按照编写顺序运行一组任务、导入或包含一个角色,然后运行更多的任务。
- 缺点是,在没有仔细检查的情况下,可能不太清楚您的剧本使用的是哪些角色,因为角色切入了任务内部
import和include 有些许区别
- 使用
include_role
模块可以动态
包含角色, - 使用
import_role
模块则可静态
导入角色。
创建一个角色,执行的任务为打印当前的主机名
$ansible-galaxy init role_tasks_demo
$echo "- shell: hostname" > roles/role_tasks_demo/tasks/main.yml
编写剧本,两种不同的方式引入角色
---
- name: Executing a role as a task
hosts: a_web_servers
tasks:
- name: A normal task
debug:
msg: 'first task'
- name: A task to import_role role_tasks_demo here
import_role:
name: role_tasks_demo
- name: A task to include role_tasks_demo here
include_role:
name: role_tasks_demo
- name: Another normal task
debug:
msg: 'second task'
执行剧本
$ansible-playbook role_tasks.yaml
PLAY [Executing a role as a task] ********************************************************************
TASK [Gathering Facts] *******************************************************************************
ok: [serverb.lab.example.com]
TASK [A normal task] *********************************************************************************
ok: [serverb.lab.example.com] =>
"msg": "first task"
TASK [role_tasks_demo : shell] ***********************************************************************
changed: [serverb.lab.example.com]
TASK [A task to include role_tasks_demo here] ******************************************************
TASK [role_tasks_demo : shell] ***********************************************************************
changed: [serverb.lab.example.com]
TASK [Another normal task] ***************************************************************************
ok: [serverb.lab.example.com] =>
"msg": "second task"
PLAY RECAP *******************************************************************************************
serverb.lab.example.com : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以看到,按照任务编写的顺序执行,角色执行了两次,这里需要注意一点,通过 import_role
方式导入的角色并会作为当前剧本的一部分,而通过 include
的方式会作为一个单独的任务模块来执行,我么通过执行的输出也可以看到,具体的原因:
-
使用
import_role
时,ansible-playbook
命令首先解析角色并插入到play
中,然后开始执行。Ansible
会立即检测和报告语法错误,不会开始执行playbook。 -
使用
include_role
时,Ansible 会在 play 执行期间到达 include_role 任务时解析角色并插⼊到 play 中。如果Ansible 检测到角色中存在语法错误,则中止执行 playbook 。
对于 when 指令
的行为有所不同。使用include_role
任务时,如果when
指令中的条件为 false,则 Ansible不解析角色。看一个Demno
$cat role_tasks.yaml
---
- name: Executing a role as a task
hosts: servera
tasks:
- name: A normal task
debug:
msg: 'first task'
- name: import_role role
import_role:
name: role_tasks_demo
when: false
- name: include_role role
include_role:
name: role_tasks_demo
- name: Another normal task
debug:
msg: 'second task'
执行我么可以看到。import_role
被直接跳过了,因为when的原因,并没有执行
$ansible-playbook role_tasks.yaml
PLAY [Executing a role as a task] *********************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [servera]
TASK [A normal task] **********************************************************************************
ok: [servera] =>
"msg": "first task"
TASK [role_tasks_demo : shell] ************************************************************************
skipping: [servera]
TASK [include_role role] ******************************************************************************
TASK [role_tasks_demo : shell] ************************************************************************
changed: [servera]
TASK [Another normal task] ****************************************************************************
ok: [servera] =>
"msg": "second task"
PLAY RECAP ********************************************************************************************
servera : ok=4 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
角色执行前&&任务执行后的钩子
有时候希望⼀个剧本 在角色之前运行某些任务
,以及它们所通知的处理程序。也可能希望在普通任务tasks
和处理程序handler
运行后运行 play 中的任务
。
可以使用两个指令
(而非 tasks
)来实现这一目标:
pre_tasks
是在roles 部分前
运行的tasks 部分
。post_tasks
是在tasks 部分
以及tasks 所通知的任何处理程序后
运行的tasks 部分
。
通过Demo来看下,创建一个测试角色,同样打印主机名
$ansible-galaxy init tasks_hook_demo
$echo "- shell: hostname" > roles/tasks_hook_demo/tasks/main.yml
编写剧本,使用 pro_taks
和post_task
来执行剧本之前前后的钩子任务
---
- name: task task exec order
hosts: a_web_servers
pre_tasks:
- name: pre_tasks in task
shell: echo 'pre_tasks'
post_tasks:
- name: post_tasks in taks
shell: echo 'post_tasks'
tasks:
- name: task
shell: echo 'tasks'
roles:
- tasks_hook_demo
可以看到编写顺序并不会影响到执行顺序
$ansible-playbook tasks_hook.yaml
PLAY [task task exec order] **************************************************************************
TASK 关于如何控制Ansible Playbook的执行顺序运行选定的剧本资源的一些笔记