自动化运维工具ansible-playbook

一、playbook简介与文件格式

playbook字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,此次由计算机进行表演,由计算机安装、部署应用,提供对外服务,以及组织计算机处理各类各样的事情。python

playbook文件由YMAL语言编写。YMAL格式是相似于JSON的文件格式,便于人理解和阅读,同时便于书写。首先学习了解一下YMAL的格式,对后面书写playbook颇有帮助。如下为playbook经常使用到的YMAL格式规则。linux

 文件的第一行应该以“---” (三个连字符)开始,代表YMAL文件的开始。
 在同一行中,#以后的内容表示注释,相似于shell,python和ruby。
 YMAL中的列表元素以“-”开头而后紧跟着一个空格,后面为元素内容。
 同一个列表中的元素应该保持相同的缩进。不然会被看成错误处理。
 play中hosts,variables,roles,tasks等对象的表示方法都是键值中间以“:”分隔表示,“:”后面还要增长一个空格。正则表达式

首先看下面这个例子:shell

- apple
- banana
- orange

等价于JSON的下面这个格式:json

[
 “apple”,
 “banana”,
 “orange”
]

playbook文件是经过ansible-playbook命令进行解析的,ansbile-playbook命令会根据自上而下的顺序依次执行playbook文件中的内容。安全

二、playbook的组成

playbook是由一个或多个“play”组成的列表。play的主要功能在于,将事先合并为一组的主机组合成事先经过Ansible定义好的角色。将多个play组织在一个playbook中就可让它们联同起来按事先编排好的机制完成一系列复杂的任务。ruby

playbooks主要有如下四部分构成,分别以下。app

 Target部分: 定义将要执行 playbook 的远程主机组。
 Variable部分: 定义playbook运行时须要使用的变量。
 Task部分: 定义将要在远程主机上执行的任务列表。
 Handler部分: 定义task 执行完成之后须要调用的任务。运维

下面介绍下构成playbook的四个组成部分。 dom

(1)Hosts和Users

  • playbook中的每个play的目的都是为了让某个或某些远程主机以某个指定的用户身份执行任务。
  • hosts:用于指定要执行任务的远程主机,每一个playbook都必须指定hosts,hosts也可使用通配符格式。主机或主机组在inventory清单(hosts文件)中指定,可使用系统默认的/etc/ansible/hosts,也能够本身编辑,在运行的时候加上-i选项,可指定自定义主机清单的位置。
  • remote_user:用于指定在远程主机上执行任务的用户。能够指定任意用户,也可使用sudo,可是用户必需要有执行相应任务的权限。

(2)任务列表

play的主体部分是task list。

task list中的各任务按次序逐个在hosts中指定的全部远程主机上执行,即在全部远程主机上完成第一个任务后再开始第二个。在运行自上而下某playbook时,若是中途发生错误,则全部已执行任务都将回滚,所以在更正playbook后须要从新执行一次。

task的目的是使用指定的参数执行模块,而在模块参数中可使用变量。模块执行一个命令,即便执行一次或屡次, 其结果是同样的,这意味着playbook屡次执行是安全的,由于其结果均一致。tasks包含name和要执行的模块,name是可选的,只是为了便于用户阅读,建议加上去,模块是必需的,同时也要给予模块相应的参数。

定义tasks推荐使用module: options”的格式,例如:

service: name=httpd state=running

(3)handlers

用于当关注的资源发生变化时采起必定的操做。handlers是和“notify”配合使用的。
“notify”这个动做可用于在每一个play的最后被触发,这样能够避免屡次有改变发生时,每次都执行指定的操做,经过“notify”,仅在全部的变化发生完成后一次性地执行指定操做。
在notify中列出的操做称为handler,也就是说notify用来调用handler中定义的操做。
注意:在notify中定义的内容必定要和handlers中定义的“ - name”内容同样,这样才能达到触发的效果,不然会不生效。

(4)tags
tags用于让用户选择运行或略过playbook中的部分代码。Ansible具备幂等性,所以会自动跳过没有变化的部分;可是当一个playbook任务比较多时,一个一个的判断每一个部分是否发生了变化,也须要很长时间。所以,若是肯定某些部分没有发生变化,就能够经过tags跳过这些代码片段。

三、Playbook执行结果解析

使用ansible-playbook运行playbook文件,输出的内容为JSON格式。而且由不一样颜色组成,便于识别。通常而言,输出内容中,每一个颜色表示的含义以下。
 绿色表明执行成功,但系统保持原样。
 黄色表明系统状态发生改变,也就是执行的操做生效。
 红色表明执行失败,会显示错误信息。

下面是一个简单的playbook文件:

- name: create user
  hosts: 172.16.213.231
  user: root
  gather_facts: false
  vars:
    user1: testuser
  tasks:
   - name: start createuser
     user: name="{{user1}}"

上面的playbook 实现的功能是新增一个用户,每一个参数含义以下。
 name参数对该playbook实现的功能作一个概述,后面执行过程当中,会输出name的值。
 hosts参数指定了对哪些主机进行操做。
 user参数指定了使用什么用户登陆到远程主机进行操做。
 gather_facts参数指定了在执行task任务前,是否先执行setup模块获取主机相关信息,此参数默认值为true,表示开启,若是咱们在task中要使用facts信息时,就须要开启此功能。不然能够设置为false。设置为false能够加快playbook的执行速度。
 vars参数,指定了变量,这里指字一个user1变量,其值为testuser,须要注意的是,变量值必定要用引号括起来。
 tasks指定了一个任务,其下面的name参数一样是对任务的描述,在执行过程当中会打印出来。user是一个模块,user后面的name是user模块里的一个参数,而增长的用户名调用了上面user1变量的值。

三、playbook中tasks语法使用

在playbook中,task部分是整个任务的核心,咱们前面介绍的ansible的经常使用模块,例如commands模块、shell模块、file模块、cron模块、user模块等,在playbook中仍然可用,每一个模块所使用的参数以及含义跟命令行模式下也彻底同样,只不过写法不一样而已,下面经过几个例子来看看playbook中常见功能模块的写法。

(1)、playbook示例

下面是一个playbook示例,test.yml文件内容以下:

- hosts: hadoophosts
  remote_user: root
  tasks:
   - name: create hadoop user
     user: name=hadoop state=present
   - name: create hadoop directory and chmod/chown
     file: path=/opt/hadoop state=directory mode=0755 owner=hadoop group=hadoop
   - name: synchronize hadoop program
     synchronize: src=/data/hadoop/ dest=/opt/hadoop
   - name: Setting environment variables
     shell: echo "export JAVA_HOME=/usr/jdk" >> /etc/profile

这个playbook文件中,使用了user、file、synchronize和shell模块,文件开始定义了一个主机组hadoophosts,而后设置root用户在远程主机上执行操做,接着,就是task任务的开始,“- name”是描述性信息,用来标识任务执行内容和进度,第一个task用来建立一个hadoop用户,使用了user模块,注意,上面的user表示ansible的user模块,而user后面的name、state是user模块的参数,这些参数含义上面已经作过介绍,这里就不在重复了

下面还有file模块、synchronize模块以及shell模块,它们的写法跟user模块相似,也再也不过多介绍。

今后文件能够看出,经过playbook模式编写的文件更加简洁、易懂,只要设置好了任务的运行策略、顺序,每次须要用到这个操做的话,直接执行就能够了。执行的方式以下:

[root@server239 ansible]# ansible-playbook  test.yml
除了前面已经介绍过的ansible模块,还有一些模块在playbook中也常常用到,下面再介绍一些经常使用的playbook模块。

(2)、unarchive模块

unarchive模块用来实现解压缩,也就是将压缩文件解压分发到远程不一样节点上。只需记住以下几个参数便可:

 src: 源文件路径,这个源文件在管理机上。
 dest: 指定远程主机的文件路径。
 mode:设置远程主机上文件权限。

看下面这个例子:

- hosts: 172.16.213.231
  remote_user: root
  gather_facts: false
  tasks:
   - name: unarchive spark files
     unarchive: src=/src/spark.tar.gz dest=/opt

这个操做是将管理机上的/src/spark.tar.gz文件传输到远程主机上后进行解压缩,并将解压缩后的文件放到远程主机的/opt目录下。注意,这个例子中咱们设置了gather_facts选项为false,这是由于下面的操做中,没有用到facts信息。

(3)、lineinfile、replace模块

在自动化运维中,对文件进行内容替换是一个很是常见的场景,好比修改、删除、添加操做系统的某些参数等,Ansible中虽然提供了shell模块结合sed命令来达到替换的效果,但常常会遇到须要转义的问题,而且考虑到可读性性和可维护性等多方面因素,使用Ansible自带的替换模块是一个不错的选择。Ansible经常使用的替换模块为replace和lineinfile。

replace模块能够根据指定的正则表达式替换远程主机下某个文件中的内容,经常使用的参数有以下几个:

 path:要操做的远程主机上文件的路径。
 regexp:正则表达式,指定替换规则。
 replace:指定最终要替换的字符串。
 backup:是否在修改文件以前对文件进行备份,yes是进行备份。

看下面这个例子:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: modify selinux
     replace: path=/etc/selinux/config regexp="enforcing" replace=disabled backup=yes

这个操做是对远程主机上/etc/selinux/config文件中的enforcing字符串进行替换,替换为disabled,替换前进行备份。其实就是关闭远程主机上selinux服务。

最后,再介绍一下lineinfile,此模块也能够实现replace的功能,但lineinfile功能更增强大,支持的参数也比较多,经常使用参数含义以下:

 path:操做的远程主机上的文件路径
 regexp:正则表达式,要替换的内容规则
 line:指定替换后的文本内容
 state:当设置为absent表明删除匹配的行
 insertafter:insertafter参数能够将文本插入到“指定的行”以后
 insertbefore:insertbefore参数能够将文本插入到“指定的行”以前
 backup:进行替换操做前是否进行备份

下面来看一个基于lineinfile的playbook任务:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - lineinfile: dest=/etc/profile insertafter='ulimit(.*)' line="ulimit -c unlimited"
   - lineinfile: dest=/etc/profile line="export JAVA_HOME=/usr/jdk"
   - lineinfile: dest=/etc/selinux/config regexp='SELINUX=(.*)' line='SELINUX=disabled'
   - lineinfile: dest=/etc/resolv.conf regexp='search(.*)' state=absent

这个playbook任务中,调用了四次lineinfile替换操做,第一次是在/etc/profile文件中找到以ulimit开头的行,并在后面添加一行内容"ulimit -c unlimited",第二次是在/etc/profile文件的最后添加一个JAVA_HOME路径,第三次是修改/etc/selinux/config文件中以“SELINUX=”开头的行,将其替换为“SELINUX=disabled”,其实就是关闭selinux,最后一个操做是在/etc/resolv.conf文件找查找以search开头的行,而后将其删除掉。

(4)、register、set_fact、debug模块

ansible中定义变量的方式有不少种,能够将模块的执行结果注册为变量,也能够在roles中的文件内定义变量,还可使用内置变量等,而register、set_fact均可用来注册一个变量。

使用register选项,能够将当前task的输出结果赋值给一个变量,看下面这个例子:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: ps command
     shell: hostname
     register: host_result
   - debug: var=host_result

此例子是将在远程主机上执行的shell命令“hostname”的输出结果赋值给变量host_result,而后再将变量引用并使用debug模块输出。输出结果是json格式的。注意,此例子最后还使用了debug模块,此模块用于在调试中输出信息.

下面是上面playbook的debug输出结果:

TASK [debug] **********************************************************
ok: [172.16.213.231] => {
    "host_result": {
        "changed": true,
        "cmd": "hostname",
        "delta": "0:00:00.007228",
        "end": "2020-04-01 04:42:34.254587",
        "failed": false,
        "rc": 0,
        "start": "2020-04-01 04:42:34.247359",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "server231.localdomain",
        "stdout_lines": [
            "server231.localdomain"
        ]
    }
}

能够看出,此输出是一段json格式的数据,最顶端的key为host_result,大括号内还有多个二级key,咱们想要的结果是输出远程主机的主机名便可,不须要这些额外的二级key信息,如何实现这个要求呢,若是想要输出json数据的某二级key项,可使用"key.dict"或"key['dict']"的方式引用便可。从上面输出能够看到,咱们须要的二级key是stdout项,因此要仅仅输出此项内容,能够将变量引用改成host_result.stdout便可,也就是将上面的playbook任务改为以下内容:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: hostname command
     shell: hostname
     register: host_result
   - debug: var=host_result.stdout
   - debug: 'msg="output: {{host_result.stdout}}"'

在这个playbook中,咱们又增长了一个debug参数,debug模块经常使用的参数有两个,分别是msg和var,它们均可以引用变量输出信息,但有一点小区别,msg能够输出自定义信息,而且变量须要双大括号包含起来,而var参数只能输出变量,而且不须要双大括号。

修改后的playbook执行debug输出结果以下:

TASK [debug] ********************************************************
ok: [172.16.213.231] => {
    "host_result.stdout": "server231.localdomain"
}

TASK [debug] ********************************************************
ok: [172.16.213.231] => {
    "msg": "output: server231.localdomain"
}

从输出可知,这个才是咱们想要的结果。

set_fact和register的功能很相似,它也能够将task输出赋值给变量。set_fact更像shell中变量的赋值方式,能够将某个变量的值赋值给另外一个变量,也能够将字符串赋值给变量。看下面这个例子:

- hosts: 172.16.213.231
  remote_user: root
  tasks:
   - name: hostname command
     shell: hostname
     register: host_result
   - set_fact: var1="{{host_result.stdout}}"
   - set_fact: var2="This is a string"
   - debug: msg="{{var1}}  {{var2}}"

这个例子是将hostname的输出结果赋值给host_result变量,而后经过set_fact将host_result变量赋值给var1变量,接着又将一个字符串赋值给var2变量,最后,经过debug模块输出这些变量信息。注意这些模块的使用方式和书写格式。

这个playbook的输出结果为:

TASK [debug] **********************************************************
ok: [172.16.213.231] => {
    "msg": "server231.localdomain  This is a string"
}

(5)、delegate_to、connection、和local_action模块

ansible默认只会对远程主机执行操做,但有时候若是须要在管理机本机上执行一些操做,该如何实现呢,这个实现的方法有不少,能够经过delegate_to(任务委派)来实现,也能够经过connection:local方法,还能够经过local_action关键字来实现。

下面来看一个例子,说明它们的用法。

- hosts: 172.16.213.231
  remote_user: root
  gather_facts: true
  tasks:
   - name: connection
     shell: echo "connection . {{inventory_hostname}} $(hostname) ." >> /tmp/local.log
     connection: local
   - name: delegate_to
     shell: echo "delegate_to . {{inventory_hostname}} $(hostname) ." >> /tmp/local.log
     delegate_to: localhost
   - name: local_action
     local_action: shell echo "local_action. {{inventory_hostname}} $(hostname)" >> /tmp/local.log

这个例子中,依次使用了connection、delegate_to和local_action三种方式,还使用了一个变量{{inventory_hostname}},这是ansible的一个内置变量,它用来获取远程主机的主机名,说到主机名,其实就是用到了facts信息,因此,须要设置gather_facts选项为true,另外,$(hostname)是shell里面的变量,也是用来获取主机名,此例子实现的功能是将远程主机的主机名依次输出到管理机的/tmp/local.log文件中。

相关文章
相关标签/搜索