ansible笔记(10):初识ansible playbook 假设,我们想要在test70主机上安装nginx并启动,我们可以在ansible主机中执行如下3条命令 ansible test70 -m yum_repository -a 'name=aliEpel description="alibaba EPEL" baseurl=http://mirrors.aliyun.com/epel/7/$basearch' ansible test70 -m yum -a 'name=nginx disable_gpg_check=yes enablerepo=aliEpel' ansible test70 -m service -a "name=nginx state=started" 我们通过上述3条命令,先确定配置了对应的yum源,然后使用yum模块安装了nginx,最后使用service模块启动了nginx,最终达到了我们的目的。 但是在实际的工作环境中,我们可能需要经常在新主机上安装nginx,难道每次有新的服务器加入工作环境,我们都要修改上述3条命令中的主机名并且重新将每一条命令执行一遍吗?这样似乎有些麻烦,肯定有更好的办法,没错,我们可以将上述命令写成脚本,每次修改一些变量,然后执行脚本就行了,这样似乎方便了不少,而ansible天生就提供了这种类似"脚本"的功能,在ansible中,类似"脚本"的文件被称作"剧本",'剧本'的英文名称为'playbook',我们只需要将要做的事情编写成playbook,把不同的模块按照顺序编排在剧本中,ansible就会按照剧本一步一步的执行,最终达到我们的目的,虽然playbook的功能与脚本类似,但是剧本并不是简单的将ad-hoc命令按照顺序堆砌在一个可执行文件中,编写剧本需要遵循YAML语法,如果你没有接触过YAML语法,不用害怕,坚持看完后面的示例,熟悉一些固定套路以后,你也可以自己编写playbook。 那么怎样编写playbook呢?我们先从一个简单的示例开始 首先,我们需要创建一个YAML格式的playbook文件。 playbook文件以".yaml"或者".yml"作为文件名后缀,此处我们创建一个名为"test.yml"的剧本文件。 在编写剧本之前,我们先来回顾两个简单的ad-hoc命令,比如如下两条命令: [root@node1 data]# ansible test211 -m ping [root@node1 data]# ansible test211 -m file -a "path=/data/test211 state=directory" 上述命令表示使用ping模块去ping主机test70,然后再用file模块在test70主机上创建目录,那么,如果把上述命令转换成playbook的表现形式,该如何书写呢?示例如下 (此处先进行示例,后文会说明怎样执行playbook) --- - hosts: test211 remote_user: root tasks: - name: ping the host ping: - name: make directory test file: path: /data/testfile state: directory 如上所示,第一行使用三个横杠作为开始,在YAML语法中,"---"表示文档开始。 第二行使用"- "作为开头(注意:横杠后面有空格),如果你了解过YAML语法,那么你一定知道YAML使用"- "表示一个块序列的节点,如果你不了解YAML语法,不用在此处纠结,先这么写,写的多了,自然会理解,从上例可以看出,"- "后面使用hosts关键字指定了要操作的主机,hosts关键字对应的值为test70,表示我们要在test70主机上进行操作,"hosts: test70"是一个键值对,注意,在YAML语法中使用冒号映射键值对时,'冒号'后面必须有'空格',这也是语法,没有为什么,记住就好,如果你想要一次性在多台主机上进行操作,可以同时写多个主机,每台主机使用逗号隔开,比如'hosts: test70,test61',如果你在清单中对主机进行了分组,也可以使用组名。 第三行,使用remote_user关键字可以指定在进行远程操作时使用哪个用户进行操作,'remote_user: root'表示test70的root用户进行操作,上图中,remote_user关键字与hosts关键字对齐,表示它们是平级的,之前的文章中提到过,在YAML语法中进行缩进时,不能使用tab键进行缩进,必须使用空格,所以,为了兼容使用tab键进行缩进的使用习惯,可以将vim编辑器设置为自动将tab转成空格。 第四行,使用tasks关键字指明要进行操作的任务列表,之后的行都属于tasks键值对中的值。 之后的行都属于tasks任务列表中的任务,可以看出,整个任务列表一共有两个任务组成,每个任务都以"- "开头,每个任务都有自己的名字,任务名使用name关键字进行指定,第一个任务使用ping模块,使用ping模块时没有指定任何参数。第二个任务使用file模块,使用file模块时,指定了path参数与state参数的值。 好了,test.yml已经编写完成了,但是,我们还没有运行这个剧本,运行剧本需要使用'ansible-playbook'命令,示例如下 [root@node1 data]# ansible-playbook test.yml PLAY [test211] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************************************************************************************************************************************************** ok: [test211] TASK [ping the host] **************************************************************************************************************************************************************************************************************************************************************************************************************************************************** ok: [test211] TASK [make directory test] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************** changed: [test211] PLAY RECAP ************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** test211 : ok=3 changed=1 unreachable=0 failed=0 如上图所示,playbook执行后返回了一些信息,这些信息是这次剧本运行的概况。 'PLAY [test211]'表示这次运行的playbook中有一个'play'是针对test211这台主机运行的,一个'playbook'是由一个或多个'play'组成的,这样说可能不太容易理解,那么我们打个比方,一个'剧本'是由一个或多个'桥段'组成的,每个桥段都有不同的场景、人物、故事,所有的桥段组合在一起,组成一个完整的剧本,剧本就是playbook,桥段就是play,而上例中,整个剧本中只有一个桥段,也就是说,上例的playbook中,我们只写了一个play,在下文中我们会举例说明怎样书写多个play,到时候你会更加明白到底什么是所谓的'play',当然,'桥段'只是我自己为了方便理解给'play'起的中文名,官方名称只叫"play"。 从上述信息可以看出,仅有的这个play是针对test211运行的,这个play一共包含三个任务,第一个任务的名字叫做'Gathering Facts',第二个任务的名字叫做'Ping the host',第三个任务的名字叫做'make directory test',看到此处你会发现,我们在playbook中明明只写了两个任务,为什么最后执行时却有三个任务呢?这是因为,每个play在执行时,都会先执行一个默认任务,这个默认任务就是'Gathering Facts','Gathering Facts'任务会收集当前play对应的目标主机的相关信息,收集完这些基础信息后,才会执行我们指定的任务,由于上例中,hosts的值只有test211一个主机,所以这个play只针对test211运行,所以'Gathering Facts'这个任务只收集了test211的相关信息,执行完默认任务后,开始执行'Ping the host'任务和'make directory test'任务,由于在执行playbook之前,/testdir/test目录已经存在于test211主机中,所以'make directory test'任务返回的信息是绿色的,如果对应的目录并不存在,'make directory test'任务返回的信息应该是黄色的,这是因为幂等性的缘故,前文已经解释过,此处不再赘述。 当playbook中的所有play执行完毕后,在返回信息的'PLAY RECAP'中可以对所有目标主机的执行情况进行'回顾',由于我们的目标主机只有test211一台,所以只看到了test211的执行概括信息。 我们已经执行了一个playbook,这个playbook中只有一个play,我们也可以在这个playbook中多写几个play,示例如下: --- - hosts: test211 remote_user: root tasks: - name: pint the host ping: - name: make directory test211 file: path: /data/test211 state: directory - hosts: test212,test215 remote_user: root tasks: - name: touch file file: path: /touchfile state: touch - hosts: test212 test215 remote_user: root tasks: - name: create user jack user: name: jack 如上所示,上例中有多个play,第一个play针对test211执行,这个play会执行两个任务。 第二个play针对test212和test215执行,第二个play只包含一个任务,即在对应的目标主机上创建/touchfile文件 第三个play针对test212和test215执行,细心如你一定发现了,当你需要在play中指定多个主机时,有两种语法可以使用,第二个play和第三个play中的语法都可以指定多个主机,第三个play也只包含一个任务,即在test212和test215主机上创建jack用户。 多个play写完以后,我们来运行一下剧本试试 如下所示,每个play执行时都会显示当前play对应的目标主机,并且每个play执行时都会先执行默认的facts任务,收集对应目标主机的信息,当所有play都执行完毕后,会出现'PLAY RECP',相当于一个小的报告信息,看到这里,你应该已经理解了到底什么是所谓的'play'了吧。 [root@node1 data]# ansible-playbook test2.yml PLAY [test211] **************************************************************** TASK [Gathering Facts] ******************************************************** ok: [test211] TASK [pint the host] ********************************************************** ok: [test211] TASK [make directory test211] ************************************************* ok: [test211] PLAY [test212,test215] ******************************************************** TASK [Gathering Facts] ******************************************************** ok: [test215] ok: [test212] TASK [touch file] ************************************************************* changed: [test212] changed: [test215] PLAY [test212 test215] ******************************************************** TASK [Gathering Facts] ******************************************************** ok: [test215] ok: [test212] TASK [create user jack] ******************************************************* changed: [test215] changed: [test212] PLAY RECAP ******************************************************************** test211 : ok=3 changed=0 unreachable=0 failed=0 test212 : ok=4 changed=2 unreachable=0 failed=0 test215 : ok=4 changed=2 unreachable=0 failed=0 如果你并不是很熟悉YAML语法,你可以不用过于深究YAML语法,只需要死记硬背,记住某些"固定套路"即可编写playbook,写的多了,自然会对这些语法有所理解 如果你的playbook写完了,但是你不能确定playbook文件中是否存在语法错误,那么你可以使用如下命令对playbook进行语法检查 [root@node1 data]# ansible-playbook --syntax-check /data/test2.yml 执行上述命令后,如果只返回了playbook的名称,就表示没有语法错误。 除了对playbook进行语法测试,我们还能够'模拟执行'playbook,'模拟执行'并不是真正的执行,只是'假装'执行一下,playbook中的任务并不会真正在目标主机中运行,所以你可以放心大胆的进行模拟,使用如下命令即可模拟运行playbook,模拟运行功能可以帮助我们'预估'playbook是否能够正常执行。 [root@node1 data]# ansible-playbook --check test2.yml 注意:使用上述命令进行'模拟'时,一些任务可能会报错,这可能是因为报错的任务在执行时需要依赖之前的其他任务的完成结果,但是因为是'模拟'执行,所以之前的任务并不会真正的执行,既然之前的任务没有真正的执行,自然不会产生对应的结果,所以后面的任务就报错了,也就是说,我们并不能完全以'模拟'的反馈结果作为playbook是否能够正常运行的判断依据,只能通过'模拟'大概的'预估'一下而已。