浅谈ansible,一个强大的批量管理工具

一,ansible环境搭建及初体验

1.Ansible具有如下特点:

部署简单,只需在主控端部署Ansible环境,被控端无需做任何操作;
默认使用SSH协议对设备进行管理;
主从集中化管理;
配置简单、功能强大、扩展性强;
支持API及自定义模块,可通过Python轻松扩展;
通过Playbooks来定制强大的配置、状态管理
对云计算平台、大数据都有很好的支持;

2,本地测试环境

ansible-01  192.168.5.46  Ansible主控端
ansible-02  192.168.5.198  被控端
ansible-03        192.168.4.170       被控端

3,在ansible-01上部署Ansible,并配置环境变量
yum安装Ansible需要epel源,安装完成后查看下版本

添加yum 源

vim /etc/yum.repos.d/ansible

添加如下内容:

[epel]
name = all source for ansible
baseurl = https://mirrors.aliyun.com/epel/7/x86_64/
enabled = 1
gpgcheck = 0
[ansible]
name = all source for ansible
baseurl = http://mirrors.aliyun.com/centos/7.3.1611/os/x86_64/
enabled = 1
gpgcheck = 0
yum clean all

安装ansible:

yum install ansible -y
ansible --version

4,配置主机ssh互信

ssh-keygen

一直敲回车键即可
5,将密钥拷贝到被控端

ssh-copy-id root@192.168.6.190

------------------到这里可以说我们已经可以进行本地进行一些简单的操作,但是离我们批量管理主机还是有很大差距,下面进行第一步配置(inventory)——清单,实现批量控制远程主机

二,制定演员表

1,记录目标主机
我们在章节一中没有对ansible进行任何配置,所以只能通过localhost来控制本机,所以说离达到我们批量控制主机差距还很远,那么控制端怎么指定特定的主机执行特定的人物呢?

让ansible发挥重大作用的第一步就是配置演员表(inventory),他表示主机节点清单,也是资源的一种,通过配置inventory可以定义哪些主机是可以被控制的。其实这个过程和拍电影有点像,inventory就是电影中的演员表,而inventory定义的主机就是演员,负责完成各自的剧本
1.1 inventory文件路径
inventory默认的路径为/etc/ansible/hosts,当然我们也可以通过修改配置文件去修改这个路径
在下面配置文件中修改#inventory = /etc/ansible/hosts

vim /etc/ansible/ansible.cfg

1.2配置inventory
ansible inventory文件的书写格式遵循ini配置模式。从2.4以后支持其他格式,比如yaml格式的inventory。此处以ini格式为例,假设所有的规则都定义在/etc/ansible/hosts文件中。
1.2.1一行一主机的定义方式
ansible模式是基于ssh连接的,所以一般情况下inventory中的每隔目标节点都配置主机名或者IP地址、sshd监听的端口号、链接的用户名密码、ssh连接时的参数等等,当然还有很多默认值,所以最简单的是直接指定主机名或者IP地址即可。

例如,在默认的inventory文件/etc/ansible/hosts添加我们的测试被控端目标主机

ansible-02
ansible_03 ansible_host=192.168.4.170
192.168.6.198
192.168.4.170:22
192.168.4.1[1:3] ansible_port=22

这样定义以后,ansible就可以控制我们任意一个目标主机了

[root@ansible-01 ~]# ansible ansible-02 -m copy -a 'src=/etc/passwd dest=/tmp'
ansible-02 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "checksum": "559241a27cdef8fe05dd2e7ba10fd395e4031bd5", 
    "dest": "/tmp/passwd", 
    "gid": 0, 
    "group": "root", 
    "mode": "0644", 
    "owner": "root", 
    "path": "/tmp/passwd", 
    "secontext": "unconfined_u:object_r:admin_home_t:s0", 
    "size": 898, 
    "state": "file", 
    "uid": 0
}

[root@ansible-01 ~]# ansible 192.168.6.198 -m file -a 'path=/tmp/passwd state=absent'
192.168.6.198 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "path": "/tmp/passwd", 
    "state": "absent"
}

上面的inventory配置中:
(1)第一行通过主机名定义,在absible链接该节点时会进行主机名dns解析
(2)第二行也是通过主机名进行定义,但是使用了一个主机变量ansible_host=IP,此时ansible去链接该节点时将直接通过IP地址进行转换,而不会进行dns解析,所以此时的ansible_03相当于时主机别名,他可以命名为任何其他名称,如ansible_04
(3)第三行通过主机IP进行定义
(4)第四行通过IP定义时还加了端口号
(5)第五行通过范围方式展开成了3个主机节点192.168.4.11和192.168.4.12和192.168.4.13,同时还定义了这3个节点的主机变量,ansible_port=22表示链接这两个节点时的端口号为22
范围展开的方式还支持字母范围,下面都是可行的:

范围表示   展开结果
a[1:3]    -->a1,a2,a3
[08:12]   -->08,09,10,11,12a
[a:c]     -->aa,ab,ac

上面示例中使用了两个主机变量ansible_port和ansible_host,它们直接定义在主机的后面,这些变量都是连接目标主机时的行为控制变量,通常它们都能见名知意。Ansible支持很多个连接时的行为控制变量,而且不同版本的Ansible的行为控制变量名称可能还不同,比如在以前版本中指定端口号的行为变量是ansible_ssh_port 。

inventory变量命                             含义  
ansible_host                     ansible连接节点时的IP地址
ansible_port                     连接对方的端口号,ssh连接时默认为22
ansible_user                     连接对方主机时使用的主机名。不指定时,将使用执行ansible或ansible-playbook命令的用户
ansible_password              连接时的用户密码
ansible_connection            连接类型,有效值包括smart、ssh、paramiko、local、docker、winrm,默认为smart。smart表示智能选择ssh和paramiko,当SSH支持ControlPersist(即持久连接)时使用ssh,否则使用paramiko。local和docker是非基于ssh连接的方式,winrm是连接windows的插件
ansible_ssh_private_key_file      指定密钥认证ssh连接
时的私钥文件
ansible_ssh_common_args         提供给ssh、sftp、scp命令的额外参数
ansible_become                           允许进行权限提升
ansible_become_method              指定提升权限的方式,例如可使用sudolsu/runas等方式
ansible_become_user                   提升为哪个用户的权限,默认提升为root
ansible_become_password           提升为指定用户权限时的密码

1.2.2 inventory中的普通变量
在定义inventory时,除了可以指定连接的行为控制变量,也可以指定Ansible的普通变量,以便在ansible执行任务时使用。
例如:

node1 node1_var="hello world"node2 ansible_host=192.168.200.28

在ansible执行任务时可以引用普通变量:

$ ansible node1 -m debug -a 'var=node1_var'node1 | SUCCESS =>{
"node1_var": "hello world"
}

1.2.3主机分组
上面的示例中是每行单独定义一个主机,这样的方式虽然简单,但是极其不方便管理多个节点。
为此,Inventory支持对主机进行分组,每个组内可以定义多个主机,每个主机都可以定义在任何一个或多个主机组内。
例如:

[nginx]
192.168.200.27
192.168.200.28 ansible_password='123456'192.168.200.29

[apache]
192.168.200.3[0:3]

[mysql]
192.168.200.27
192.168.200.29

这里定义了3个主机组: nginx主机组、apache主机组和mysql主机组。nginx组包含3个节点,apache主机组包含4个节点,mysql主机组包含2个节点。

需要注意的是,mysql组中的节点也同时存在于nginx组,一个主机同时存在于多个组内是允许也是必要的功能,只有这样才能更为灵活的对各个节点进行分类管理。

有了主机组,就可以让ansible控制一个组,从而让该组内所有主机执行任务:

$ ansible apache -m copy -a 'src=/etc/passwd dest=/tmp'

Ansible默认预定义了两个主机组: all分组和ungrouped 分组。
(1).all分组中包含所有分组内的节点
(2).ungrouped分组包含所有不在分组内的节点
(3).这两个分组都不包含localhost这个特殊的节点

定义了inventory之后,可以使用ansible --list或ansible–playbook --list命令来查看主机组的信息,还可以使用更为专业的ansible-inventory命令来查看主机组信息。

#使用ansible或ansible-playbook列出所有主机
$ ansible -iletclansible/hosts nginx --list
hosts (3):
192.168.200.27
192.168.200.28
192.168.200.29
#使用ansible-inventory列出nginx组中的主机
$ ansible-inventory -iletc/ansible/hosts nginx --graph
@nginx:
|--192.168.200.27
|--192.168.200.28
|--192.168.200.29
 #使用ansible-inventory列出nginx组中的主机,同时带上变量
 $ ansible-inventory nginx --graph --vars
 @nginx:
l--192.168.200.27
l l--{ansible_password = 123456}
ll--{ansible_port = 22}
l--192.168.200.28
ll--{ansible_password = 123456}l l
--{ansible_port = 22}
l--192.168.200.29
ll--{ansible_password = 123456}
l l--{ansible_port = 22}
l--{ansible_password = 123456}
#使用ansible-inventory列出all组内的主机
$ ansible-inventory --graph all
all:
l --@apache:
||--192.168.200.30
ll--192.168.200.31
ll--192.168.200.32
|--192.168.200.33l --@mysql:
||--192.168.200.27
| |--192.168.200.29
l --@nginx:
| |--192.168.200.27
| |--192.168.200.28
| |--192.168.200.29
l --@ungrouped:
| | --node1
| |--node2
#使用ansible-inventoryl以json格式列出所有主机的信息$ansible-inventory --list

1.2.4 主机组变量
有了主机组之后,可以直接为主机组定义变量,这样组内的所有主机都具有该变量。

[nginx]
192.168.200.27
192.168.200.28 ansible_password=123456
192.168.200.29
[nginx:vars]
ansible_password='123456'
[all:vars]
ansible_port=22
[ungrouped:vars]ansible_port=22

上面[nginx:vars]表示为nginx组内所有主机定义变量ansible_password=‘123456’。而[all:vars]和[ungrouped:vars]分别表示为all和ungrouped这两个特殊的主机组内的所有主机定义变量。
1.2.5 组嵌套
lnventory还支持主机组的分组嵌套,可以通过[GROUP:children]的方式定义一个主机组,并在其中包含子组。

例如:

[nginx]
192.168.200.27
192.168.200.28
192.168.200.29
[apache]
192.168.200.3[0:3]
[webservers:children]
nginx
apache

其中webservers主机组中包含了nginx组合apache组内的所有主机。
甚至,还可以递归嵌套。例如下面的示例中,centos7分组中的webservers仍然是一个包含子组的分组。

[nginx]
192.168.200.27
192.168.200.28
192.168.200.29
[apache]
192.168.200.3[0:3]
[mysql]
192.168.200.27
192.168.200.30
[webservers:children]
nginx
apache
[centos7:children]
webservers
mysql

1.2.6多个inventory文件
当Ansible要管理的节点非常多时,仅靠分组的逻辑可能也不足够方便管理,这个时候可以定义多个inventory文件并放在一个目录下,并按一定的命名规则为每个inventory命名,以便见名知意。

例如,创建一个名为/etclansiblelinventorys的目录,在其中定义a和b两个inventory文件,内容分别如下:

#/etc/ansiblelinventorys/a的内容:
[nginx]
192.168.200.27
192.168.200.28 ansible_password='123456'
192.168.200.29
[apache]
192.168.200.3[0:3]
#/etc/ansible/inventorys/b的内容:
[mysql]
192.168.200.27
192.168.200. 29
[web:children]
apache
nginx
[os:children]
web
mysql

现在要使用多个inventory的功能,需要将inventory指定为目录路径。
例如,Ansible配置文件将inventory 指令设置为对应的目录:

inventory       = /etc/ansible/inventorys

或者,ansible或ansible-playbook命令使用-i INVENTORY选项指定的路径应当为目录。

$ansible-inventory -i /etc/ansible/inventorys --graph all

执行下面的命令将列出所有主机:

$ ansible-inventory -i /etc/ansible/inventorys --graph all

inventory指定为目录时,inventory文件最好不要带有后缀,就像示例中的a和b文件。因为Ansible当使用目录作为inventory时,默认将忽略一些后缀的文件不去解析。需要修改配置文件中的inventory_ignore_extensions项来禁止忽略指定后缀(如ini后缀)的文件。

#inventory_ignore_extensions = ~,.orig, .bak, .ini, .cfg, .retry,.pyc,.pyo
inventory_ignore_extensions = ~,.orig,.bak,.cfg,.retry,.pyc,.pyo

1.2.7 ansible初体验
好了,到这里我们已经做好了一些基本的配置,下面我们可以简单操作几个模块来初步体验一下ansible怎么批量管理多个主机。
在控制节点上执行:

ansible web -m copy -a 'src=/etc/passwd dest=/tmp'
192.168.6.252 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "559241a27cdef8fe05dd2e7ba10fd395e4031bd5", 
    "dest": "/tmp/passwd", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "bf3868e5ab41324a70c287ba4a2b276c", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:admin_home_t:s0", 
    "size": 898, 
    "src": "/root/.ansible/tmp/ansible-tmp-1612151416.64-2827-211063335286658/source", 
    "state": "file", 
    "uid": 0
}
192.168.7.24 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "559241a27cdef8fe05dd2e7ba10fd395e4031bd5", 
    "dest": "/tmp/passwd", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "bf3868e5ab41324a70c287ba4a2b276c", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:admin_home_t:s0", 
    "size": 898, 
    "src": "/root/.ansible/tmp/ansible-tmp-1612151416.67-2829-181410410282037/source", 
    "state": "file", 
    "uid": 0
}

该命令的作用是将控制端的/etc/passwd文件拷贝到被控制主机节点的/tmp下

ansible是一个命令,我们记住就好了,出了ansible还有我们上面用到的ansible-playbook命令。

web参数表示ansible要控制的节点主机组,这里将指挥主机组里的主机执行拷贝操作

执行任务主要是执行模块,模块的执行可以还依赖一些模块参数。在ansible命令行中,使用-m Module来指定要执行哪个模块,即执行什么任务,使用-a ARGs来指定模块运行时的参数。
本示例中的模块为copy模块,传递给copy模块的参数包含两项:

. src=/etc/ passwd 指定源文件
. dest=/tmp            指定拷贝的目标路径

现在我们只要记住一些常用的模块即可,后面我们可以按需求到官网去查找https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
有时候为了方便快速查找到需要的模块,可以使用ansible-doc -l | grep 'xxx'命令来筛选模块。例如我想查看具有拷贝功能的模块:

ansible-doc -l | grep 'copy'
vsphere_copy                                                  Copy a file to a VMware datastore                                                                                       
win_copy                                                      Copies files to remote locations on windows hosts                                                                       
bigip_file_copy                                               Manage files in datastores on a BIG-IP                                                                                  
ec2_ami_copy                                                  copies AMI between AWS regions, return new image id                                                                     
win_robocopy                                                  Synchronizes the contents of two directories using Robocopy                                                             
copy                                                          Copy files to remote locations                                                                                          
na_ontap_lun_copy                                             NetApp ONTAP copy LUNs                                                                                                  
icx_copy                                                      Transfer files from or to remote Ruckus ICX 7000 series switches                                                        
unarchive                                                     Unpacks an archive after (optionally) copying it from the local machine                                                 
ce_file_copy                                                  Copy a file to a remote cloudengine device over SCP on HUAWEI CloudEngine switches                                      
postgresql_copy                                               Copy data between a file/program and a PostgreSQL table                                                                 
ec2_snapshot_copy                                             copies an EC2 snapshot and returns the new Snapshot ID                                                                  
nxos_file_copy                                                Copy a file to a remote NXOS device                                                                                     
netapp_e_volume_copy                                          NetApp E-Series create volume copy pairs      

根据描述,大概确定是否有需要的模块。
找到模块后,想看他的大概功能和用法,可以继续使用ansible-doc命令

#详细模块描述手册
ansible-doc copy
#只包含模块参数用法的模块描述手册
ansible-doc -s copy

再继续看一个例子,通过ansible删除刚才我们copy到远程主机上的passwd文件。

ansible web -m file -a 'path=/tmp/passwd state=absent'

path表示要操作的文件路径
state指定执行什么操作,此处指定absent表示删除操作
Ansible的很多模块都提供了一个state参数,它是一个非常重要的参数。它的值一般都会包含present和absent两种状态值(并非一定),不同模块的present和absent状态表示的含义不同,但通常来说,present状态表示肯定、存在、会、成功等含义,absent则相反,表示否定、不存在、不会、失败等含义。

例如这里的file模块,absent状态表示递归删除文件/目录,类似于rm -r命令,touch状态和touch命令的功能一样,directory状态表示递归创建目录,类似于mkdir -p命令。

所以,我们在远程主机上创建文件和目录的命令如下:

#创建文件
ansible web -m file -a 'path=/tmp/a state=touch'
192.168.6.252 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/tmp/a", 
    "gid": 0, 
    "group": "root", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 0
}
192.168.7.24 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "dest": "/tmp/a", 
    "gid": 0, 
    "group": "root", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 0
}
#创建目录
ansible web -m file -a 'path=/tmp/b state=directory'
192.168.7.24 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "root", 
    "path": "/tmp/b", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 6, 
    "state": "directory", 
    "uid": 0
}
192.168.6.252 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "gid": 0, 
    "group": "root", 
    "mode": "0755", 
    "owner": "root", 
    "path": "/tmp/b", 
    "secontext": "unconfined_u:object_r:user_tmp_t:s0", 
    "size": 6, 
    "state": "directory", 
    "uid": 0
}

最后再看一个debug模块,他用来调试或者输出一些数据。
debug两个常用的参数:
msg和var,这两个参数是互斥的,所以只能使用其中一个。msg参数可以输出字符串,也可以输出变量值,var参数只能输出变量值。

例如,输出“hello world”,需要使用msg参数:

ansible web -m debug -a 'msg="hello world"'
192.168.6.252 | SUCCESS => {
    "msg": "hello world"
}
192.168.7.24 | SUCCESS => {
    "msg": "hello world"
}

ansible中也支持使用变量,ansible命令的-e选项或者–extra-vars选项可以设置变量,设置的方式为-e ‘var1=“aaa” var2=“bbb”’。
例如,设置变量后使用debug的msg参数输出

[root@localhost ~]# ansible web -e 'str=world' -m debug -a 'msg="hell {{str}}"'
192.168.6.224 | SUCCESS => {
    "msg": "hell world"
}
192.168.7.49 | SUCCESS => {
    "msg": "hell world"
}

注意上面示例中的msg=“hello {{str}}”,Ansible的字符串是可以不用引号去包围的,例如msg=hello是允许的,但如果字符串中包含了特殊符号,则可能需要使用引号去包围,例如此处的示例出现了会产生歧义的空格。此外,要区分变量名和普通的字符串,需要在变量名上加一点标注:用{[}包围Ansible的变量,这其实是Jinja2模板(如果不知道,先别管这是什么)的语法。其实不难理解,它的用法和Shell下引用变量使用s符号或$(}是一样的,例如echo “hello ${var}”。

debug模块除了msg参数,还有一个var参数,它只能用来输出变量(还包括以后我们要介绍的Jinja2表达式),而且var参数引用变量的时候,不能使用{0}}包围,因为var参数已经帮我们隐式地包围了一层{0}。例如:

[root@localhost ~]# ansible web -e 'str=hello world' -m debug -a 'var=str'
192.168.6.224 | SUCCESS => {
    "str": "hello"
}
192.168.7.49 | SUCCESS => {
    "str": "hello"
}

另外ansible默认的全局配置文件大概有以下几个
(1).ANSIBLE_CFG:环境变量指定的配置文件
(2). ansible.cfg:当前目录下的ansible.cfg
(3). ~/ .ansible.cfg:家目录下的ansible.cfg
(4)./etc/ansible/ansible.cfg: 默认的全局配置文件

三,ansible的灵魂:playbook

1,Ansible playbook
ansible命令每次只能执行一个任务,这种运行方式称为Ad-hoc(点对点模式),不考虑Ansible特性的话,这功能比ssh远程执行命令还要弱。

所l以,Ansible靠ansible命令是撑不起自动化管理这把大伞的,Ansible真正强大的是playbook,它才是Ansible撬动自动化管理的结实杠杆。
1.1 playbook、play、和task的关系
在前面介绍inventory的时候,我将它类比为演员表,在这里,我继续对playbook、play和task跟拍电影中的一些过程做个对比。

playbook译为剧本,就像电影、电视剧的剧本一样,剧本中记录了电影的每一片段应该怎么拍,包括:拍之前场景布置、拍之后的清场、每一个演员说什么话做什么动作、每一个演员穿什么样的衣服,等等。

Ansible的playbook也如此,电影的每一个片段可以对应于playbook中的每一个play ,每一个play都可以有多个任务(tasks),tasks可以对应于电影片段中的每一幕。所以,playbook可以用来组织多个任务,然后让这些任务统一执行,就像shell脚本组织多个命令一样,这种组织多个事件、多个任务的行为,有一个更高大上的术语:“编排”。

还可以继续更细致的对应起来。比如每一个play都可以定义自己的环境,比如play级别的变量,对应于电影片段的场景布置,每一个play都需要指定要执行该play的主机,即先确定好这个电影片段中涉及的演员,每一个Play可以有pre_tasks,对应于正式开拍之前的布置,每一个play可以有post_tasks,对应于拍完之后的清场。

而我们人,既是编写playbook的编剧,也是让playbook跑起来的导演。

简单总结一下playbook、play和task的关系:
1.playbook中可以定义一个或多个play
2每个play中可以定义一个或多个task
3.每个play都需要通过hosts指令指定要执行该play的目标主机
4.每个play都可以设置一些该play的环境控制行为,比如定义play级别的变量
-其中还可以定义两类特殊的task: pre_tasks和post_tasks
- pre_tasks表示执行执行普通任务之前执行的任务列表
- post_tasks表示普通任务执行完之后执行的任务列表

下面举个例子,比如以下playbook示例,我们先不用管这个是什么意思,我们只要从缩进或者名称上可以大致看懂这个playbook里包含两个play:“play1"和"play2”,每个play中又包含了两个task。且play1和play2执行的分别是nginx和apache里的节点就够了

cat first.yml
---
- name: play 1
  hosts: nginx
  gather_facts: false
  tasks:
    - name: task1 in play1
      debug:
         msg: "output task1 in play1"
    - name: task2 in play1
      debug:
         msg: "output task2 in play1"
- name: play 2
  hosts: apache
  gather_facts: false
  tasks:
    - name: task1 in play2
      debug:
         msg: "output task1 in play2"
    - name: task2 in play2
      debug:
         msg: "output task2 in play2"

使用ansible-playbook命令执行这个yml文件

ansible-playbook first.yml

输出结果:

ansible-playbook first.yml

PLAY [play 1] *****************************************************************************************************************************************************************************

TASK [task1 in play1] *********************************************************************************************************************************************************************
ok: [192.168.6.224] => {
    "msg": "output task1 in play1"
}
ok: [192.168.7.49] => {
    "msg": "output task1 in play1"
}

TASK [task2 in play1] *********************************************************************************************************************************************************************
ok: [192.168.6.224] => {
    "msg": "output task2 in play1"
}
ok: [192.168.7.49] => {
    "msg": "output task2 in play1"
}

PLAY [play 2] *****************************************************************************************************************************************************************************

TASK [task1 in play2] *********************************************************************************************************************************************************************
ok: [192.168.6.224] => {
    "msg": "output task1 in play2"
}
ok: [192.168.7.49] => {
    "msg": "output task1 in play2"
}

TASK [task2 in play2] *********************************************************************************************************************************************************************
ok: [192.168.6.224] => {
    "msg": "output task2 in play2"
}
ok: [192.168.7.49] => {
    "msg": "output task2 in play2"
}

PLAY RECAP ********************************************************************************************************************************************************************************
192.168.6.224              : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.7.49               : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

我们初步解析以上输出内容:
首先执行的是playbook中的 “play 1” , nginx主机组(有2个节点)要执行这个play,且这个play中有两个任务要执行,所以输出结果为:

PLAY [play 1] *****************************************************************************************************************************************************************************

TASK [task1 in play1] *********************************************************************************************************************************************************************
ok: [192.168.6.224] => {
    "msg": "output task1 in play1"
}
ok: [192.168.7.49] => {
    "msg": "output task1 in play1"
}

TASK [task2 in play1] *********************************************************************************************************************************************************************
ok: [192.168.6.224] => {
    "msg": "output task2 in play1"
}
ok: [192.168.7.49] => {
    "msg": "output task2 in play1"
}


其中ok表示任务执行成功,且PLAY和TASK后面都指明了play的名称、task的名称。

执行完"play 1"之后,执行“play 2" , apache主机组(有2个节点)要执行这个play,且这个play中有两个任务要执行,所以输出的输出结果和上面的类似。

最后输出的是每个主机执行任务的状态统计,比如某个主机节点执行成功的任务有几个,失败的有几个。

PLAY RECAP ********************************************************************************************************************************************************************************
192.168.6.224              : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.7.49               : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

好了,到这里我们就该学playbook的写法了
1.2 playbook的语法:YAML
ansible的playbook采用yaml语法,它以非常简洁的方式实现了json格式的事件描述。yaml之于json就像markdown之于html一样,极度简化了json的书写。

YAML文件后缀通常为.yaml或.yml 。

YAML的基本语法规则如下:(1).使用缩进表示层级关系
(2).缩进时不允许使用Tab键,只允许使用空格
(3).缩进的空格数目不重要,只要相同层级的元素左对齐即可(4).yaml文件以"—"作为文档的开始,以表明这是一个yaml文件
(5).#表示注释,从这个字符一直到行尾,都会被解析器忽略
(6).字符串不用加引号,但在可能产生歧义时,需加引号(单双引号皆可),比如引用变量时
(7).布尔值非常灵活,不分区大小写的true/false、yes/no、onloff、y/n、0和1都允许

YAML支持三种数据结构:
(1).对象: key/value格式,也称为哈希结构、字典结构或关联数组
(2).数组:也称为列表
(3).标量(scalars):单个值

1.2.1 对象
一组键值对,使用冒号隔开key和value。冒号后面至少一个空格

name: zhangshao

等价于json:

{
"name": "zhangshao"
}

1.2.2 数组

---
- shell
- perl
- python

等价于json:

["shell","perl","python"]
---
- zhang1: shell
- zhang2: prel
- zhang3: python

等价于json:

[
  {"zhang1": "shell"},
  {"zhang2": "perl"},
  {"zhang3": "python"}
]

将对象与数组混合:

zhangshao:
- shell
- prel
- python

等价于json:

{
  "zhangshao": ["shell","perl",python"]
}

1.2.3 字典

---
person1:
  name: zhangshao
  age: 22
  agent: male
person2:
  name: rangmengxiangfei
  age: 19
  gender: female

等价于json:

{
  "person2": {
    "gender": "female",
    "age": 19,
    "name": "rangmengxiangfei"
    },
  "person1": {
    "gender": "male",
    "age": 22,
    "name": "zhangshao"
    }
}

1.2.4 复合结构

---
- person1:
  name: zhangshao
  age: 22
  langs:
     - perl
     - puby
     - shell

- person2
  name: rangmengxiangfei
  age: 19
  langs:
    - python
    - javascript

等价于json:

[
  {
    "langs": [
      "Perl",
      "Ruby",
      "Shell"
      ],
      "person1": null,
      "age": 22,
      "name" : "zhangshao"
      },
      {
      "person2": null,
      "age": 19,
      "langs": [
      "Python",
      "Javascript"
      ]"name": "rangmengxiangfei"}

1.2.5 字符串续行
字符串可以写成多行,从第二行开始,必须至少有一个单空格缩进。换行符会被转为空格。

srt: hell
  world
  hello world

也可以使用>换行,它类似于上面的多层缩进写法。此外,还可以使用|在换行时保留换行符。

this: |
  foo
  bar
that: >
  foo
  bar
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值