内容简介:所属分类:ansible
- A+
所属分类:ansible 运维技术
在本博客中,ansible是一个系列文章,我们会尽量以通俗易懂的方式总结ansible的相关知识点。
ansible系列博文直达链接:ansible轻松入门系列
"ansible系列"中的每篇文章都建立在前文的基础之上,所以, 请按照顺序阅读这些文章,否则有可能在阅读中遇到障碍。
在进行编程时,我们都会将可以重复利用的代码提取出来,将提取出的代码作为一个逻辑单元,这个逻辑单元通常被称为"函数"或者"方法","函数"可以让我们更加方便的、重复的调用一段代码,而且,如果需要更改这段代码的逻辑,只要更改函数本身即可,所有调用函数之处的逻辑都会随之改变,同时,函数还可以让程序的可读性变强,比如,主干程序中只有几行代码,这几行代码只是调用了对应的几个函数,我们通常通过函数名称,就能大概的推算出函数的功能,当我们阅读主干程序时,看到各个函数的名称之后,就能够大概推算出整个主干程序的作用和逻辑,如果不使用函数,而是将所有代码都写在主干程序文件中,那么只能一行一行的阅读所有代码以后,才能大概的了解主干程序的作用,所以综上所述,函数能为我们带来很多的方便之处。
在ansible中,其实也有类似的功能,这种功能被称之为"include",这篇文章我们就来了解一下"include"的用法。
注意:ansible经过版本的迭代更新,"include"的使用方法也有所变化,此处我们先从比较原始的使用方法开始介绍"include",然后再介绍新的使用方式,原始的使用方式以及对应的关键字在之后的版本中可能会被弃用,所以官网推荐我们使用新的方式进行操作,但是此处我们仍然从原始的使用方式开始总结,以便你看到别人早期编写的playbook时,也能够理解其含义。
先来看一个小示例,假设,我想要编写两个playbook,这两个playbook分别用于安装LAMP环境和LNMP环境,两个playbook的大致内容如下:
# cat lamp.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - yum: name: mysql state: present - yum: name: php-fpm state: present - yum: name: httpd state: present # cat lnmp.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - yum: name: mysql state: present - yum: name: php-fpm state: present - yum: name: nginx state: present
你一定看出来了,无论在lamp.yml中还是在lnmp.yml中,安装 mysql 和 php 部分的任务完全相同的,所以,我们可以把这两个任务提取出来,作为一个逻辑单元,示例如下,我们把安装mysql和php部分的任务提取到install_MysqlAndPhp.yml文件中,文件内容如下:
# cat install_MysqlAndPhp.yml - yum: name: mysql state: present - yum: name: php-fpm state: present
如上例所示,两个task被提取到了install_MysqlAndPhp.yml文件中,当我们需要安装mysql和php-fpm时,只需要调用此yml文件即可,那么怎样调用这个文件呢?方法如下,我们只要把lamp.yml和lnmp.yml修改为如下模样即可:
# cat lamp.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - include: install_MysqlAndPhp.yml - yum: name: httpd state: present # cat lnmp.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - include: install_MysqlAndPhp.yml - yum: name: nginx state: present
正如你所看到的,我们使用了include模块,引用了install_MysqlAndPhp.yml文件,当我们引用此文件时,install_MysqlAndPhp.yml文件中的tasks都会在被引用处执行,这就是include的用法,是不是很简单,没错,include模块可以指定一个文件,这个文件中的内容是一个任务列表(一个或多个任务),当使用include模块引用对应的文件时,文件中的任务会在被引用处执行,就好像写在被引用处一样。
经过上述描述,你一定已经明白了怎样使用"include",上例中,是在tasks关键字中使用include引用了对应的任务列表,其实,在handlers关键字中,也可以使用include,在前文中我们总结过,handlers其实也是一种任务,只是这种任务有相应的触发条件而已(此处不再赘述),那么怎样在handlers关键字中使用include呢?示例如下
# cat test_include.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - file: path: /opt/ttt state: touch notify: test include handlers handlers: - name: test include handlers include: include_handler.yml # cat include_handler.yml - debug: msg: "task1 of handlers" - debug: msg: "task2 of handlers" - debug: msg: "task3 of handlers"
如上例所示,当"test include handlers"被触发时,include_handler.yml中的任务将会执行
在上述示例中,无论是在tasks中使用include,还是在handlers中使用include,都是引用了一个任务列表,其实,"include"不仅能够引用任务列表,还能够引用playbook,比如,在一个playbook中引用另一个playbook,示例如下:
# cat lamp.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - include: install_MysqlAndPhp.yml - yum: name: httpd state: present - include: lnmp.yml
如上例所示,我们在lamp.yml的结尾引入了lnmp.yml,当我们在执行lamp.yml时,会先执行lamp相关的任务,然后再执行lnmp.yml中的任务。
在使用"函数"或者"方法"时,可能会需要传入一些"参数",以便更加灵活的根据实际情况作出对应的处理,那么,include有没有相似的功能呢?答案是肯定的,示例如下:
# cat test_include1.yml
---
- hosts: test70
remote_user: root
gather_facts: no
tasks:
- include: in.yml
test_var1=hello
test_var2=test
# cat in.yml
- debug:
msg: "{{ test_var1 }}"
- debug:
msg: "{{ test_var2 }}"
如上例所示,在in.yml文件中一共有两个debug任务,这两个任务分别需要两个变量,在in.yml中并未定义任何变量,而是在test_include1.yml中使用include模块引用in.yml时,传入了两个参数,这两个参数的名字与变量名相同,执行上例playbook,可以看到in.yml中的两个任务都正常输出了,这就是向include文件传参的方法,是不是很容易,除了上述方法,我们还能够使用vars关键字,以key: value变量的方式传入参数变量,示例如下:
tasks: - include: in.yml vars: test_var1: hello test_var2: test
上述两种方式都可以传入参数变量,通过vars关键字也能够传入结构稍微复杂的变量数据,以便在包含的文件中使用,示例如下:
# cat test_include1.yml
---
- hosts: test70
remote_user: root
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 }}"
在前文 中,我们也总结过tags的使用方法,我们也可以针对某个include去打标签,示例如下:
# cat test_include1.yml --- - hosts: test70 remote_user: root 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_include1.yml时,指定tag为t2,那么in2.yml中的所有任务将会被执行,执行效果如下:
# ansible-playbook --tags=t2 test_include1.yml
PLAY [test70] ************************************
TASK [debug] ************************************
ok: [test70] => {
"msg": "task1 in in2.yml"
}
TASK [debug] ************************************
ok: [test70] => {
"msg": "task2 in in2.yml"
}
PLAY RECAP ************************************
所以,tag是针对include文件中的所有任务生效的。
经过上述描述 ,你一定已经猜到了,我们也可以对include添加条件判断,还可以对include进行循环操作,示例如下:
# cat test_include1.yml --- - hosts: test70 remote_user: root gather_facts: no tasks: - include: in3.yml when: 2 > 1 - include: in3.yml loop: - 1 - 2 - 3 # cat in3.yml - debug: msg: "task1 in in3.yml" - debug: msg: "task2 in in3.yml"
通过上例,你一定看明白了,如果我们想要循环的调用多个任务,则可以使用上例中的方法,将需要循环调用的多个任务写入到一个yml文件中,然后使用include调用这个yml文件,再配合loop进行循环即可。
说到循环,我们再来看一个小例子
# cat A.yml
---
- hosts: test70
remote_user: root
gather_facts: no
tasks:
- include: B.yml
loop:
- 1
- 2
- 3
# cat B.yml
- debug:
msg: "{{item}}--task1 in B.yml"
- debug:
msg: "{{item}}--task2 in B.yml"
如上例所示,我们在A.yml中include了B.yml,并且循环调用了B.yml中的两个任务,在B.yml中,我们输出了item的信息,上例playbook执行结果如下
# ansible-playbook A.yml
PLAY [test70] *************************************************
TASK [include] *************************************************
included: /testdir/ansible/B.yml for test70
included: /testdir/ansible/B.yml for test70
included: /testdir/ansible/B.yml for test70
TASK [debug] *************************************************
ok: [test70] => {
"msg": "1--task1 in B.yml"
}
TASK [debug] *************************************************
ok: [test70] => {
"msg": "1--task2 in B.yml"
}
TASK [debug] *************************************************
ok: [test70] => {
"msg": "2--task1 in B.yml"
}
TASK [debug] *************************************************
ok: [test70] => {
"msg": "2--task2 in B.yml"
}
TASK [debug] *************************************************
ok: [test70] => {
"msg": "3--task1 in B.yml"
}
TASK [debug] *************************************************
ok: [test70] => {
"msg": "3--task2 in B.yml"
}
PLAY RECAP *************************************************
test70 : ok=9 changed=0 unreachable=0 failed=0
如你所见,我们在B文件中输出了item的信息,而item的信息正是A文件中loop对应的列表,换句话说就是,内层文件中的任务使用的item其实是外层文件中的item。
聪明如你,一定想到了一个问题,如果B文件中也有循环操作,当我们在B文件中输出item信息时,item信息是B文件中的item信息,还是A文件中的item信息呢?我们一起来试试看,示例如下
# cat A.yml
---
- hosts: test70
remote_user: root
gather_facts: no
tasks:
- include: B.yml
loop:
- 1
- 2
# cat B.yml
- debug:
msg: "{{item}}--task in B.yml"
loop:
- a
- b
- c
如上例所示,B.yml中循环调用了debug模块,而在A.yml中,又循环的调用了B.yml,当出现这种"双层循环"的情况时,B文件中的item信息到底是什么呢?上例playbook执行效果如下:
如上图所示,当出现上述"双层循环"的情况时,内层item的信息为B.yml中的loop列表,而不是A.yml中的loop列表,在这种情况下,如果想要在B文件中获取到A文件中的item信息,该怎么办呢?其实,上图中紫色的警告信息中,已经包含了我们想要的答案,没错,正如你所看到的,在上例的情况中,使用'loop_var选项'即可在B文件中获取到A文件中的item信息,看到'loop_var',有没有觉得很眼熟?眼熟就对了,在前一篇文章中,我们保留了一个loop_control选项没有介绍,这个选项就是'loop_var'选项,那么现在,我们就来介绍一下loop_control的loop_var选项,示例如下:
# cat A.yml
---
- hosts: test70
remote_user: root
gather_facts: no
tasks:
- include: B.yml
loop:
- 1
- 2
loop_control:
loop_var: outer_item
# cat B.yml
- debug:
msg: "{{outer_item}}--{{item}}--task in B.yml"
loop:
- a
- b
- c
如上例所示,我们在A文件中循环调用了B文件,并且在循环时使用了loop_control的loop_var选项,我们将loop_var选项的值设置为"outer_item",这表示,我们将外层循环的item值存放在了"outer_item"变量中,在B文件中的debug任务中,同时输出了"outer_item"变量和"item"变量的值,执行A.yml,执行结果如下:
# ansible-playbook A.yml
PLAY [test70] *************************************************
TASK [include] *************************************************
included: /testdir/ansible/B.yml for test70
included: /testdir/ansible/B.yml for test70
TASK [debug] *************************************************
ok: [test70] => (item=a) => {
"msg": "1--a--task in B.yml"
}
ok: [test70] => (item=b) => {
"msg": "1--b--task in B.yml"
}
ok: [test70] => (item=c) => {
"msg": "1--c--task in B.yml"
}
TASK [debug] *************************************************
ok: [test70] => (item=a) => {
"msg": "2--a--task in B.yml"
}
ok: [test70] => (item=b) => {
"msg": "2--b--task in B.yml"
}
ok: [test70] => (item=c) => {
"msg": "2--c--task in B.yml"
}
PLAY RECAP *************************************************
test70 : ok=4 changed=0 unreachable=0 failed=0
从执行结果可以看出,"outer_item"变量中的值正是外层循环中item的值,这就是loop_var选项的作用。
当出现这种"双层循环"的情况时,则可以在外层循环中使用loop_var选项指定一个变量,这个变量可以用来代替外层循环中的item变量,以便在内层循环中获取到外层循环的item的值,从而避免了两层循环中"item"变量名的冲突,看到这里,你一定已经看明白了。
这篇文章就先总结到这里,希望能够对你有所帮助~
我的微信公众号
关注"实用运维笔记"微信公众号,当博客中有新文章时,可第一时间得知哦~
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
About Face 3
Alan Cooper、Robert Reimann、David Cronin / John Wiley & Sons / 2007-5-15 / GBP 28.99
* The return of the authoritative bestseller includes all new content relevant to the popularization of how About Face maintains its relevance to new Web technologies such as AJAX and mobile platforms......一起来看看 《About Face 3》 这本书的介绍吧!