18.include用法
简介
在前面的学习当中,我们一直使用一个playbook文件来组织所有的task任务。但是,当我们项目越来越大,task越来越多的时候,如果还将所有的task都写到一个playbook当中,可读性就会变差,这个时候我们就需要重新来组织playbook了。
我们可以将一个大的playbook拆成若干个小的playbook文件,在主配置文件中将这些零碎的小文件引入进来,而这种方式就叫做playbook的"include"。
include
playbook的include其实就是使用include关键字
tasks include
include简单示例
下面是两个playbook示例,分别用于安装lamp和lnmp环境:
# cat lamp.yml
- hosts: test
gather_facts: no
tasks:
- package:
name: mysql
state: present
- package:
name: php-fpm
state: present
- package:
name: httpd
state: present
# cat lnmp.yml
- hosts: test
gather_facts: no
tasks:
- package:
name: mysql
state: present
- package:
name: php-fpm
state: present
- package:
name: nginx
state: present
在上面的示例当中,我们可以看到lamp和lnmp中mysql和php的安装都是一样的,所以我们可以将这两个任务提取出来,放到一个单独的task文件中,然后在lnmp和lamp中引入:
# cat install_mysql_php.yml
- package:
name: mysql
state: present
- package:
name: php-fpm
state: present
# cat lamp.yml
- hosts: test
gather_facts: no
tasks:
- include: install_mysql_php.yml
- package:
name: httpd
state: php-fpm
# cat lnmp.yml
- hosts: test
gather_facts: no
tasks:
- include: install_mysql_php.yml
- package:
name: nginx
state: php-fpm
在include时引入变量
也可以在include的时候,传入变量:
# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: wordpress.yml user=timmy
- include: wordpress.yml user=alice
- include: wordpress.yml user=bob
# cat wordpress.yml
- debug:
msg: "{{ user }}"
通过如下方式带入变量:
tasks:
- { include: wordpress.yml, user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }
再给一个例子:
- hosts: test
gather_facts: no
tasks:
- include: in.yml
vars:
users:
bob:
gender: male
lucy:
gender: female
# cat in.yml
- debug:
msg: "{{ item.key }} is {{ item.value.gender }}"
loop: "{{users | dict2items }}""
在include中使用tag
# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in1.yml
tags: t1
- include: in2.yml
tags: t2
# cat in1.yml
- debug:
msg: "task1 in in1.yml"
- debug:
msg: "task2 in in1.yml"
# cat in2.yml
- debug:
msg: "task1 in in2.yml"
- debug:
msg: "task2 in in2.yml"
在上面的示例当中,两个Include分别对应两个tag,如果我们在执行test_include.yml时,指定tag为t2,那么in2.yml中的所有任务都会被执行。所以tag是针对include的所有任务生效。
在include中使用条件判断
# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in.yml
when: 2 > 1
# cat in.yml
- debug:
msg: "task in in.yml"
在include中使用循环
下面是一个简单的循环示例:
# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in.yml
loop:
- 1
- 2
# cat in.yml
- debug:
msg: "task1 in in.yml"
- debug:
msg: "task2 in in.yml"
可以看到in.yml被循环执行了两次。
我们可以稍微修改in.yml示例如下:
# cat in.yml
- debug:
msg: "{{ item }} task1 in in.yml"
- debug:
msg: "{{ item }} task2 in in.yml"
再次执行playbook的结果如下:
ansible-playbook test_include.yml
输出:
PLAY [servera] ****************************************************************************
TASK [include] ****************************************************************************
included: /etc/ansible/in.yml for servera => (item=1)
included: /etc/ansible/in.yml for servera => (item=2)
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "1 task1 in in.yml"
}
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "1 task2 in in.yml"
}
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "2 task1 in in.yml"
}
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "2 task2 in in.yml"
}
PLAY RECAP ********************************************************************************
servera : ok=6 changed=0 unreachable=0 failed=0
可以看到item的值就来自test_include中的loop循环。那么这就引出了一个问题:如果正好in.yml当中也有循环时怎么办?
# cat in.yml
- debug:
msg: "{{ item }} task1 in in.yml"
loop: ['a','b','c']
再次执行test_include,结果如下:
ansible-playbook test_include.yml
PLAY [servera] ****************************************************************************
TASK [include] ****************************************************************************
included: /etc/ansible/in.yml for servera => (item=1)
included: /etc/ansible/in.yml for servera => (item=2)
TASK [debug] ******************************************************************************
[WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value
something else to avoid variable collisions and unexpected behavior.
ok: [servera] => (item=a) => {
"msg": "a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "c task1 in in.yml"
}
TASK [debug] ******************************************************************************
[WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value
something else to avoid variable collisions and unexpected behavior.
ok: [servera] => (item=a) => {
"msg": "a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "c task1 in in.yml"
}
PLAY RECAP ********************************************************************************
servera : ok=4 changed=0 unreachable=0 failed=0
这个时候,可以看到最终item的值来自in.yml中的循环。那如果我就想要使用 test_include 中的循环的值怎么办?我们再次修改 test_include.yml 以及 in.yml 如下:
# cat test_include.yml
- hosts: test
gather_facts: no
tasks:
- include: in.yml
loop:
- 1
- 2
loop_control:
loop_var: outer_item
# cat in.yml
- debug:
msg: "{{outer_item }} {{ item }} task1 in in.yml"
loop: ['a','b','c']
再次查看结果
PLAY [servera] ****************************************************************************
TASK [include] ****************************************************************************
included: /etc/ansible/in.yml for servera
included: /etc/ansible/in.yml for servera
TASK [debug] ******************************************************************************
ok: [servera] => (item=a) => {
"msg": "1 a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "1 b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "1 c task1 in in.yml"
}
TASK [debug] ******************************************************************************
ok: [servera] => (item=a) => {
"msg": "2 a task1 in in.yml"
}
ok: [servera] => (item=b) => {
"msg": "2 b task1 in in.yml"
}
ok: [servera] => (item=c) => {
"msg": "2 c task1 in in.yml"
}
PLAY RECAP ********************************************************************************
servera : ok=4 changed=0 unreachable=0 failed=0
可以看到, outer_item 中的值正是外层循环中item的值。当出现这个双层循环时,可以在外层循环中使用loop_var 选项指定一个变量,这个变量用于替代外层循环中的item变量,以便在内层循环中获取到外层循环的item的值,从而避免两层循环中item变量名的冲突。
handlers include
handlers include与tasks include大体类似,直接给例子:
# handlers1.yml内容如下:
# this might be in a file like handlers/handlers.yml
- name: restart apache
service: name=apache state=restarted
# handlers.yml包含handlers1.yml示例:
handlers:
- include: handlers/handlers.yml
playbook include
include也可以用于将一个playbook导入到另一个playbook中:
- name: this is a play at the top level of a file
hosts: all
remote_user: root
tasks:
- name: say hi
tags: foo
shell: echo "hi..."
- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml
include_tasks
基本使用
在前面我们详细说了include的用法,然而事实上在后续的ansible版本当中,include语法可能会被弃用。而使用一些新的关键字来代替include的原始用法,include_tasks就是其中之一。
我们知道include可以用于包含tasks,handlers,playbooks等,而include_tasks则专门用于包含tasks:
# cat include_tasks_ex.yml
- hosts:
gather_facts: no
tasks:
- debug:
msg: "task1"
- include_tasks: in.yml
- debug:
msg: "task2"
# cat in.yml
- debug:
msg: "{{ item }} task1 in in.yml"
- debug:
msg: "{{ item }} task2 in in.yml"
执行结果如下:
PLAY [servera] ****************************************************************************
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "task1"
}
TASK [include_tasks] **********************************************************************
included: /etc/ansible/in.yml for servera
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "task1 in in.yml"
}
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "task2 in in.yml"
}
TASK [debug] ******************************************************************************
ok: [servera] => {
"msg": "task2"
}
PLAY RECAP ********************************************************************************
servera : ok=5 changed=0 unreachable=0 failed=0
可以看到,当我们使用 include_tasks 时, include_tasks 本身会被当做一个task,这个task会把include的文件的路径输出在控制台中中, 这就是 include_tasks 和 include 之间的区别。include是透明的,而include_tasks 是可见的, include_tasks 更像是一个任务,这个任务包含了其他的一些任务。
在ansible 2.7版本当中, include_tasks 还加入了新的参数,下面是一个简单用法示例:
include_tasks:
file: in.yml
当然这种使用方法与 include_tasks: in.yml 的效果完全相同。
在include_tasks中使用tags
在前面我们提到过,如果为include添加tags,那么tags是对include中所有任务生效的。也就是说,如果调用include对应的tag,那么include文件中的所有任务都会执行。
但是对 include_tasks 添加tags,则只会对 include_tasks 本身生效, include_tasks 中所有的任务都不生效。示例如下:
# cat include_tasks_ex.yml
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task1"
- include_tasks:
file: in.yml
tags: t1
- debug:
msg: "test task3"
# cat in.yml
- debug:
msg: "test task2"
执行结果如下:
# ansible-playbook include_tasks_ex.yml --tags t1
PLAY [test] *******************************************************************************
TASK [include_tasks] **********************************************************************
included: /etc/ansible/in.yml for 10.1.61.187
PLAY RECAP ********************************************************************************
10.1.61.187 : ok=1 changed=0 unreachable=0 failed=0 skipped=0
如果想要tags对 include_tasks 中包含的所有任务生效,则需要使用 include_tasks 模块的apply参数并配合tags: always 内置tag:
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task1"
- include_tasks:
file: in.yml
apply:
tags: t1
tags: always
- debug:
msg: "test task3"
执行结果:
# ansible-playbook include_tasks_ex.yml --tags t1
PLAY [test] *******************************************************************************
TASK [include_tasks] **********************************************************************
included: /etc/ansible/in.yml for 10.1.61.187
TASK [debug] ******************************************************************************
ok: [10.1.61.187] => {
"msg": "test task2"
}
PLAY RECAP ********************************************************************************
10.1.61.187 : ok=2 changed=0 unreachable=0 failed=0 skipped=0
在上一篇我们讲到tags的时候说过,如果一个任务被打上了 tags: always 标签,则即使我们调用其他任务的标签,该任务也会被执行。
需要说明的是,在这里, tags: always 标签只针对 include_tasks 本身生效,也就是说,如果其他任务的标签被调用, include_tasks 本身会被调用,而其包含的任务不会被调用。如果要想其包含的任务也总是被调用,可修改配置如下:
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task1"
- include_tasks:
file: in.yml
apply:
tags: t1,always
tags: always
- debug:
msg: "test task3"
import_tasks
import_tasks 与 include_tasks 用法类似,都用于包含一个任务列表:
# cat import_tasks_ex.yml
- hosts: test
gather_facts: no
tasks
- debug:
msg: "test task1"
- import_tasks: in.yml
# cat in.yml
- debug:
msg: "test task2"
执行结果:
# ansible-playbook import_tasks_ex.yml
PLAY [test] *******************************************************************************
TASK [debug] ******************************************************************************
ok: [10.1.61.187] => {
"msg": "test task1"
}
TASK [debug] ******************************************************************************
ok: [10.1.61.187] => {
"msg": "test task2"
}
PLAY RECAP ********************************************************************************
10.1.61.187 : ok=2 changed=0 unreachable=0 failed=0 skipped=0
可以看到, import_tasks 模块并不会像 include_tasks 模块一样,在控制台输出自身的任务信息,其相对透明。
除此之外, import_tasks 和 include_tasks 还有如下不同:
-
import_tasks 是静态的,被import的文件在playbook被加载时就预处理了,而 include_tasks 是动态的,被include的文件在playbook被运行时候才开始处理。一个简单的例子:
- hosts: test
gather_facts: no
vars:
file_name: in.yml
tasks:
- include_tasks: {{ file_name }}
- import_tasks: {{ file_name }}在上面的示例中, include_tasks 和 import_tasks 均会被执行。 再看下面的例子:
- hosts: test
gather_facts: no
tasks:
- set_fact:
file_name: in.yml
- include_tasks: {{ file_name }}
- import_tasks: {{ file_name }}此时, import_tasks 就会出错:
# ansible-playbook include_import_tasks_ex.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 i
or extra-vars passed in from the command line. Static imports cannot use variables fro
sources like group or host vars.当使用静态的import时,请确保文件名中使用到的变量被定义在vars、vars_files或者extra-vars中,不支持其他的方式传入变量。
-
如果想要对包含的任务列表进行循环操作,则只能使用 include_tasks , import_tasks 不支持循环操作。也就是说,使用 loop 或者 with_X 对include文件进行循环操作时,只能配合 include_tasks 才能正常使用。
-
当使用when对include文件添加条件判断时, include_tasks 和 import_tasks有着本质的不同:
- 当对 include_tasks 使用when时,when对应的条件只会应用于 include_tasks 任务本身,当执行被包含的任务时,不会对这些被包含的任务重新进行条件判断
- 当对 import_tasks 使用when时,when对应的条件会被应用于被import的文件中的每一个任务,当执行被import的任务时,会对每一个被包含的任务进行同样的条件判断。
示例如下:
# cat include_import_tasks_ex2.yml
- hosts: test
gather_facts: no
tasks:
- name: set testvar to 0
set_fact:
testnum: 0
- debug:
msg: 'include_tasks: in1.yml'
- include_tasks: in1.yml
when: testnum == 0
- name: set testvar to 0
set_fact:
testnum: 0
- debug:
msg: 'import_tasks: in1.yml'
- import_tasks: in1.y
when: testnum == 0
执行结果:
# ansible-playbook include_import_tasks_ex2.yml
PLAY [test] *******************************************************************************
TASK [set testvar to 0] *******************************************************************
ok: [10.1.61.187]
TASK [debug] ******************************************************************************
ok: [10.1.61.187] => {
"msg": "include_tasks: in1.yml"
}
TASK [include_tasks] **********************************************************************
included: /etc/ansible/in1.yml for 10.1.61.187
TASK [set_fact] ***************************************************************************
ok: [10.1.61.187]
TASK [debug] ******************************************************************************
ok: [10.1.61.187] => {
"msg": "test task2"
}
TASK [set testvar to 0] *******************************************************************
ok: [10.1.61.187]
TASK [debug] ******************************************************************************
ok: [10.1.61.187] => {
"msg": "import_tasks: in1.yml"
}
TASK [set_fact] ***************************************************************************
ok: [10.1.61.187]
TASK [debug] ******************************************************************************
skipping: [10.1.61.187]
PLAY RECAP ********************************************************************************
10.1.61.187 : ok=8 changed=0 unreachable=0 failed=0 skipped=1
在handlers中使用include_tasks及import_tasks
我们知道,handlers中执行的其实也是任务,只不过是被触发才会运行,所以如果要在handlers中引入任务,也可直接使用 include_tasks 和 import_tasks 。没有 include_handlers 的说法。
import_playbook
我们在前面提到过,include除了可以引用任务列表,还可以引用整个playbook,在之后的版本中,如果想要引入playbook,则需要使用 import_playbook 模块。在2.8版本后,使用include引用整个playbook的特性会被弃用。
示例:
# cat import_playbook_ex.yml
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task"
- import_playbook: inplay.yml
# cat inplay.yml
- hosts: test
gather_facts: no
tasks:
- debug:
msg: "test task in inplay.yml"