首页 > 其他分享 >关于如何控制Ansible Playbook的执行顺序、运行选定的剧本资源的一些笔记

关于如何控制Ansible Playbook的执行顺序、运行选定的剧本资源的一些笔记

时间:2022-12-12 15:35:28浏览次数:46  
标签:tasks tags changed 笔记 task Ansible role Playbook servera


写在前面

  • 和小伙伴们分享一些​​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 [Gathering Facts] *******************************************************************************
ok: [serverb.lab.example.com]
TASK [pre_tasks in task] *****************************************************************************
changed: [serverb.lab.example.com]
TASK [tasks_hook_demo : shell] ***********************************************************************
changed: [serverb.lab.example.com]
TASK [task] ******************************************************************************************
changed: [serverb.lab.example.com]
TASK [post_tasks in taks] ****************************************************************************
changed: [serverb.lab.example.com]
PLAY RECAP *******************************************************************************************
serverb.lab.example.com : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

上面的执行顺序为 ​​pre_tasks > roles > tasks > post_tasks​​,不管语句顺序如何变化,都不会改变执行顺序

这里,有小伙伴会想到,如果任务中有handlers应该如何处理?

有​​handlers​​的执行顺序

Ansible 按照以下顺序运行 Play 的不同部分:

  • ​pre_tasks​
  • ​pre_tasks ​​​部分中通知的处理程序​​ handlers​
  • ​roles​
  • ​tasks​
  • ​roles​​​ 和 ​​tasks​​​ 部分中通知的处理程序​​ handlers​
  • ​post_tasks​
  • ​post_tasks​​​ 部分中通知的处理程序​​ handlers​

这些部分在 Play 中的编写顺序不会修改以上列出的执行顺序。来看一个demo

这里先创建一个角色,使用shell 模块打印一句话

$ansible-galaxy  init task_liruilong_exec_order --init-path=roles
- task_liruilong_exec_order was created successfully
$ansible-galaxy list | grep liruilo
- task_liruilong_exec_order, (unknown version)
$tee roles/task_liruilong_exec_order/tasks/main.yml <<- EOF
> ---
> - name: role: task_liruilong_exec_order
> shell: echo "roles liruilong"
> EOF
---
- name: role task_liruilong_exec_order
shell: echo "roles liruilong"

然后我在角色中添加一个通知,在角色的handlers文件夹中定义执行的事件,在taks发生chagent的时候会触发通知,执行通知的handler

$vim roles/task_liruilong_exec_order/tasks/main.yml
$cat roles/task_liruilong_exec_order/tasks/main.yml
---
- name: role task_liruilong_exec_order
shell: echo "roles liruilong"
notify:
- role_handler
$vim roles/task_liruilong_exec_order/handlers/main.yml
$cat roles/task_liruilong_exec_order/handlers/main.yml
---
# handlers file for task_liruilong_exec_order
- name: role_handler
shell: echo 'role in role_handler'
$vim task_order.yaml

来看下的剧本,在pre_tasks和post_tasks,tasks中,都有剧本中handler的notify。

$cat task_order.yaml
---
- name: tesk task exec order
hosts: servera
pre_tasks:
- name: task in pre_taks
shell: echo 'task in pre_tasks'
notify:
- ploybook_in_handler
roles:
- role: task_liruilong_exec_order

tasks:
- name: task in tasks
shell: echo 'task in tasks'
notify:
- ploybook_in_handler

post_tasks:
- name: taks in post_tasks
shell: echo 'task in post_tasks'
notify:
- ploybook_in_handler

handlers:
- name: ploybook_in_handler
shell: echo ' ploybook in handle'

执行剧本,可以看到剧本你的执行顺序为:​​pre_taks > pre_taks handler > role > tasks > role handler > tasks handler > post_tasks > post_tasks handler​

$ansible-playbook  task_order.yaml
PLAY [tesk task exec order] **************************************************************************
TASK [Gathering Facts] *******************************************************************************ok: [servera]
TASK [task in pre_taks] ******************************************************************************changed: [servera]
RUNNING HANDLER [ploybook_in_handler] ****************************************************************changed: [servera]
TASK [task_liruilong_exec_order : role task_liruilong_exec_order] ***********************************changed: [servera]
TASK [task in tasks] *********************************************************************************changed: [servera]
RUNNING HANDLER [task_liruilong_exec_order : role_handler] *******************************************changed: [servera]
RUNNING HANDLER [ploybook_in_handler] ****************************************************************changed: [servera]
TASK [taks in post_tasks] ****************************************************************************changed: [servera]
RUNNING HANDLER [ploybook_in_handler] ****************************************************************changed: [servera]
PLAY RECAP *******************************************************************************************servera : ok=9 changed=8 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

这里要说明的是剧本中这些块的顺序不会改变执行顺序。一个剧本包含pre_tasks,roles,tasks,post tasks和handlers部分是不寻常的。如果在多个部分中得到通知,则处理程序handler可以在剧本执行期间的不同时间多次运行。但是不会存在 一个处理的程序连续执行多次,即同一时间段,多此次通知只执行一次。

那么这里的话,如果希望某一任务在发生change后,同时通知多个处理程序handler,应该如何处理

任务一次性通知多个handlers任务

  • ​按名称通知handlers列表。​
  • ​通知配置了listen来监听多个handlers​

下面的剧本中,notify部分并没有发生改变,但是​​handlers​​​部分添加一个​​listen​​​属性,即由原来的​​通知变成了监听​​​,这里其实有点类似观察者设计模式的两种实现方式​​,当观察者的数据较少的时候,采用的是推的一种方式,较多时,通过拉的方式实现。​

---
- name: tesk task exec order
hosts: servera
pre_tasks:
- name: task in pre_taks
shell: echo 'task in pre_tasks'
notify:
- ploybook_in_handler
roles:
- role: task_liruilong_exec_order

tasks:
- name: task in tasks
shell: echo 'task in tasks'
notify:
- ploybook_in_handler

post_tasks:
- name: taks in post_tasks
shell: echo 'task in post_tasks'
notify:
- ploybook_in_handler

handlers:
- name: ploybook_in_handler 1
shell: echo ' ploybook in handle 1'
listen: ploybook_in_handler
- name: ploybook_in_handler 2
shell: echo ' ploybook in handle 2'
listen: ploybook_in_handler
$ansible-playbook  task_order.yaml
PLAY [tesk task exec order] ***************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [servera]
TASK [task in pre_taks] *******************************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 1] ***************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 2] ***************************************************************
changed: [servera]
TASK [task_liruilong_exec_order : role task_liruilong_exec_order] ************************************
changed: [servera]
TASK [task in tasks] **********************************************************************************
changed: [servera]
RUNNING HANDLER [task_liruilong_exec_order : role_handler] ********************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 1] ***************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 2] ***************************************************************
changed: [servera]
TASK [taks in post_tasks] *****************************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 1] ***************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 2] ***************************************************************
changed: [servera]
PLAY RECAP ********************************************************************************************
servera : ok=12 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

当然,有小伙伴说,我不嫌麻烦,我就想每次推,那么应该如何实现,可以在notafy写成一个list

$cat  roles/task_liruilong_exec_order/tasks/main.yml
---
- name: role task_liruilong_exec_order
shell: echo "roles liruilong"
notify:
- role_handler1
- role_handler2

$cat roles/task_liruilong_exec_order/handlers/main.yml
---
# handlers file for task_liruilong_exec_order
- name: role_handler1
shell: echo 'role in role_handler 1'
- name: role_handler2
shell: echo 'role in role_handler 2'
$

这样也是可以的

$ansible-playbook task_order.yaml
PLAY [tesk task exec order] ***************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [servera]
.........
TASK [task_liruilong_exec_order : role task_liruilong_exec_order] ************************************
changed: [servera]
TASK [task in tasks] **********************************************************************************
changed: [servera]
RUNNING HANDLER [task_liruilong_exec_order : role_handler1] *******************************************
changed: [servera]
RUNNING HANDLER [task_liruilong_exec_order : role_handler2] *******************************************
changed: [servera]
.....

在上面的Demo中,可以看到,任务处理程序 handler 总是在一个任务块处理完之后才会执行,那如果我希望在任务执行到一半,执行前面的任务处理程序,应该这么处理?

立即运行剧本中特定任务通知的任何处理程序

若要立即运行由 Play 中特定任务通知的任何处理程序,可以添加一个使用​​ meta 模块​​​及​​ flush_handlers 参数​​任务:

​meta: flush_handlers​

---
- name: tesk task exec order
hosts: servera
pre_tasks:
- name: task in pre_taks
shell: echo 'task in pre_tasks'
notify:
- ploybook_in_handler
roles:
- role: task_liruilong_exec_order

tasks:
- name: task in tasks 1
shell: echo 'task in tasks 1'
notify:
- ploybook_in_handler

- meta: flush_handlers

- name: task in tasks 2
shell: echo 'task in tasks 2'
notify:
- ploybook_in_handler

post_tasks:
- name: taks in post_tasks
shell: echo 'task in post_tasks'
notify:
- ploybook_in_handler

handlers:
- name: ploybook_in_handler 1
shell: echo ' ploybook in handle 1'
listen: ploybook_in_handler
- name: ploybook_in_handler 2
shell: echo ' ploybook in handle 2'
listen: ploybook_in_handler
$ ansible-playbook  task_order.yaml
PLAY [tesk task exec order] **************************************************************************
TASK [Gathering Facts] *******************************************************************************
ok: [servera]
TASK [task in pre_taks] ******************************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 1] **************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 2] **************************************************************
changed: [servera]
TASK [task_liruilong_exec_order : role task_liruilong_exec_order] ***********************************
changed: [servera]
TASK [task in tasks 1] *******************************************************************************
changed: [servera]
RUNNING HANDLER [task_liruilong_exec_order : role_handler1] ******************************************
changed: [servera]
RUNNING HANDLER [task_liruilong_exec_order : role_handler2] ******************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 1] **************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 2] **************************************************************
changed: [servera]
TASK [task in tasks 2] *******************************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 1] **************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 2] **************************************************************
changed: [servera]
TASK [taks in post_tasks] ****************************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 1] **************************************************************
changed: [servera]
RUNNING HANDLER [ploybook_in_handler 2] **************************************************************
changed: [servera]
PLAY RECAP *******************************************************************************************
servera : ok=16 changed=15 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$

控制主机执行顺序

Ansible根据剧本hosts指令确定要管理的主机。默认情况下,Ansible2.4和更高版本​​根据清单中主机列出的顺序运行剧本​​​。您可以使用​​order指令​​更改该顺序。

order指令接受以下值:

  • ​inventory​​ 清单顺序。这是默认值。
  • ​reverse_inventory​​ 清单相反顺序。
  • ​sorted​​ 主机按字母顺序排列。数字在字母前排序。
  • ​reverse_sorted​​ 主机以相反的字母顺序排列。
  • ​shuffle​​ 每次您执行剧本时,随机排序。

由于Ansible通常在多个主机上并行运行每个任务,因此 ansible-playbook 命令的输出可能无法反映预期的顺序:​​输出显示的是任务完成顺序,而不是执行顺序。​

来看一个Demo,下面的配置中,定义了​​order: sorted​​的顺序执行主机

$ cat  host-all.yaml
---
- name: Executing a role as a task
hosts: all
order: sorted
tasks:
- name: A normal task
debug:
msg: 'first task'

可以看到执行顺序即为字母顺序

$ ansible-playbook  host-all.yaml -f 1
PLAY [Executing a role as a task] *********************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [servera]
ok: [serverb]
ok: [serverc]
ok: [serverd]
ok: [servere]
ok: [serverf]
TASK [A normal task] **********************************************************************************
ok: [servera] => {
"msg": "first task"
}
ok: [serverb] => {
"msg": "first task"
}
ok: [serverc] => {
"msg": "first task"
}
ok: [serverd] => {
"msg": "first task"
}
ok: [servere] => {
"msg": "first task"
}
ok: [serverf] => {
"msg": "first task"
}
PLAY RECAP ********************************************************************************************
servera : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverc : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverd : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
servere : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverf : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

主机执行顺序为随机执行

$ cat host-all.yaml
---
- name: Executing a role as a task
hosts: all
order: shuffle
tasks:
- name: A normal task
debug:
msg: 'first task'
$ ansible-playbook  host-all.yaml -f 1

PLAY [Executing a role as a task] *********************************************************************

TASK [Gathering Facts] ********************************************************************************
ok: [serverb]
ok: [serverc]
ok: [servere]
ok: [serverf]
ok: [serverd]
ok: [servera]

TASK [A normal task] **********************************************************************************
ok: [serverf] => {
"msg": "first task"
}
ok: [servera] => {
"msg": "first task"
}
ok: [servere] => {
"msg": "first task"
}
ok: [serverd] => {
"msg": "first task"
}
ok: [serverb] => {
"msg": "first task"
}
ok: [serverc] => {
"msg": "first task"
}

PLAY RECAP ********************************************************************************************
servera : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverb : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverc : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverd : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
servere : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
serverf : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

来看一个具体的实际应用。

实战

$ cat deploy_haproxy.yml
- name: Ensure HAProxy is deployed
hosts: lb_servers
force_handlers: True

pre_tasks:
- name: Setting the maintenance message
copy:
dest: /etc/motd.d/maintenance
content: "Maintenance in progress\n"

roles:
# The "haproxy" role has a dependency on the "firewall" role.
# The "firewall" role requires a "firewall_rules" variable be defined.
- role: haproxy

post_tasks:
- name: Removing the maintenance message
file:
path: /etc/motd.d/maintenance
state: absent

handlers:
- name: Sending an email to student
mail:
subject: "HAProxy reloaded on {{ inventory_hostname }}"
to: [email protected]
delegate_to: localhost
become: false
listen: reload haproxy

- name: Logging a message to syslog
syslogger:
msg: "HAProxy reloaded on {{ inventory_hostname }}"
delegate_to: localhost
become: false
listen: reload haproxy

上面的剧本在执行haproxy这个角色的时候,通过​​pro_taks​​​添加登录时的欢迎信息,提示系统正在维护,维护信息写到 ​​/etc/motd.d/maintenance​​​,同时在角色执行完后,在​​post_tasks​​中删除欢迎信息。

同时监听角色中的haproxy services文件重新加载的事件,当服务配置文件重新load的时候,发送邮件,并且写入系统日志

$ cat roles/haproxy/handlers/main.yml
---
# handlers file for haproxy


- name: restart haproxy
service:
name: haproxy
state: restarted

- name: reload haproxy
service:
name: haproxy
state: reloaded

$

执行剧本

$ ansible-playbook  deploy_haproxy.yml
PLAY [Ensure HAProxy is deployed] *********************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [servera.lab.example.com]
TASK [Setting the maintenance message] ****************************************************************
changed: [servera.lab.example.com]
TASK [firewall : Ensure Firewall Sources Configuration] ***********************************************
ok: [servera.lab.example.com] => (item={'port': '80/tcp'})
TASK [haproxy : Ensure haproxy packages are present] **************************************************
changed: [servera.lab.example.com]
TASK [haproxy : Ensure haproxy is started and enabled] ************************************************
changed: [servera.lab.example.com]
TASK [haproxy : Ensure haproxy configuration is set] **************************************************
changed: [servera.lab.example.com]
RUNNING HANDLER [haproxy : reload haproxy] ************************************************************
changed: [servera.lab.example.com]
RUNNING HANDLER [Sending an email to student] *********************************************************
ok: [servera.lab.example.com]
RUNNING HANDLER [Logging a message to syslog] *********************************************************
changed: [servera.lab.example.com]
TASK [Removing the maintenance message] ***************************************************************
changed: [servera.lab.example.com]
PLAY RECAP ********************************************************************************************
servera.lab.example.com : ok=10 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You have new mail in /var/spool/mail/student

登录机器可以发现系统维护的信息,等角色执行完。维护信息消失

$ ssh servera
Activate the web console with: systemctl enable --now cockpit.socket

Maintenance in progress
[student@servera ~]$ exit
logout
Connection to servera closed.
You have mail in /var/spool/mail/student
$ ssh servera
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Sat Aug 13 01:15:44 2022 from 172.25.250.9
[student@servera ~]$

可以看到邮件信息

$ mail
Heirloom Mail version 12.5 7/5/10. Type ? for help.
"/var/spool/mail/student": 1 message 1 new
>N 1 [email protected] Sat Aug 13 01:10 27/1102 "HAProxy reloaded on servera.lab.example.com"
& q
Held 1 message in /var/spool/mail/student

博文参考

​《Red Hat Ansible Engine 2.8 DO447》​


标签:tasks,tags,changed,笔记,task,Ansible,role,Playbook,servera
From: https://blog.51cto.com/u_13474506/5929826

相关文章

  • Ansible如何使用lookup插件模板化外部数据
    写在前面今天和小伙伴分享使用lookup插件模板化外部数据博文内容比较简单主要介绍的常用lookup插件和对应的Demo外部数据如何代替cat等通过lookup插件读取理解不足小伙伴帮......
  • 《Maven实战》读书笔记
    嗯,实训的时候第一次接触maven,感觉好方便,嗯,所以假期的时候刷这本书,好累呀,是心里不大舒服,加油,未来。                       ......
  • 关于Linux中Keepalived高可用热备自动化部署的一些笔记
    写在前面今天和小伙伴们分享一些Keepalived相关笔记博文内容涉及:vrrp协议由来Ansible方式Keepalived安装部署Keepalived脚本方式配置服务检查Keepalived自动化部......
  • 《java8高级应用与开发》读书笔记(二)
    写在前面本笔记涉及内容:类加载、反射、枚举、注解、国际化、格式化类加载:是指将类的class文件读入内存,并为之创建一个Java.lang.class对象。即当线程使用任何一个类时,系统都......
  • Spring Cloud 学习笔记
     笔记是看了江南一点雨的课,然后做的,基本上是上课笔记的东西,实践多一点,然后结合《深入理解SpringCloud与微服务构建》一书,自己做了一些记载。有看不懂的大家可以留言一起......
  • 关于Linux中通过 Systemd Path Unit 监听配置更新自动重启服务的一些笔记
    写在前面在​​stackoverflow.com​​​的​​Unix&Linux​​社区看到有小伙伴提出相关的问题。这里整理分享给小伙伴,博文内容涉及:​​Systemd​​​的​​Pat......
  • 关于Linux挂载 fstab 和 systemd.mount 使用场景的一些笔记
    写在前面在​​stackoverflow​​​的​​Unix&Linux​​社区看到相关的问题。有大佬做了解答,感觉问题不错,答案也不错,整理分享给小伙伴原问题地址:​​tmpontmpfs:fs......
  • 关于 Linux中systemd的一些笔记
    写在前面嗯,准备​​RHCA​​,学习整理这部分知识博文内容涉及:​​systemd​​简述对于​​unit​​的信息的介绍通过​​systemctl​​​命令控制​​Serviceunit​​......
  • 《云原生入门级开发者认证》学习笔记之云原生基础设施之容器技术
    写在前面嗯,报了考试,整理课堂笔记记忆学习的原因:虽然考了​​CKA​​​,了解了一些​​K8s​​相关的知识但是对​​云原生​​整个体系一直都很模糊希望对云原生有一个......
  • 如何编写清晰的Ansible剧本(复杂剧本如何构建)
    写在前面嗯,学习Ansible高级特性,整理这部分笔记博文内容涉及复杂Ansible剧本的编写规范一个具体的编写Demo食用方式:理论有些枯燥,不感兴趣小伙伴可以直接跳过去看Demo......