一个好的剧本,执行起来会很是丝滑,良好的执行体验让你甚至感觉不到执行了很久,哈…。——–山河已无恙
写在前面
和小伙伴们分享一些分析Ansible回调插件的笔记
一个好的剧本,执行起来会很是丝滑,良好的执行体验让你甚至感觉不到执行了很久,哈…
Ansible提供了CallBack插件来处理playbook中的回调事件。我们可以通过回调插件分析剧本资源利用率、消耗时间,从而优化剧本。
博文涉及内容:
查看Callback插件以及插件说明
分析控制节点执行剧本CPU和内存的消耗
统计任务和角色剧本的执行时间
自定义一个callBack插件实现执行完剧本打开博客
食用方式
理解不足小伙伴帮忙指正
一个好的剧本,执行起来会很是丝滑,良好的执行体验让你甚至感觉不到执行了很久,哈…。——–山河已无恙
对这方面感兴趣的小伙伴可以到官网看下:https://docs.ansible.com/ansible/2.8/plugins/callback.html
什么是Ansible Callback插件 关于回调插件,官网文档中这样讲,Ansible的回调插件可以在响应事件
时向 Ansible 添加新行为。默认情况下,回调插件控制在运行命令行程序时看到的大部分输出,但也可用于添加额外的输出、与其他工具集成以及将事件编组到存储后端。如有必要,也可以创建自定义回调插件
开发
的方式理解,
从细粒度编码角度理解,可以理解为钩子,回调函数,类比的话,类似后端JVM中的钩子进程,在JVM进程结束时运行的进程。处理一些资源释放。前端VUE的8个生命周期方法,从指令编译到数据加载、模板渲染,DOM挂载等不同时期都会触发对应的回调函数。(Ansible 的回调也同样基于剧本生命周期方法实现)
从粗粒度编程思想理解,类似面向切面编程(AOP),把代码的执行逻辑块之间的连接点看做是一个个切入点,把一些不重要,但是需要的东西做成切面,在必要时织入到逻辑块内。
运维
的方式理解:
类似Linux
开机,启动的第一个进程systemd要引导一些系统必要的启动项,当然内核版本不同,对应的启动规则不同,但是如果你配置的服务设置了开启自启,会在启动级别的target目录下建一个指向服务service文件的软连接,即服务一定是某个启动级别target的正向依赖。这里的配置开启自启的服务可以理解为我们给Ansible配置回调插件。
亦或者我们之前讲的 剧本中的任务控制指令pro_task和post_taks
,K8s中的 pod hook
,通过poststart和prestop
,配置在pod创建和死亡的回调处理等等。
那么在Ansible中通过CallBack
插件调整对各种事件的响应
来扩展 Ansible
。其中一些插件也会修改命令行工具
(如ansible-playbook 命令)的输出,以提供额外的信息。
不只是剧本可以使用,临时命令的方式也可以使用回调。感兴趣小伙伴可以看看官网
需要说明的是Ansible
附带的大多数回调默认情况下是禁用
的,需要在ansible.cfg
文件中列入白名单
才能正常工作,通过 callback_whitelist
指令在ansible.cfg
中启用这些插件,这里2.8和2.9的版本还有些区别。我们主要看下2.8的版本
ansible.cfg
的配置,下面的配置中,在插件白名单里添加了timer, profile_tasks, cgroup_perf_recap
这三个回调
1 2 3 4 [ defaults] inventory=inventory remote_user=devops callback_whitelist=timer, profile_tasks, cgroup_perf_recap
使用ansible-doc -t callback -l
命令可以列出可用的插件
1 2 3 4 5 6 7 8 9 10 11 $ ansible-doc -t callback -l actionable shows only items that need attention aws_resource_actions summarizes all "resource:actions" completed cgroup_memory_recap Profiles maximum memory usage of tasks and full execution using cgroups cgroup_perf_recap Profiles system activity of tasks and full execution using cgroups context_demo demo callback that adds play/task context counter_enabled adds counters to the output items (tasks and hosts/task) debug formatted stdout/stderr display ..... ..... $
使用ansible-doc -t callback plugin-name
查看指定插件的文档:下面是我们查看的timer
这个插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ ansible-doc -t callback timer > TIMER (/usr/lib/python3.6/site-packages/ansible/plugins/callback/timer.py) This callback just adds total play duration to the play stats. * This module is maintained by The Ansible Community REQUIREMENTS: whitelist in configuration CALLBACK_TYPE: aggregate METADATA: status: - preview supported_by: community 。。。。。。。。。
可以看到,这个插件用于将总的play
执行时间添加到当前play
的最后输出中。来简单看一下怎么实现的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 [root@foundation0 ~] from __future__ import (absolute_import, division, print_function)__metaclass__ = type DOCUMENTATION = ''' callback: timer callback_type: aggregate requirements: - whitelist in configuration short_description: Adds time to play stats version_added: "2.0" description: - This callback just adds total play duration to the play stats. ''' from datetime import datetimefrom ansible.plugins.callback import CallbackBaseclass CallbackModule (CallbackBase ): """ This callback module tells you how long your plays ran for. """ CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'aggregate' CALLBACK_NAME = 'timer' CALLBACK_NEEDS_WHITELIST = True def __init__ (self ): super (CallbackModule, self).__init__() self.start_time = datetime.utcnow() def days_hours_minutes_seconds (self, runtime ): minutes = (runtime.seconds // 60 ) % 60 r_seconds = runtime.seconds % 60 return runtime.days, runtime.seconds // 3600 , minutes, r_seconds def playbook_on_stats (self, stats ): self.v2_playbook_on_stats(stats) def v2_playbook_on_stats (self, stats ): end_time = datetime.utcnow() runtime = end_time - self.start_time self._display.display("Playbook run took %s days, %s hours, %s minutes, %s seconds" % (self.days_hours_minutes_seconds(runtime))) [root@foundation0 ~]
逻辑很简单,我们可以看到CallbackModule继承了CallbackBase
,覆盖了需要回调的方法。
1 2 3 4 5 6 7 8 $ cat /usr/lib/python3.6/site-packages/ansible/plugins/callback/__init__.py | grep -A 2 playbook_on_stats def playbook_on_stats(self, stats): pass -- def v2_playbook_on_stats(self, stats): self.playbook_on_stats(stats)
playbook_on_stats
、v2_playbook_on_stats
,这两个方法中,后者是在play中分配对象的回调,前者用于在play结束时的回调。 看一下CallbackBase
的注释
1 2 3 4 5 6 7 8 9 $ cat /usr/lib/python3.6/site-packages/ansible/plugins/callback/__init__.py | grep -A 6 CallbackBase | tail -n 7 class CallbackBase(AnsiblePlugin): '' ' This is a base ansible callback class that does nothing. New callbacks should use this class as a base and override any callback methods they wish to execute custom actions. ' '' $
这是一个基本的ansible回调类,它什么都不做。新的回调使用这个类作为基类,重写他们希望执行的任何回调方法自定义操作。
如果需要编写一些自定义的回调插件,我们可以以同样的方法来尝试
下面来看看如何通过利用CallBack插件统计资源消耗
和执行时间
来分析Playbook的执行性能。
我们编写一个剧本用于测试,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 $ cat tags.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 tags: - play-tag-2 tasks: - name: task 2 tag shell: echo 'tags to task 2' tags: - task-tag-2
执行测试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ ansible-playbook copy_task.yaml PLAY [Deploy the w eb content on the web servers] ****************************************************************** TASK [copy demo] *************************************************************************************************** ok: [servera] ok: [serverc] ok: [serverd] ok: [serverf] ok: [serverb] ok: [servere] PLAY RECAP ********************************************************************************************************* servera : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 serverb : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 serverc : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 serverd : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 servere : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 serverf : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Playbook run took 0 days, 0 hours, 0 minutes, 1 seconds
统计Playbook执行性能(控制节点 CPU 和内存) cgroup_perf_recap
插件可以分析playbook
运行期间的控制节点性能。在playbook
执行结束时,它将显示全局摘要和每个任务的摘要。这些摘要包括 CPU 和内存消耗,以及在 playbook 和 tasks 执行期间启动的进程 的最大数量。
来看下插件文档,= is mandatory
修饰的变量为强制需要,所以我们还需要定义变量用于执行中那个控制组下执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ ansible-doc -t callback cgroup_perf_recap > CGROUP_PERF_RECAP (/usr/lib/python3.6/site-packages/ansible/plugins/callback/cgroup_perf_recap.py) This is an ansible callback plugin utilizes cgroups to profile system activity of ansible and individual tasks, and display a recap at the end of the playbook execution * This module is maintained by The Ansible Community OPTIONS (= is mandatory): = control_group Name of cgroups control group set_via: env: - name: CGROUP_CONTROL_GROUP ini: - key: control_group section: callback_cgroup_perf_recap ......
cgroup_perf_recap
CallBack插件依赖于 Linux 控制组(cgroup)
功能来监控和分析 ansible-playbook命令。
在 Linux 系统上,可以使用控制组来限制和监控一组进程可以消耗的资源,如内存或 CPU。若要设置这些限值,可以创建⼀个新组,设置限值,然后将进程添加到该组中。
需要安装cgcreate所在的安装包
1 2 3 4 5 6 7 8 $ sudo yum provides */cgcreate Last metadata expiration check: 0:00:45 ago on Sun 14 Aug 2022 08:35:18 PM CST. libcgroup-tools-0.41-19.el8.x86_64 : Command-line utility programs, services and daemons for libcgroup Repo : rhel-8.0-for-x86_64-baseos-rpms Matched from: Filename : /usr/bin/cgcreate $ yum -y install libcgroup-tools-0.41-19.el8.x86_64
使用 root 用户通过 cgcreate 命令创建专用控制组:
1 $ sudo cgcreate-a user:user-t user:user -g cpuacct,memory,pids:ansible_profile
1 2 $ sudo cgcreate -a student:student -t student:student -g cpuacct,memory,pids:ansible_profile [sudo] password for student:
-a 和 -t 选项显示可以访问和管理控制组的用户和组。
-g 选项指定新控制组的名称
下一步,是在ansible.cfg
文件中启用插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [defaults] inventory=inventory remote_user=devops roles_path=roles gathering=explicit forks=10 callback_whitelist = cgroup_perf_recap [callback_cgroup_perf_recap] control_group=ansible_profile [privilege_escalation] become=True become_method=sudo become_user=root become_ask_pass=False
在新控制组中运行ansible-playbook
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 $ ansible-playbook tags.yaml PLAY [tags Demo 1] *************************************************************************************** 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 [task 2 tag] **************************************************************************************** changed: [servera] PLAY RECAP *********************************************************************************************** servera : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 CGROUP PERF RECAP **************************************************************************************** Memory Execution Maximum: 17.70MB cpu Execution Maximum: 0.00% pids Execution Maximum: 0.00 memory: tag_role : tags roles (52540000-fa09-85ab-8a5c-000000000010): 17.70MB task 1 tag (52540000-fa09-85ab-8a5c-000000000012): 17.70MB include or import a tasks file (52540000-fa09-85ab-8a5c-000000000013): 17.70MB task 1 (52540000-fa09-85ab-8a5c-000000000033): 17.70MB task 2 (52540000-fa09-85ab-8a5c-000000000034): 17.70MB task 1 in block (52540000-fa09-85ab-8a5c-000000000015): 17.70MB task 2 in block (52540000-fa09-85ab-8a5c-000000000016): 17.70MB task 2 tag (52540000-fa09-85ab-8a5c-000000000019): 17.70MB cpu: tag_role : tags roles (52540000-fa09-85ab-8a5c-000000000010): 0.00% task 1 tag (52540000-fa09-85ab-8a5c-000000000012): 0.00% include or import a tasks file (52540000-fa09-85ab-8a5c-000000000013): 0.00% task 1 (52540000-fa09-85ab-8a5c-000000000033): 0.00% task 2 (52540000-fa09-85ab-8a5c-000000000034): 0.00% task 1 in block (52540000-fa09-85ab-8a5c-000000000015): 0.00% task 2 in block (52540000-fa09-85ab-8a5c-000000000016): 0.00% task 2 tag (52540000-fa09-85ab-8a5c-000000000019): 0.00% pids: tag_role : tags roles (52540000-fa09-85ab-8a5c-000000000010): 0.00 task 1 tag (52540000-fa09-85ab-8a5c-000000000012): 0.00 include or import a tasks file (52540000-fa09-85ab-8a5c-000000000013): 0.00 task 1 (52540000-fa09-85ab-8a5c-000000000033): 0.00 task 2 (52540000-fa09-85ab-8a5c-000000000034): 0.00 task 1 in block (52540000-fa09-85ab-8a5c-000000000015): 0.00 task 2 in block (52540000-fa09-85ab-8a5c-000000000016): 0.00 task 2 tag (52540000-fa09-85ab-8a5c-000000000019): 0.00 $
统计任务和角色阶段耗时 timer
、profile_tasks
和 profile_roles
CallBack插件可⽤于确定速度较慢的任务和角色。
timer
插件显示playbook
执行的持续时间。
profile_tasks
添加每个任务的开始时间,并在 playbook 执行结束时显示每个任务所用的时间,按降序排列。
profile_roles
在结束时显示每个角色所用的时间,按降序排列。
激活这些插件需要在ansible.cfg
文件中添加或更新callback_whitelist
指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [defaults] inventory=inventory remote_user=devops roles_path=roles gathering=explicit forks=10 callback_whitelist = timer,profile_roles,profile_tasks [privilege_escalation] become=True become_method=sudo become_user=root become_ask_pass=False
执行的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 $ ansible-playbook tags.yaml PLAY [tags Demo 1] *************************************************************************************** TASK [tag_role : tags roles] ***************************************************************************** Monday 15 August 2022 23:46:17 +0800 (0:00:00.021) 0:00:00.021 ********* Monday 15 August 2022 23:46:17 +0800 (0:00:00.021) 0:00:00.021 ********* changed: [servera] TASK [task 1 tag] **************************************************************************************** Monday 15 August 2022 23:46:18 +0800 (0:00:01.154) 0:00:01.176 ********* Monday 15 August 2022 23:46:18 +0800 (0:00:01.154) 0:00:01.176 ********* changed: [servera] TASK [include or import a tasks file] ******************************************************************* Monday 15 August 2022 23:46:19 +0800 (0:00:00.341) 0:00:01.518 ********* Monday 15 August 2022 23:46:19 +0800 (0:00:00.341) 0:00:01.518 ********* included: /home/student/DO447/labs/task-execution/tasks_file for servera TASK [task 1] ******************************************************************************************** Monday 15 August 2022 23:46:19 +0800 (0:00:00.018) 0:00:01.537 ********* Monday 15 August 2022 23:46:19 +0800 (0:00:00.019) 0:00:01.537 ********* changed: [servera] TASK [task 2] ******************************************************************************************** Monday 15 August 2022 23:46:19 +0800 (0:00:00.333) 0:00:01.871 ********* Monday 15 August 2022 23:46:19 +0800 (0:00:00.333) 0:00:01.870 ********* changed: [servera] TASK [task 1 in block] *********************************************************************************** Monday 15 August 2022 23:46:19 +0800 (0:00:00.345) 0:00:02.216 ********* Monday 15 August 2022 23:46:19 +0800 (0:00:00.345) 0:00:02.216 ********* changed: [servera] TASK [task 2 in block] *********************************************************************************** Monday 15 August 2022 23:46:20 +0800 (0:00:00.332) 0:00:02.548 ********* Monday 15 August 2022 23:46:20 +0800 (0:00:00.332) 0:00:02.548 ********* changed: [servera] PLAY [tags Demo 2] *************************************************************************************** TASK [task 2 tag] **************************************************************************************** Monday 15 August 2022 23:46:20 +0800 (0:00:00.344) 0:00:02.893 ********* Monday 15 August 2022 23:46:20 +0800 (0:00:00.344) 0:00:02.893 ********* changed: [servera] PLAY RECAP *********************************************************************************************** servera : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Monday 15 August 2022 23:46:20 +0800 (0:00:00.348) 0:00:03.241 ********* =============================================================================== shell ------------------------------------------------------------------- 2.05s tag_role ---------------------------------------------------------------- 1.15s include_tasks ----------------------------------------------------------- 0.02s ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ total ------------------------------------------------------------------- 3.22s Monday 15 August 2022 23:46:20 +0800 (0:00:00.348) 0:00:03.241 ********* =============================================================================== tag_role : tags roles ----------------------------------------------------------------------------- 1.15s task 2 tag ---------------------------------------------------------------------------------------- 0.35s task 2 -------------------------------------------------------------------------------------------- 0.35s task 2 in block ----------------------------------------------------------------------------------- 0.34s task 1 tag ---------------------------------------------------------------------------------------- 0.34s task 1 -------------------------------------------------------------------------------------------- 0.33s task 1 in block ----------------------------------------------------------------------------------- 0.33s include or import a tasks file ------------------------------------------------------------------- 0.02s Playbook run took 0 days, 0 hours, 0 minutes, 3 seconds $
我们可以在剧本输出中看到每个任务的执行时间。对于时间长的可以调整剧本优化,关于优化方式,小伙伴可以看看我之前的文章,关于其他的插件,小伙伴可以官网看看。具体的版本不同,插件使用方式略有差异。
自定义一个callBack插件 上面的都是社区或者官方的一些插件,下面我们看看如何自己编写一个插件
这里我们做一个简单Demo,所以这个插件的的功能就是在剧本执行完,在浏览器打开我的博客,…………
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 """ @File : disblog.py @Time : 2022/09/01 00:09:06 @Author : Li Ruilong @Version : 1.0 @Contact : 1224965096@qq.com @Desc : ansible callback plugins 执行完剧本浏览器打开我的博客 """ from __future__ import (absolute_import, division, print_function)__metaclass__ = type DOCUMENTATION = ''' callback: disblog callback_type: aggregate requirements: - whitelist in configuration short_description: 执行完剧本浏览器打开我的博客 version_added: "2.0" description: - 执行完剧本浏览器打开我的博客 ''' from datetime import datetimeimport webbrowserfrom ansible.plugins.callback import CallbackBaseclass CallbackModule (CallbackBase ): """ 执行完剧本浏览器打开我的博客 """ CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'aggregate' CALLBACK_NAME = 'disblog' CALLBACK_NEEDS_WHITELIST = True def __init__ (self ): super (CallbackModule, self).__init__() def playbook_on_stats (self, stats ): webbrowser.open ('https://liruilong.blog.csdn.net/' );
放到:/usr/lib/python3.6/site-packages/ansible/plugins/callback/
目录下
我们可以通过命令查看插件
1 2 3 $ vim disblog.py $ ansible-doc -t callback -l | grep disblog disblog 执行完剧本浏览器打开我的博客
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ ansible-doc -t callback disblog > DISBLOG (/usr/lib/python3.6/site-packages/ansible/plugins/callback/disblog.py) 执行完剧本浏览器打开我的博客 * This module is maintained by The Ansible Community REQUIREMENTS: whitelist in configuration CALLBACK_TYPE: aggregate METADATA: status: - preview supported_by: community $
配置文件添加插件到白名单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@foundation0 resource_stat] [defaults] inventory = ./inventory remote_user = devops ask_pass = false callback_whitelist = disblog [privilege_escalation] become = false become_method = sudo become_user = root become_ask_pass = false [root@foundation0 resource_stat]
在剧本执行完后,直接打开了我的博客主页,当前这里需要考虑启动级别
博文参考
《Red Hat Ansible Engine 2.8 DO447》