本帖最后由 yun 于 2019-9-11 17:17 编辑
ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。 "ansible系列"中的每篇文章都建立在前文的基础之上,所以,请按照顺序阅读这些文章,否则有可能在阅读中遇到障碍。
在上一篇文章中我们总结了"include"的使用方法,而且我们提到,"include"的某些原始用法在之后的版本中可能会被弃用,在之后的版本中,会使用一些新的关键字代替这些原始用法,那么在这篇文章中,我们就来介绍一下这些与"include"有关的新的关键字。
include_tasks先来介绍一下include_tasks模块,在理解了include以后,再来理解include_tasks,简直不要太轻松,我们知道,include模块可以用来包含一个任务列表,include_tasks模块的作用也是用来包含一个任务列表,在之后的版本中,如果我们想要包含一个任务列表,那么就可以使用"include_tasks"关键字代替"include"关键字,示例如下: - # cat intest.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task1"
- - include_tasks: in.yml
- - debug:
- msg: "test task2"
-
- # cat in.yml
- - debug:
- msg: "task1 in in.yml"
- - debug:
- msg: "task2 in in.yml"
复制代码如上例所示,当我们需要包含一个任务列表时,"include_tasks"关键字的用法与"include"完全相同,那么我们执行一下上例的playbook,看看执行效果如何 如上图所示,当我们使用"include_tasks"时,"include_tasks"本身会被当做一个"task",这个"task"会把被include的文件的路径输出在控制台中,这就是"include_tasks模块"与"include模块"之间的区别,如果将上例中的"include_tasks"关键字替换成"include",控制台中则不会显示上图中标注的任务信息,由此可见"include"是透明的,"include_tasks"是可见的,"include_tasks"更像是一个任务,这个任务包含了其他的一些任务。
在ansible的2.7版本中,"include_tasks"模块中加入了新的参数,由于我当前使用的版本为2.6.2,所以此处先将ansible升级至2.7.0版本(当前使用yum源能够更新的最新版本为2.7.0),以便了解"include_tasks"模块的新参数,在之后的文章中,如果没有特殊说明,则使用的是2.7.0版本的ansible进行演示。
从2.7版本开始,"include_tasks"模块加入了file参数和apply参数,先来聊聊file参数,file参数可以用来指定要包含的任务列表文件,语法如下: - - include_tasks:
- file: in.yml
复制代码上例表示包含in.yml文件,in.yml文件中的任务将被引用,你可能会有疑问,上例的写法和如下写法的效果相同吗? 没错,这两种写法的最终效果是完全相同的,只不过一个使用了"file"参数的方式,另一个使用了"free_form"的方式(free_form的意思前文中已经总结过,此处不再赘述),虽然语法上不同,但是本质上没有区别。
在前一篇文章中我们演示过,如果为include添加tags,那么tags是对include文件中的所有任务生效的,也就是说,如果调用include对应的tag,那么include文件中的所有任务都会执行,如果对"include_tasks"添加tags,tags是不是也会对include_tags中的所有任务生效呢?我们来试试,示例如下 - # cat intest.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task1"
- - include_tasks:
- file: in.yml
- tags: t1
- - debug:
- msg: "test task2"
复制代码如上例所示,我们为"include_tasks"任务添加了标签t1,那么,我们在执行此playbook时,指定使用t1标签,执行后输出如下 - # ansible-playbook intest.yml --tags t1
-
- PLAY [test70] *******************************************
-
- TASK [include_tasks] *************************************
- included: /testdir/ansible/in.yml for test70
-
- PLAY RECAP ********************************************
- test70 : ok=1 changed=0 unreachable=0 failed=0
复制代码从执行后的输出信息可以看出,当我们指定t1标签后,"include_tasks"这个任务本身被调用了,而"include_tasks"对应文件中的任务却没有被调用,所以我们可以得出结论,在使用tags时,"include_tasks"与"include"并不相同,标签只会对"include_tasks"任务本身生效,而不会对其中包含的任务生效。 如果我们想要tags对"include_tasks"中包含的所有任务都生效,该怎么实现呢?这时,我们就需要使用到"include_tasks"模块的apply参数了,示例如下: - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - include_tasks:
- file: in.yml
- apply:
- tags:
- - t1
复制代码如上例所示,除了使用file参数指定包含的任务列表文件以外,还使用了apply参数,apply参数调用了tags关键字,tags关键字用于指定一个标签列表,列表中的标签将会被应用到in.yml文件中的所有任务上,换句话说就是,apply参数可以指定哪些标签会被应用到被包含的任务上,聪明如你,一定看懂了,但是,按照上述语法,就能够正常的将t1标签应用到in.yml中的所有任务吗?不如我们来实际测试一下,看看与我们预想的结果是否一致,上例playbook执行后结果如下 - # ansible-playbook intest.yml --tags t1
-
- PLAY [test70] *******************************************
-
- PLAY RECAP ********************************************
复制代码如你所见,当我们执行上例playbook以后,并没有如我们预想的一样调用in.yml中的任务,就连"include_tasks"任务自身也没有被调用,我们该怎么办呢?如果想让上例playbook按照我们所想的那样运行,则需要使用如下方法: - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - include_tasks:
- file: in.yml
- apply:
- tags:
- - t1
- tags: always
复制代码如上例所示,在使用"include_tasks"时,不仅使用apply参数指定了tags,同时还使用tags关键字,对"include_tasks"本身添加了always标签(前文中已经总结过always标签的含义,如果你忘记了可以回顾前文),按照上例的语法,才能够按照我们所想的那样,将t1标签应用到in.yml中的所有任务上,效果如下: - # ansible-playbook intest.yml --tags t1
-
- PLAY [test70] *******************************************
-
- TASK [include_tasks] *************************************
- included: /testdir/ansible/in.yml for test70
-
- TASK [debug] *******************************************
- ok: [test70] => {
- "msg": "task1 in in.yml"
- }
-
- TASK [debug] *******************************************
- ok: [test70] => {
- "msg": "task2 in in.yml"
- }
-
- PLAY RECAP *******************************************
- test70 : ok=3 changed=0 unreachable=0 failed=0
复制代码从上述执行结果可以看出,当我们调用t1标签时,in.yml中的所有任务都执行了,但是你可能会有疑问,如果对"include_tasks"自身添加了always标签,那么我们调用其他标签时,in.yml文件中的任务也会"always"执行吗?我们来测试一下,playbook如下: - # cat intest.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task"
- tags: t0
- - include_tasks:
- file: in.yml
- apply:
- tags:
- - t1
- tags: always
复制代码执行上例playbook时,我们只调用t0标签,看看t1标签对应的in.yml中的任务会不会"always"执行,测试结果如下: - # ansible-playbook intest.yml --tags t0
-
- PLAY [test70] *******************************************
-
- TASK [debug] *******************************************
- ok: [test70] => {
- "msg": "test task"
- }
-
- TASK [include_tasks] *************************************
- included: /testdir/ansible/in.yml for test70
-
- PLAY RECAP ********************************************
- test70 : ok=2 changed=0 unreachable=0 failed=0
复制代码从上述测试结果可以看出,in.yml中的任务并未"always"执行,而"include_tasks"任务本身却"always"执行了,所以,我们可以得出结论,上例中的always标签只是针对"include_tasks"任务自身而言的,之所以为"include_tasks"任务添加always标签,就是为了让apply参数中的t1标签能够针对包含的所有任务生效,always标签并不会对被包含的所有任务生效,如果想要被包含的任务也都"always"执行,该怎么办呢?你一定已经想到了,没错,在apply参数的tags列表中添加always标签即可,示例如下: - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task"
- tags: t0
- - include_tasks:
- file: in.yml
- apply:
- tags: t1,always
- tags: always
复制代码综上所述就是,apply参数中的tags用于给in.yml中的任务统一打标签,"include_tasks"对应的tags用于给"include_tasks"任务自身打标签,同时,如果想要apply参数中的tags能够生效,"include_tasks"的标签中必须包含always标签,是不是觉得有点"绕",多动手试试就不绕了。
除了使用参数的方式能够指定tags,使用"free_form"的方式也可以指定tags,示例如下: - ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task"
- tags: t0
- - include_tasks: in.yml
- args:
- apply:
- tags: t1
- tags: always
复制代码我觉得我已经说清楚了,你肯定也已经明白了。
import_tasks刚才已经了解了"include_tasks"的用法,现在聊聊另一个与它很像的模块:"import_tasks",如果想要包含引用一个任务列表,也可以使用"import_tasks"关键字,示例如下: - # cat intest1.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task"
- - import_tasks: in.yml
-
- # cat in.yml
- - debug:
- msg: "task1 in in.yml"
- - debug:
- msg: "task2 in in.yml"
复制代码如上例所示,"import_tasks"模块使用了free-form的方式引用了in.yml文件,到目前的版本为止,"import_tasks"模块只能使用free-form的方式引用任务列表文件,没有其他方式可以使用,那么我们来执行一下上例playbook,执行后输出如下: - # ansible-playbook intest1.yml
-
- PLAY [test70] *******************************************
-
- TASK [debug] *******************************************
- ok: [test70] => {
- "msg": "test task"
- }
-
- TASK [debug] *******************************************
- ok: [test70] => {
- "msg": "task1 in in.yml"
- }
-
- TASK [debug] *******************************************
- ok: [test70] => {
- "msg": "task2 in in.yml"
- }
-
- PLAY RECAP ********************************************
- test70 : ok=3 changed=0 unreachable=0 failed=0
复制代码从输出的结果可以看出,"import_tasks"模块并不会像"include_tasks"模块那样,在控制台中输出相关的任务信息,"import_tasks"是相对透明的。
除了上述不同,"import_tasks"和"include_tasks"到底有什么不同之处呢?它们的不同之处在于,"import_tasks"是静态的,"include_tasks"是动态的。
那么动态和静态又是什么意思呢,它们有什么不同呢?动态和静态的主要区别在于被include的文件的加载时机不同。 "静态"的意思就是被include的文件在playbook被加载时就展开了(是预处理的)。 "动态"的意思就是被include的文件在playbook运行时才会被展开(是实时处理的)。
由于"include_tasks"是动态的,所以,被include的文件的文件名可以使用任何变量替换。 由于"import_tasks"是静态的,所以,被include的文件的文件名不能使用动态的变量替换。 这样说可能不容易理解,不如来看一个小示例,有时候,我们想要使用变量替换被包含文件的文件名,示例如下: - # cat intest3.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- vars:
- file_name: in.yml
- tasks:
- - import_tasks: "{{file_name}}"
- - include_tasks: "{{file_name}}"
复制代码如上例所示,被包含的文件名并没有写死,而是使用变量替换成了对应的文件名,执行上例playbook,是完全可以正常执行的。 但是,如果我们不使用vars关键字定义变量,而是使用set_fact的方式定义变量,能不能正常执行呢?我们来试试,示例如下: - # cat intest3.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - set_fact:
- file_name: in.yml
- - import_tasks: "{{file_name}}"
- - include_tasks: "{{file_name}}"
复制代码如上例所示,我们使用了set_fact的方式定义了file_name变量,尝试执行上例playbook,会发现如下报错: - # ansible-playbook intest3.yml
- ERROR! Error when evaluating variable in import path: {{file_name}}.
-
- When using static imports, ensure that any variables used in their names are defined in vars/vars_files
- or extra-vars passed in from the command line. Static imports cannot use variables from facts or inventory
- sources like group or host vars.
复制代码从报错信息可以看出,当使用静态的import时,请确保文件名中使用到的变量被定义在vars中、vars_files中、或者extra-vars中,静态的import不支持其他方式传入的变量。
除了上述不同之处,在使用"循环操作"和"条件判断"时,"include_tasks"和"import_tasks"也有很多不同点需要注意,注意点如下。
如果想要对包含的任务列表进行循环操作,则只能使用"include_tasks"关键字,不能使用"import_tasks"关键字,"import_tasks"并不支持循环操作, 也就是说,使用"loop"关键字或"with_items"关键字对include文件进行循环操作时,只能配合"include_tasks"才能正常运行。
我们知道,当使用when关键字对include文件添加了条件判断时,只有条件满足后,include文件中的任务列表才会被执行,其实,when关键字对"include_tasks"和"import_tasks"的实际操作有着本质区别,区别如下: 当对"include_tasks"使用when进行条件判断时,when对应的条件只会应用于"include_tasks"任务本身,当执行被包含的任务时,不会对这些被包含的任务重新进行条件判断。 当对"import_tasks"使用when进行条件判断时,when对应的条件会应用于被include的文件中的每一个任务,当执行被包含的任务时,会对每一个被包含的任务进行同样的条件判断。
这样说不容易理解,来看一个小示例,就容易理解多了,示例playbook如下: - # cat intest4.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - name: '----------set testvar to 0'
- set_fact:
- testnum: 0
- - debug:
- msg: '-----include_tasks-----enter the in1.yml-----'
- - include_tasks: in1.yml
- when: testnum == 0
-
- - name: '----------set testvar to 0'
- set_fact:
- testnum: 0
- - debug:
- msg: '-----import_tasks-----enter the in1.yml-----'
- - import_tasks: in1.yml
- when: testnum == 0
-
- # cat in1.yml
- - set_fact:
- testnum: 1
-
- - debug:
- msg: "task1 in in1.yml"
复制代码如上例 所示,我们分别使用"include_tasks"和"import_tasks"两个关键字包含了in1.yml文件,并且,二者都有相同的条件判断,即当testnum变量的值为0时,才会调用in1.yml中的任务,无论是在测试"include_tasks"还是测试"import_tasks"之前,都会使用set_fact将testnum的值设置为0,以便条件成立后执行in.yml中的任务,在in.yml中 ,又使用set_fact将testnum的值设置为了1,为什么这样做呢?咱们稍后解释,同时,在in1.yml中输出了一条测试信息,那么你预想一下,如果执行上例的intest4.yml剧本,"task1 in in1.yml"这条测试信息会输出几次呢?请先在你的大脑里预演一遍上例的playbook,然后再查看实际的运行效果 ,实际的运行效果如下: - PLAY [test70] *********************************************
-
- TASK [----------set testvar to 0] ******************************
- ok: [test70]
-
- TASK [debug] *********************************************
- ok: [test70] => {
- "msg": "-----include_tasks-----enter the in1.yml-----"
- }
-
- TASK [include_tasks] ***************************************
- included: /testdir/ansible/in1.yml for test70
-
- TASK [set_fact] ********************************************
- ok: [test70]
-
- TASK [debug] **********************************************
- ok: [test70] => {
- "msg": "task1 in in1.yml"
- }
-
- TASK [----------set testvar to 0] *******************************
- ok: [test70]
-
- TASK [debug] **********************************************
- ok: [test70] => {
- "msg": "-----import_tasks-----enter the in1.yml-----"
- }
-
- TASK [set_fact] *********************************************
- ok: [test70]
-
- TASK [debug] **********************************************
- skipping: [test70]
-
- PLAY RECAP ***********************************************
- test70 : ok=8 changed=0 unreachable=0 failed=0
复制代码从上述实际运行结果中可以看出,"task1 in in1.yml"这条测试信息只输出了一遍,这可能与我们预想的效果不太一样,那么上例具体是怎样执行的呢?我们来捋一遍,之前粗略的解释过,当对"include_tasks"使用when进行条件判断时,when对应的条件只会应用于"include_tasks"任务本身,所以,当testnum的值为0时,条件成立,in1.yml中的任务被执行,于是,debug模块输出了 "task1 in in1.yml",如我们所想,一切正常,然后继续执行之后的任务,我们重新将testnum的值设置为了0,所以"import_tasks"的条件" testnum == 0"也同样成立了 ,于是,开始执行in1.yml文件中的任务,之前粗略的介绍过,when对应的条件会应用于被include的文件中的每一个任务,当执行被包含的任务时,会对每一个被包含的任务进行同样的条件判断,所以,当执行in1.yml文件中的第一个任务(set_fact任务)时,此任务会再次对" testnum == 0"条件进行判断,如果"testnum == 0",则执行set_fact任务,此时,条件完全成立,于是,执行set_fact任务,将testnum的值设置为1,当执行到debug任务时,同样会对debug任务进行" testnum == 0"条件判断,如果条件成立,则执行debug任务,但是,由于上一个任务已经把testnum的值设置为1,所以此时条件不成立,于是,debug任务并没有被执行,这就是上例playbook运行的整个过程,你肯定已经明白了,由此可见,"when"对于"include_tasks"和"import_tasks"的操作在本质上存在差别。
说完了上述注意点,我们再来聊聊"tags"与"handlers"。
与"include_tasks"不同,当为"import_tasks"添加标签时,tags是针对被包含文件中的所有任务生效的,与"include"关键字的效果相同。
"include_tasks"与"import_tasks"都可以在handlers中使用,并没有什么不同,不过在当前2.7.0版本中,如果在handlers中使用"import_tasks"引用任务列表,会出现bug,bug的具体表现可以参考如下链接: https://github.com/ansible/ansible/issues/47392
import_playbook在上篇文章中我们还提到,使用"include"关键字除了能够引用任务列表,还能够引用整个playbook,在之后的版本中,如果想要引入整个playbook,则需要使用"import_playbook"模块代替"include"模块,因为在2.8版本以后,使用"include"关键字引用整个playbook的特性将会被弃用。
"import_playbook"的示例如下: - # cat intest6.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task in intest6.yml"
-
- - import_playbook: intest7.yml
-
- # cat intest7.yml
- ---
- - hosts: test70
- remote_user: root
- gather_facts: no
- tasks:
- - debug:
- msg: "test task in intest7.yml"
复制代码如上例所示, "import_playbook"的用法与"include"的用法并没有什么区别,此处不再赘述。
这篇文章中我们总结了一些新的模块,通过这些模块可以替代"include"模块的一些功能特性,文章就先总结到这里吧,希望能够对你有所帮助~ |