yun 发表于 2019-9-10 14:51:22

ansible笔记(37):include(二)

本帖最后由 yun 于 2019-9-11 17:17 编辑

ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。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文件中的任务将被引用,你可能会有疑问,上例的写法和如下写法的效果相同吗?  - include_tasks: 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 *******************************************

TASK *************************************
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 *******************************************

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 *******************************************

TASK *************************************
included: /testdir/ansible/in.yml for test70

TASK *******************************************
ok: => {
    "msg": "task1 in in.yml"
}

TASK *******************************************
ok: => {
    "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 *******************************************

TASK *******************************************
ok: => {
    "msg": "test task"
}

TASK *************************************
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 *******************************************

TASK *******************************************
ok: => {
    "msg": "test task"
}

TASK *******************************************
ok: => {
    "msg": "task1 in in.yml"
}

TASK *******************************************
ok: => {
    "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 *********************************************

TASK [----------set testvar to 0] ******************************
ok:

TASK *********************************************
ok: => {
    "msg": "-----include_tasks-----enter the in1.yml-----"
}

TASK ***************************************
included: /testdir/ansible/in1.yml for test70

TASK ********************************************
ok:

TASK **********************************************
ok: => {
    "msg": "task1 in in1.yml"
}

TASK [----------set testvar to 0] *******************************
ok:

TASK **********************************************
ok: => {
    "msg": "-----import_tasks-----enter the in1.yml-----"
}

TASK *********************************************
ok:

TASK **********************************************
skipping:

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"模块的一些功能特性,文章就先总结到这里吧,希望能够对你有所帮助~
页: [1]
查看完整版本: ansible笔记(37):include(二)