[Ansible]-04 ansible总结

一、环境介绍与准备

1、环境介绍

5台RHEL6.8主机,192.168.100.101-105,其中101是ansible服务器,102-105是受管机。主机名分别是:

www.101.com、www.102.com、www.103.com、www.104.com、www.105.com。

2、身份认证环境准备

ssh秘钥认证环境配置(使得101能够免密ssh登录各个受管机),在101上以root身份运行:

ssh-keygen   #在/root/.ssh目录下生成一个公钥文件和一个私钥文件
ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.100.102   #需要先输入102的root密码,然后就把101的公钥传给了102,这样101就可以使用ssh root@192.168.100.102来免密登录102了。
同理对103、104、105也如法炮制:
ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.100.103
ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.100.104
ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.100.105

3、DNS环境准备

为了偷懒,这里不再专门搭建DNS服务器,仅在101上面写本地hosts。

二、安装ansible及pip环境

1、配置yum源

操作系统是RHEL6.8,阿里yum源配置内容如下:

[aliBase]
name=aliBase
baseurl=https://mirrors.aliyun.com/centos/6/os/x86_64/
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/centos/6/os/x86_64/RPM-GPG-KEY-CentOS-6
配置完后,执行yum clean all && yum makecache

2、下载阿里的epel源

wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo

3、安装ansible

yum -y install ansible

验证:

4、安装pip及passlib

yum -y install python-pip
pip install passlib

说明:其实这步并不是现在必须要做的,只是由于ansible是基于Python环境运行的,而pip是Python包管理工具。后面在playbook中对明文密码进行哈希的时候会需要一个叫passlib的Python库,这个库就需要pip来安装,因此,现在提前先安装好,免得以后麻烦。

三、ansible目录结构

1、ansible配置文件路径:/etc/ansible/

其中,hosts是主机清单配置文件,ansible.cfg是ansible的主配置文件

2、ansible可执行文件路径:/usr/bin/

该目录下存放了ansible所有的可执行文件

3、ansible其他文件路径(了解即可)

库文件:/usr/lib/...
help文档:/usr/share/doc/...
man文件:/usr/share/man/...

四、ansible主配置文件

1、整体介绍

通过yum安装完ansible之后,主配置文件默认是/etc/ansible/ansible.cfg。其实ansible的主配置文件可以存放在多个地方,ansible读取配置文件的顺序依次是:当前命令执行时所在的目录 → 用户家目录下的.ansible.cfg文件 → /etc/ansible/ansible.cfg文件,先找到哪个就使用哪个的配置。ansible.cfg中做的全部配置都可以在命令行中以参数的形式传入或者在playbook中定义。

ansible安装完之后,无需做任何手动配置即可使用。因此,这里仅记录几个有可能手动修改的配置项。

2、可能需要修改的配置内容

2.1、认证方式的配置

host_key_checking = False
这个配置项用来设置是否启用密钥认证,如果设置成false,则不使用密钥认证,使用密码认证;如果设置成true,则使用密钥认证,不使用密码认证。默认是true,即使用密钥认证。

2.2、有关sudo的配置

[privilege_escalation]   
#become=True   #是否启用sudo
#become_method=sudo   #sudo的方式
#become_user=root   #sudo成root
#become_ask_pass=False   #sudo后是否验证密码
### 2.3、有关自定义模块存放路径的配置
#library        = /usr/share/my_modules/
复制这行并去掉注释,我们就可以设置自定义模块的路径了

五、主机清单

1、主机清单配置文件路径

/etc/ansible/hosts

2、主机清单配置示例

2.1、如果是密码认证,则(加粗斜体仅表示非必需):

[A-group]
192.168.100.[102:104] ansible_port=22 ansible_user=root ansible_ssh_pass=wwchy239
[B-group]
test105 ansible_host=www.105.com ansible_port=22 ansible_user=root ansible_ssh_pass=wwchy239
[ALL:children]
A-group
B-group
#A-group、B-group、ALL是组名,即102-104是一组,105自己是一组,children关键字表示ALL组包含了A-group组和B-group组; test105是受管机别名;ansible_host表示受管机地址(当使用别名时ansible_host不可省略);ansible_port表示访问受管机的端口;ansible_user表示使用的受管机的账户名;ansible_ssh_pass表示使用的受管机的密码。

2.2、如果是秘钥认证,则可省略账户与密码:

[A-group]
192.168.100.[102:104] ansible_port=22
[B-group]
test105 ansible_host= www.105.com ansible_port=22
[ALL:children]
A-group
B-group

3、说明

上述hosts文件的配置采用的是INI风格,还可以采用YAML风格。但后面的playbook只能用YAML风格编写。因此,YAML的语法在这里不再展示,在后续的playbook中自然会用到。

4、注意

ansible默认使用的是秘钥认证,如果想使用密码认证,除了需要在主机清单中写入账户名、密码以外,还需要在ansible.cfg中把#host_key_checking = False前的注释符删掉,使之生效,即不再检查秘钥,从而使用密码认证。

六、ad-hoc与模块

1、简介

ad-hoc是指一次性执行的、单条的ansible命令。模块是ansible实现各种操作的代码块,ansible的所有功能都是通过调用模块实现的。模块的常用操作(q键退出查看):
查看所有模块列表:ansible-doc -l
导出所有模块的详细用法:ansible-doc -a -vvv > ./ansible.txt
查看某个模块的详细用法:ansible-doc -s 模块名 -vvv

2、ad-hoc调用模块的语法

ansible 受管机 -m 模块名 -a “参数”
-m表示调用模块;-a表示给模块输入参数;“参数”表示具体的参数,参数之间用空格隔开。如果受管机有多个,可以使用逗号或者冒号分割。(此后模块梳理的示例采用ad-hoc展示)
例1:ansible all -m ping #all表示ping所有受管机
例2:ansible 192.168.100.105 -m fetch -a "src=/etc/fstab dest=/test/ansible"
例3:ansible t2,t3 -m ping

3、文件类模块之:copy

功能:把服务器上的文件复制到受管机上。

src参数:指定要复制的源文件
dest参数(必须):指定复制的目标目录
content参数:指定复制的文件内容。src和content二者必有其一,否则报错;如果使用content参数,则dest参数必须是文件而不能是目录。
backup参数:同名不同内容的文件是否备份。可选值为yes、no,默认为no。
owner、group、mode参数:分别指定复制后文件的属主、属组、权限。

例1:ansible 192.168.100.102 -m copy -a “src=/root/a.txt dest=/opt/ backup=yes owner=ww group=ww mode=0644”

# 把服务器上的/root/a.txt复制到192.168.100.102的/opt/目录下,同名不同内容则先备份后复制,复制过去的文件的属主、属组都是ww,权限是rw-r--r--。

例2:ansible 192.168.100.102 -m copy -a ‘content=”aaa\nbbb\n” dest=/opt/test’

# 在192.168.100.102的/opt目录下创建test文件,并写入两行内容:一行是aaa,一行是bbb。

4、文件类模块之:fetch

功能:把受管机上的文件复制到服务器上。

src参数(必须):指定源文件
dest参数(必须):指定目标路径,此路径如果不存在则会自动创建。
flat参数:是否仅复制文件,而不携带目录信息。可选值有yes、no,默认是no,即默认会携带目录信息一起复制过来。如果设置为yes,则dest参数值必须是已存在的目录且以“/”结尾。

例1:ansible 192.168.100.102 -m fetch -a "src=/etc/fstab dest=/test"
执行结果:

例2:ansible 192.168.100.102 -m fetch -a "src=/etc/fstab dest=/test2/ flat=yes"
执行结果:

5、文件类模块之:unarchive

功能:解压缩文件

copy参数:是否先把待解压的文件从服务端复制到受管机,然后再解压缩。可选值是yes、no,默认是yes。
src参数(必须):当copy参数是yes时,指定源文件的路径;当copy参数是no时,指定受管机上待解压缩的文件路径。
dest参数(必须):当copy参数是yes时,指定复制文件的目标路径;当copy参数是no时,指定解压缩出来的文件的路径。
owner、group、mode参数:指定解压缩出来的文件的属主、属组、权限。

6、文件类模块之:file

功能:对受管机进行文件类的操作

path参数(必须):指定要操作的文件。
state参数:指定要操作的文件的类型,或者指定操作动作是创建还是删除(删除时无需指定操作对象类型)。可选值有touch(创建普通文件)、directory(创建目录)、link(创建软连接文件)、hard(创建硬链接文件)、absent(删除)
src参数:指定软连接或硬链接的源文件。
force参数:创建链接文件时如遇同名文件是否强制覆盖,或者如果源文件不存在是否强制创建其链接文件。可选值是yes、no。
owner、group、mode参数:指定创建的文件的属主、属组、权限。
recurse参数:当被操作对象是目录时,是否递归修改目录中文件的属性。可选值是yes、no。

7、文件类模块之:template

功能:先对.j2的模板文件进行渲染(即变量替换),然后把渲染后的文件复制到受管机指定的目录下。一般多用于程序配置文件的自动化配置。

src参数:指定服务端上的源文件路径。
dest参数:指定复制到受管机上的目标路径。
backup参数:如果有重名且不同内容的文件时,是否先备份再覆盖。可选值是yes、no,默认值是no,即直接强制覆盖。
owner、group、mode参数:指定创建的文件的属主、属组、权限。

8、文件类模块之:blockinfile(预留位置,待用到时再研究)

功能:指定文件中插入一段文本

9、文件类模块之:lineinfile(预留位置,待用到时再研究)

功能:指定文件中替换、删除一行文本

10、文件类模块之:find(预留位置,待用到时再研究)

功能:查找符合条件的文件

11、文件类模块之:replace(预留位置,待用到时再研究)

功能:替换文件中被匹配到的字符串

12、命令类模块之:command

功能:在受管机上执行命令
注意1:此模块是ansible的默认调用模块,即ad-hoc中如果不用-m指定模块,则默认调用此模块;
注意2:调用此模块执行的命令中不能包含管道符、重定向符、分号、&等特殊符号,功能有局限性(而shell模块则没有任何局限性,推荐用shell模块,因此这里不再研究模块)
注意3:此模块执行的命令不会经受管机的/bin/sh程序处理,这是该模块与shell模块的不同之处。

13、命令类模块之:shell

功能:在受管机上调用/bin/sh来执行指定命令

该模块用法:ansible 受管机 -m shell -a “参数 命令” (注意:如果需要执行多个命令,则命令之间可以用分号或者&&连接)
chdir参数:先进入指定的目录中,再执行后面的命令
creates参数:如果指定的文件存在,就不执行后面的命令
removes参数:如果指定的文件不存在,就不执行后面的命令
例1:ansible test105 -m shell -a “chdir=/test ls -l;ls -l /test2 > /test/test2.txt”   #在test105上执行如下命令:先进入/test目录,再执行ls -l,再执行ls -l /test2并把ls -l /test2的执行结果重定向至/test/test2.txt
注意:在playbook中,调用shell模块时,建议用引号把执行的shell命令整个引起来。

14、命令类模块之:script

功能:在受管机上执行服务器上的脚本

该模块用法:ansible 受管机 -m script -a “参数 脚本”
chdir参数:先进入指定的目录中,再执行脚本
creates参数:如果指定的文件存在,就不执行脚本
removes参数:如果指定的文件不存在,就不执行脚本
例1:ansible test105 -m script -a “removes=/etc/fstab /test/test.sh”

15、系统类模块之:hostname

功能:配置受管机的主机名

name参数:指定受管机要配置的主机名
注意1:该模块不会修改/etc/hosts文件中的主机名解析;
注意2:批量修改主机名时最好加变量,防止所有主机名一致

16、系统类模块之:cron(预留位置,待用到时再研究)

功能:设置计划任务

17、系统类模块之:service

功能:服务管理(前提是服务要能够通过sysv、systemd等方式来管理)

name参数:指定服务名称
state参数:指定服务的状态。可选值有started、stopped、restarted、reloaded
enabled参数:是否设置为开机自启。可选值为yes、no
例1:ansible test105 -m service -a “name=nginx state=started”

18、系统类模块之:user(预留位置,待用到时再研究)

功能:用户管理

19、系统类模块之:group(预留位置,待用到时再研究)

功能:组管理

20、系统类模块之:yum_repository

功能:yum源管理

name参数(必须):指定yum源的ID,即repo文件中每个yum源对应的“中括号”中的ID。
description参数:指定yum源的名称,即repo文件中每个yum源的name字段的内容。
baseurl参数:指定yum源的baseurl内容。
file参数:指定yum源的配置文件名称,即repo文件名的前缀。如果不指定,则默认以name参数的值作为配置文件名称。
enabled参数:是否启用yum源。可选值是yes、no,默认是yes。
gpgcheck参数:是否启用rpm包验证,可选值是yes、no,默认是no。
gpgcakey参数:当gpgcheck参数为yes时,指定验证rpm包所需的公钥。
state参数:是否删除yum源,可选值是present、absent,默认是present,即不删除。
例1:ansible test105 -m yum_repository -a “file=local name=’local yum’ description=’local yum’ baseurl=file:///mnt/cdrom”

21、系统类模块之:yum

功能:使用yum源管理软件包

name参数(必须):指定软件包名称
state参数:指定软件包的状态。可选值有present(已安装)、installed(已安装)、absent(卸载)、removed(卸载)、latest(安装最新版本),默认是present。
disable_gpg_check参数:是否禁用对rpm包的公钥验证,可选值是yes、no,默认是no(即需要验证)。注意:如果yum源的gpgcheck参数为no,则该参数必须设置为yes,否则报错。
例1:ansible test105 -m yum -a “name=nginx state=installed disable_gpg_check=yes”

22、系统类模块之:setup

功能:收集系统信息

filter参数:按照关键字进行过滤,即可精确过滤也可模糊过滤。
例1:ansible test105 -m setup -a “filter=ansible_memory_mb”
例2:ansible test105 -m setup -a “filter=*eth0*”
例3:ansible test105 -m setup   #不使用filter参数,则返回所有信息
说明:运行playbook时,默认都会运行一个叫“[Gathering Facts]”的任务,这个任务的作用是收集受管机的系统信息。其实,playbook就是自动调用了setup模块来执行“[Gathering Facts]”任务的,并且把收集到的系统信息自动保存到相应的关键字变量中。因此,我们可以在ad-hoc中手动调用setup模块来获取受管机的系统信息,也可以在playbook中直接引用这些变量来获取系统信息(示例见debug模块的配图)。

测试发现的问题:
setup模块在playbook里调用没有问题,但是在ad-hoc里调用有时会有问题。比如,当使用类似“ansible_env.LANG”这种有层次的查询时,会返回空信息。截图如下:
playbook:

playbook的执行结果:

ad-hoc的执行结果:

23、系统类模块之:debug

功能:调试、回显等

msg参数:输出自定义信息(可以调用变量)
var参数:输出变量的值

示例见下图:

注意1:msg的值建议用引号引起来。
注意2:在tasks中,debug模块不可同时使用var和msg参数,因为var和msg是两个任务,应该以两个任务的方式处理。

24、系统类模块之:selinux

功能:配置受管机的selinux的状态

state参数: 指定受管机selinux的状态,可选值有enforcing、permissive、disabled。
conf参数:指定受管机selinux的配置文件
policy参数:指定受管机selinux配置文件的SELINUXTYPE

25、其他类模块之:wait_for

功能:周期性地探测当前主机或其他主机上的某个条件是否满足,一旦满足,则继续执行后面的tasks,如果直到超时都不满足,则中断playbook的执行。

host参数:指定主机,如不配置则默认为当前主机
delay参数:延迟一定时间后开始探测,默认值是0
sleep参数:连续两次探测之间的时间间隔,单位为秒,默认值是1
timeout参数:累计探测的超时时间,默认值是300
port参数:表示探测的是端口
path参数:表示探测的是文件
state参数:状态,即探测到什么结果时就停止探测。如果探测的是端口,则可选值为started、stopped,如果探测的是文件,则可选值为present、absent。默认值是started或present
search_regex参数:当探测的是文件时,使用该参数可以用正则表达式来匹配字符串,当文件存在且文件内容可以被正则表达式匹配到,则探测成功。
注意:如果探测的是端口,可以使用host参数来指定主机;如果探测的是文件,则只能探测当前主机(即使使用host指定了其他主机,也不会生效)

示例:

如图,虽然指定了hosts为t2,但是由于使用了local_action关键字,因此, wait_for模块最终是在ansible服务端执行。如果/test/ww.txt文件存在且文件内容包含有以www开头同时以www结尾的行,则探测成功,继续执行debug模块。否则,中断该剧本。

七、playbook

1、playbook简介

playbook是用YAML语法编写的剧本,他以.yaml或.yml作为文件名后缀,用于记录和执行一些操作任务。

2、playbook的语法规则

2.1、两种常见语法

YAML语法和JSON语法是记录和表达数据结构的两种常见语法,playbook使用的是YAML语法,playbook执行后的返回信息用的就是JSON语法。

2.2、YAML语法的基本特点

a、区分大小写;
b、使用缩进表示层次关系,缩进长度无限制;
c、空白字符只能用空格键,不支持tab键;
d、使用#表示注释;
e、字符串可以不用引号;
f、使用”-”+“一个空格”表示一个数组元素;
g、使用”:”+”一个空格”表示一个字典元素,即一个键值对;
h、使用”?” +”一个空格”表示一个有嵌套结构的键;(并不常用)
i、使用”|”+”缩进的文本”表示一个文本块(每行都要对齐缩进),但会自动保留文本块中的回车换行;(并不常用)
例:

执行结果:

j、使用”>”+”缩进的文本”表示一个文本块(每行都要对齐缩进),但会自动将文本块中的回车替换为空格,显示成一行;(并不常用)
例:

执行结果:

k、使用双引号、单引号也可以表示文本块,文本无需缩进,但会自动将文本块中的回车替换为空格,显示成一行;
例: 执行结果:

2.3、YAML语法的三种数据结构及两种写法

2.3.1、纯量

字符串、数字、布尔值

2.3.2、数组(又称列表)

YAML语法中的数组有两种写法:
第一种:
(YAML多行写法)
第二种:

(YAML单行写法)
这两种写法是等效的,但是第一种写法中“-”后面必须紧跟一个空格。
(附:对应的JSON语法的写法:

2.3.3、字典(又称散列表)

YAML语法中的字典有两种写法:
第一种:

(YAML多行写法)
第二种:

(YAML单行写法)
这两种写法是等效的,但是两种写法中“:”后都必须紧跟一个空格。
(附:对应的JSON语法的写法:

2.4、YAML语法的数组、字典嵌套结构举例

下图是采用YAML多行写法的一个最简单的playbook:

如果采用YAML单行写法强行拉平写成一行,等效的playbook是:

(这简直是人类无法阅读的... )

2.5、playbook的语法总结

a、playbook支持YAML语法,采用YAML多行写法、YAML单行写法都可以,但是一般都用多行写法,因为多行写法便于阅读。
b、虽然YAML语法中的数组、字典都可以等效转换成JSON写法,但是因为playbook不支持JSON语法,因此,请不要用JSON语法来写playbook。
3、playbook的文件结构

上图展示了一个最简单的playbook文件的基本结构,这个playbook文件中记录了两个play(操作场景),每个play下包含有hosts、remote_user、tasks这3个子元素(以后还会遇见vars、tags、vars_prompt、gather_facts等子元素),其中tasks下又包含了name、模块名两个子元素(以后还会遇到notify、tags、register、block等子元素)。建议稍微记一下play、tasks下面常用的子元素。
注意1:当一个play中的受管机有多个时,可以用逗号分隔写成一行,也可以分行来写。
注意2:对模块的参数赋值时,上图中的冒号可以写成等号,即“参数1=值”、“参数2=值”。另外,也可以写成一行:“模块名: 参数1=值 参数2=值”或{参数1:值,参数2:值}。
注意3:tasks后面的name和模块名无先后顺序要求

4、playbook的命令行结构

4.1、命令行结构:(加粗斜体仅表示可选内容)

ansible-playbook PATH/TO/PLAYBOOK.yaml 选项

4.2、常用选项举例

--syntax-check:语法检查
-- check或-C:模拟执行
-e:定义变量

例1:ansible-playbook PATH/TO/PLAYBOOK.yaml -e “变量1=值 变量2=值 ...”
例2:ansible-playbook PATH/TO/PLAYBOOK.yaml -e “PATH/TO/变量文件”
-i:指定主机清单路径
--list-hosts:只列出剧本中会访问到的受管机列表,并不实际执行剧本
--list-tasks:只列出剧本中会执行的tasks列表,并不实际执行剧本
--tags=”标签名”:只执行剧本中具有指定标签的task
--skip-tags=“标签名”:执行除了有指定标签的task以外的所有task
-v、-vv、-vvv:显示命令执行过程中的详细信息

5、playbook关键字之:handlers

5.1、handlers作用简介:

handlers用来完成当一个play中的tasks执行完之后的“善后处理”工作,但是只对执行状态是changed的task生效。因此,在必要的情况下,可以用changed_when来配合handlers使用,以实现“满足条件判断时,把task执行状态修改为changed,从而能够执行handlers任务”的效果。

5.2、图示详解


注意1:handlers是与tasks平级的另外一种任务列表,因此在缩进上与tasks对齐。notify是用来调用handlers的关键字,他在tasks下一级,需要缩进。
注意2:如果task执行成功且有实际改动(即执行状态是SUCCESS和changed),才可以通过notify来调用handlers任务,否则就不调用handlers任务。
注意3:handlers默认是在所有task执行完之后才执行,但是如果某个task执行完之后需要立即进行“善后处理”,则可以在这个task后面使用meta关键字强制执行handlers中的某任务。即:如果不使用-meta,则是按照playbook的书写顺序来执行,即“touch file ww1”→ “touch file ww2”→ “handler1”→ “handler2、handler3”;上图中使用了-meta,则执行顺序变为:“touch file ww1”→ “handler1” →“touch file ww2”→ “handler2、handler3”

6、playbook关键字之:tags

6.1、tags作用简介:

我们用ansible-playbook来执行yaml文件时,默认是把tasks下面的所有任务都执行。tags用来给tasks中的任务打上标签,这样我们再用ansible-playbook来执行yaml文件时,就可以利用标签来有选择性的执行某些任务了。

6.2、图示详解

ansible-playbook tags.yaml 
# 执行结果:task2、task3、task4,因为task1有内置标签never
ansible-playbook --tags=T0 tags.yaml 
# 执行结果:task1、task2、task3、task4,因为4个任务都贴有T0标签
ansible-playbook --tags=T1 tags.yaml 
# 执行结果:task1、task2、task3,因为task3有always内置标签
ansible-playbook --tags=T2 tags.yaml
# 执行结果: task2、task3,因为task3有always内置标签
ansible-playbook --tags=T3 tags.yaml 
# 执行结果: task3,因为只有task3有T3标签
ansible-playbook --tags tagged tags.yaml
# 执行结果: task2、task3、task4,因为task1有内置标签never
ansible-playbook --tags untagged tags.yaml
# 执行结果: task3,因为task3有always内置标签
ansible-playbook --skip-tags=T0 tags.yaml
# 执行结果:无,因为所有任务都被打上了T0标签

6.3总结

a、使用“--tags=标签名”来只执行具有指定标签的任务,使用“--skip-tags=标签名”来只执行没有指定标签的任务,使用“--tags tagged”来只执行打上标签的任务,使用“--tags untagged”来只执行没有标签的任务。
b、always是内置标签,表示总是执行,只有被明确使用“--skip-tags=标签名”排除在外时才不会执行(前提是该任务除了always标签以外还有其他标签);never是内置标签,表示总不执行,只有被明确使用“--tags=标签名”匹配到时才会执行(前提是该任务除了never标签以外还有其他标签)。

6.4、注意

如果标签中有空白字符,则要用双引号或单引号引起来。

7、playbook关键字之:block

7.1、介绍

block的作用是把tasks中的多个模块绑定起来作为一个整体来对待,用来一次性执行多个操作,所以,block的层次位置应该与模块对齐。
block常与条件判断和异常捕获配合使用,但不支持循环,不能循环地将一个block执行多次。

7.2、图示用法


上图,在主机组ALL中匹配到ip地址是192.168.100.105的受管机,然后执行block中的两个debug模块。

8、playbook关键字之:serial

8.1、介绍

serial关键字的作用是把受管机分配依次执行所在的play,因此,serial关键字必须写在play下。serial多用于服务器集群不能同时操作的场合,比如,对负载均衡后面的应用服务器进行升级,如果全部服务器同时升级则会造成服务不可用。

8.2、图示用法

执行这个playbook之前,先对/test文件夹进行监视:

执行效果:/test目录下的3个文件依次创建(如果把serial所在行注释掉,3个文件则会同时创建)

9、playbook关键字之:local_action

9.1、介绍

local_action关键字的作用是设置某个task要在服务端执行,因此,local_action关键字的缩进要与tasks下面的模块名对齐。

9.2、图示用法

执行结果:(在服务端创建了3个文件)

10、playbook关键字之:run_once

10.1、介绍

run_once关键字的作用是设置所在task只执行一次,该关键字只能配合local_action关键字一起使用,以避免有多个受管机时,服务端的task执行多次。

10.2、图示用法


这是一个完整的playbook,作用是配置ansible服务端与对个受管机的ssh秘钥认证。第一个task的作用:如果服务端的私钥文件不存在,则用ssh-keygen命令创建私钥和公钥文件。但是,受管机有多台,这将造成ssh-keygen命令重复执行多次,将会询问是否覆盖私钥文件,这会使playbook执行失败。因此,ssh-keygen命令必须只能执行一次。run_once关键字实现了这个要求。

11、playbook关键字之:delegate_to

11.1、介绍

当我们的某些task不能当前受管机上执行,而是要在其他受管机上执行时,可以使用delegate_to关键字来实现。

11.2、图示用法

上图虽然是对t2主机执行的,但是由于delegate_to指向了t7,因此,file模块的任务将会在t7上执行。
如果写成delegate_to: 127.0.0.1则等效于local_action。

12、playbook关键字之:include

12.1、介绍

当项目规模较大时,playbook也将会写的很臃肿,不便于阅读和维护。ansible借鉴了apache、nginx等开源软件的配置文件的组织方式,允许使用include来把一个庞大的playbook文件分散成多个较小的文件,文件之间可以使用include关键字来相互调用。
include调用的对象可以是tasks、handlers或者整个playbook,分别举例介绍如下。

12.2、include关键字调用tasks

第1步:我们把需要调用的tasks单独写在一个文件中(比如是tasks.yaml)

第2步:在playbook中(比如是a.yaml)使用include来调用这些tasks

12.3、include关键字调用handlers

第1步:我们把需要调用的handlers单独写在一个文件中(比如是handlers.yaml)

第2步:在playbook中(比如是a.yaml)使用include来调用这些handlers

12.4、include关键字调用playbook

第1步:先写好待调用的playbook(比如是play.yaml)

第2步:在playbook中(比如是a.yaml)使用include来调用这个playbook

注意:在ansible 2.8以前的版本中,调用其他playbook文件既可以使用include关键字,还可以使用import_playbook关键字。但是,在ansible 2.8以后的版本中,调用其他playbook文件则必须要使用import_playbook关键字(取代include关键字)。 

13、playbook语法点之:变量

13.1、简介

变量可以由字母、数字、下划线组成,必须以字母开头,且不能以ansible内置的关键字作为变量名。

13.2、变量的定义与引用

13.2.1、vars关键字定义变量


即在剧本的play中用vars关键字定义的变量。上图的执行结果是:在test105主机的/test目录下创建了123123、aaabbb两个文件。
注意1:引用变量时,如果变量位于“开头位置”,即变量左侧是空格时,需要用双引号引起来,在其他位置不需要。
注意2:vars关键字定义的变量的作用域是“定play”(即vars关键字定义的变量只能在同一play中被引用)

13.2.2、命令行定义变量

在运行playbook文件或运行ad-hoc命令时,命令行中使用-e参数也可以定义变量,多个变量之间用空格隔开。
例1:ansible-playbook vars.yaml -e ‘var1=abc var2=def’
例2:ansible test105 -m shell -a “echo {{ww}}” -e “ww=‘I love Y’”
注意1:如果变量值包含空白字符,则必须用引号引起来。
注意2:命令行定义变量的优先级高于剧本定义变量,即如果命令行和剧本同时定义了同一个变量,则以命令行定义的为准。
注意3:命令行定义变量的作用范围属“全剧本自由访问”(即该剧本中的所有play都可以自由访问命令行定义的变量)

13.2.3、文件定义变量(又称变量分离)

第一步:先创建一个文件来定义变量:

第二步:引用文件中的变量
方式1:在playbook文件中使用vars_files关键字引用文件的变量:

方式2:在命令行引用文件中的变量
例1:ansible-playbook test.yaml -e “@/test/var.yaml”
注意1:如果变量分离使用了多个文件来定义变量,则在使用vars_files关键字引用变量时,每个文件都要以“-”开头。

注意2:文件定义的变量的作用范围比较自由,只有使用vars_files关键字引用文件的play才有权访问其变量。

13.2.4、主机清单定义变量

13.2.4.1、图示定义方法:

如图,给test105主机配置了两个变量:var1和var2,给主机组ALL配置了两个变量var3和var4。

13.2.4.2、图示调用方法:

如图,访问test104主机时,可以直接引用test104的变量var3,但是如果想要跨主机引用test105的变量var1,就需要使用hostvars内置变量了。因此,主机清单定义的变量的作用域是“跨play跨主机”(即主机清单定义的变量在剧本中的每个play都可以访问,但是跨主机需要使用hostvars内置变量)

13.2.5、set_fact模块定义变量

13.2.5.1、图示用法

如上图,在第一个play中定义了两个变量,一个在play中定义,一个在tasks中定义。在第二个play中分别引用和显示了这两个变量。
第二个play执行结果是:var_in_tasks变量可以正常显示,var_in_play变量显示时报错。

13.2.5.2、set_fact总结

a、set_fact模块能够实现在tasks中定义变量;
b、set_fact模块能够实现把一个变量的值完整地赋值给另一个变量;
c、set_fact模块定义的变量的作用域是“跨play跨主机”(即set_fact模块定义的变量在同一个剧本后面的play访问其他受管机时依然可以访问到,但需要使用hostvars内置变量,用法同前面的“主机清单变量”)

13.2.6、注册变量

13.2.6.1、需求引出

默认情况下,playbook中的模块运行后返回值并不显示。如果我们需要显示,就可以使用注册变量的方法。用register关键字把返回值保存到变量中,再用debug模块显示该变量的值即可。

13.2.6.2、图示详解

该playbook的“show var”task的执行结果:

13.2.6.3、注册变量总结

a、变量注册的作用不仅仅是显示task的执行结果,更重要的是可以由task的执行结果来决定后续的一些动作。因此,注册变量还是会被经常用到的。
b、注册变量的作用域是“跨play跨主机”(即注册变量在同一个剧本后面的play访问其他受管机时依然可以访问到,但需要使用hostvars内置变量,用法同前面的“主机清单变量”)

13.2.7、roles定义变量

在剧本中,使用roles关键字调用指定的角色时,可以同时给这些角色定义变量。

roles关键字调用了两个角色:common和nginx_servers,同时又给nginx_servers角色定义了一个变量:port,值是8080。
13.2.8、总结:变量的定义和引用

a、定义变量,大致分为3类:剧本内定义变量(vars关键字定义、set_fact模块定义、register注册变量、roles定义)、命令行定义变量、文件定义变量、主机清单定义变量。vars关键字定义变量作用范围最小,属“定play”;其他几种方式的作用范围都是整个剧本,但有所不同:其中主机清单定义变量、注册变量、set_fact模块定义变量在跨主机访问时需要使用hostvars内置变量,文件定义变量需要在play中使用vars_files关键字来引用变量文件,roles定义的变量只对某些特定的rose生效,命令行定义的变量的作用范围最大,属“全剧本自由访问”
b、变量引用,大致分为2类:普通变量的引用和高阶变量的引用。普通变量是指变量的值是一个单个的数字、字符串或布尔值,而高阶变量是指变量的值是数组、字典或其嵌套结构。普通变量的引用方式:{{变量名}},数组变量的引用方式:变量名[索引][索引]...,字典变量的引用方式:变量名.键名.键名...
例1:

执行结果:B

例2:

执行结果:95

例3:

执行结果:95

13.3、常见内置变量

13.3.1、facts变量

facts变量是一类变量的统称。在执行tasks的时候,默认执行的[Gathering Facts]任务收集到的受管机信息会自动保存在一些变量中,如ansible_memory_mb、ansible_processor等,这些变量就是facts变量。
facts变量必须在执行完[Gathering Facts]任务后才会有,所以,如果在play中把gather_facts设置成no,则facts变量就不存在了。另外,ad-hoc命令由于不会执行Gathering Facts任务,因此也不能访问facts变量,除非手动调用setup模块。
常用的facts内置变量:
主机名:ansible_fqdn
内存信息:ansible_memory_mb
cpu信息:ansible_processor
cpu物理核心数 :ansible_processor_cores
默认使用的ip地址:ansible_default_ipv4.address
所有ip地址:ansible_all_ipv4_addresses
eth0网卡的ip地址:ansible_eth0.ipv4.address
eth0网卡的mac地址:ansible_eth0.macaddress
所有的网卡名称:ansible_interfaces
dns信息:ansible_dns
操作系统版本名称:ansible_distribution #例:REDHAT
操作系统版本号:ansible_distribution_version #例:6.8
操作系统主版本号:ansible_distribution_major_version #例:6
内核版本:ansible_kernel #例:2.6.32-642.el6.x86_64
环境变量:ansible_env

13.3.2、hostvars

简介:非常重要的一个内置变量,他用来访问其他受管机的facts变量、set_fact变量、清单变量、注册变量。即hostvars是专门用来跨主机访问变量的。
用法:hostvars.受管机.变量名.子变量名...
图示用法:

13.3.3、ansible_version

作用:获取ansible的版本号

13.3.4、inventory_hostname

作用:获取当前受管机在主机清单中的主机别名(如无别名则获取ip地址)

13.3.5、play_hosts

作用:获取当前play访问的所有主机的主机别名列表(如无别名则获取ip地址),即play中hosts的内容。play每访问一个主机,就获取一次。

13.3.6、groups

作用:获取主机清单中所有主机的分组信息,无分组的主机自动标记为ungrouped。

13.3.7、group_names

作用:获取当前受管机所在主机分组的组名。无分组的主机自动返回ungrouped。

13.3.8、inventory_dir

作用:获取主机清单配置文件存放的路径

13.3.9、true、false

作用:分别表示逻辑“是”、逻辑“否”。
注:也可写成True、False或TRUE、FALSE。

14、playbook语法点之:人机交互

14.1、需求引出

在运行脚本时,有时候需要根据用户的输入信息来决定下一步动作,这就需要人机交互。人机交互的基本做法是,提示用户输入信息,然后把用户输入的信息保存在变量中,需要时引用该变量即可。

14.2、图示详解

14.3、注意

encrypt关键字用来把用户输入的密码进行哈希运算,这需要用pip安装一个叫passlib的python库,否则会报错。(幸好文档开始的时候已经安装好了)

15、playbook语法点之:循环

15.1、循环关键字之:with_items

15.1.1、介绍

功能:递归遍历数组,把每一个元素循环地赋值给item变量,每循环一次,就执行一次所在的task。
用法:with_items: 数组对象

15.1.2、图示

示例1:直接给出一个数组

执行结果:

示例2:给出一个数组类型的内置变量

执行结果:

15.1.3、注意

注意1:with_items必须要与所在的task中的模块名对齐,而不能与tasks关键字对齐。
注意2:task中的模块经with_items循环执行后,执行结果会保存在results变量中,再利用with_items循环依次访问results中对应的元素即可。例:

注意3:如果数组对象里嵌套有数组,with_items会递归遍历所有数组元素。例:

执行结果:

15.2、循环关键字之:with_flattened

作用与with_items完全相同

15.3、循环关键字之:with_list

15.3.1、介绍

功能:遍历数组,循环地把数组第一层的每个子元素作为整体赋值给item变量,每循环一次,就执行一次所在的task。
用法:with_list: 数组对象

15.3.2、图示

执行结果:

15.3.3、with_list总结

在处理单层数组时,with_list和with_items没有区别,在处理嵌套的多层数组时,with_items会把每个数组(包括其子数组)“拉平展开”循环的处理每个元素。而with_list只处理最外层数组。

15.4、循环关键字之:with_together

15.4.1、介绍

功能:循环地将两个数组的元素对齐合并,并将合并后的新数组赋值给item变量
用法:with_together [数组1,数组2]
注意:如果两个数组元素个数不等,则空缺位置为null

15.4.2、图示


执行结果:

15.5、循环关键字之:with_nested

15.5.1、介绍

功能:循环地将两个数组的元素交叉合并,并将合并后的新数组赋值给item变量
用法:with_nested [数组1,数组2]

15.5.2、图示

执行结果:

15.6、循环关键字之:with_sequence

15.6.1、介绍

功能:生成一个整数列表,并循环地把整数列表的元素或者format的结果赋值给item,每循环一次就执行一次所在task。
用法1:with_sequence: start=起始值 end=结束值 stride=步长
用法2:with_sequence: count=整数值
用法3:with_sequence: start=起始值 end=结束值 stride=步长 format=”自定义的格式”
注意:start、end、stride三个参数赋值必须用等号,不能用冒号。

15.6.2、图示

执行结果:

执行结果:

执行结果:

执行结果:

15.7、循环关键字之:with_random_choice

15.7.1、介绍

功能:在数组中随机选择一个元素,把这个元素赋值给item,并执行一次所在task。
用法1:with_ random_choice: 数组对象
注意:with_random_choice只循环一次,执行一次task。

15.8、循环关键字之:with_dict

15.8.1、介绍

功能:循环地将字典的键值对赋值给item,每循环一次执行一次所在task
用法:with_dict: 字典对象
注意:把键值对赋值给item后,键自动赋值给item.key,值自动赋值给item.value

15.8.2、图示

执行结果:

15.9、循环关键字之:with_file

15.9.1、介绍

功能:用于处理ansible服务器上多个文件组成的数组,循环地获取每个文件的内容并赋值给item,每循环一次就执行一次所在task。
用法:with_file: [文件1,文件2,...]

15.9.2、图示

执行结果:

15.9.3、特别注意

a、with_file是用来在受管机上循环的获取ansible服务器上的文件内容。
b、文件1、文件2... 必须是某个确定的文件,不可以使用文件名通配。

15.10、循环关键字之:with_fileglob

15.10.1、介绍

功能:用于在ansible服务器上匹配符合指定条件的文件,并循环地把匹配到的每个文件的完整路径赋值给item,然后执行一次所在task。
用法:with_fileglob [匹配范围1,匹配范围2,...]
注意:with_fileglob是用来在受管机上循环的获取ansible服务器上的文件路径。

15.10.2、图示

执行结果:

如图,一共匹配到了3个文件。

15.11、循环关键字之:with_lines

15.11.1、介绍

循环地把ansible服务器上的文件的每一行内容赋值给item,然后执行一次所在task。需要借助于cat等shell命令来获取文件内容。

15.11.2、图示


执行结果:

16、playbook语法点之:条件判断

16.1、运算符

比较运算符:

==、!=  (比较两个对象是否相等)
>、<、>=、<=  (比较两个值的大小)

逻辑运算符:

and  (逻辑与)
or  (逻辑或)
not  (逻辑非,即取反)

组合运算符:

()  (组合,即将一组操作作为一个整体)

16.2、核心关键字:when

功能:如果满足指定条件,就执行所在task或block。
运行机制:如果是在服务端进行条件判断(比如:路径判断),则条件判断只执行1次,如果执行结果是True,就把所在的task或block同时或分批交给受管机去执行(每个受管机执行task或block之前不再进行条件判断);如果是在各个受管机上进行条件判断,则把所在的task或block同时或分批交给受管机,每个受管机先执行条件判断,再根据条件判断结果来决定是否执行此task或block。
注意1:when后面的判断条件中的变量可以不加{{}}。
注意2:when后面的判断条件中的字符串要使用引号引起来,如果判断动作是is、is not、in、not in时,when后面整个判断条件也要用引号引起来。

注意3:when关键字如果与模块名对齐,则满足条件时执行模块的操作;when关键字如果与block对齐,则满足条件时执行block的操作。
注意4:“逻辑与”不仅可以用and实现,还可以用数组结构来实现。因此,下面两个图的playbook是等效的。

16.3、判断动作关键字:is、is not

功能:分别表示“是”、“不是”两个判断动作,以下的条件判断分类都要用这两个关键字来进行判断。

16.4、判断动作关键字:in、not in

功能:用于判断一个字符串是否包含在另一个字符串中,或者一个对象是否包含在一个数组中。
注意:in、not in在使用时,整个条件判断表达式需要用引号引起来。
例:

16.5、其他关键字:failed_when

功能:满足条件判断时,task执行状态会修改为“失败”,以退出playbook的执行。(相当于自定义了一个异常)
注意:failed_when只是强制把task执行状态修改成了失败,实际的task不一定真的失败。
用法:failed_when: 条件判断表达式

16.6、其他关键字:changed_when

功能:满足条件判断时,task执行状态会修改为“成功”。
注意:failed_when只是强制把task执行状态修改成了成功,实际的task不一定真的成功。

16.7、条件判断分类之:路径(或文件)判断

exists:判断是否存在
file:判断是否是文件
directory:判断是否是目录
link:判断是否是软连接
mount:判断是否是挂载点
特别注意:上述5个路径判断是用于判断ansible服务器上的路径是否存在!

16.8、条件判断分类之:字符串判断

string:判断一个对象是否是字符串
lower:字符串是否全都小写
upper:字符串是否全都大写

16.9、条件判断分类之:数字判断

number:判断一个对象是否是数字
even:判断数值是否是偶数
odd:判断数值是否是奇数
divisibleby():判断是否能被指定的数字整除

16.10、条件判断分类之:变量判断

defined:判断变量是否存在
undefined:判断变量是否不存在
none:判断是否变量已存在且值为空

16.11、条件判断分类之:task执行状态判断

success或succeeded:task是否执行成功
failure或failed:task是否执行失败
change或changed:task执行状态返回信息是否为“changed”
skip或skipped:task是否被跳过执行
注意:这里要对task的执行状态进行判断,一般会配合register来实现,先把task的执行结果通过register关键字赋值给一个变量(比如是var1),再使用条件判断语句:when: “var1 is failed”,来实现对task执行状态的判断。

16.12、条件判断分类之:数组关系判断

subset():判断一个数组是否是另一个数组的子集
superset():判断一个数组是否是另一个数组的父集

16.13、条件判断分类之:版本号判断

version():判断两个版本号的大小关系
用法:版本号1 is version(版本号2,”比较运算符”)
例:1.0.17 is version(1.0.15,”>”),执行结果:True

17、playbook语法点之:异常

17.1、异常的忽略与自定义触发

忽略异常:ignore_errors,用法是“ignore_errors: yes/no”
自定义触发异常:failed_when,用法是“failed_when: 条件判断表达式”

17.2、异常的处理

17.2.1、单模块的异常处理

方式1:先用register获取模块执行结果,再用when配合register.rc来判断模块的执行状态。
方式2:把单个模块放在block中,用rescue来实现异常捕获。(推进使用)

17.2.2、多模块的异常处理

用法:- block: ... rescue: ... always:
功能:block中任何一个模块执行失败(包括failed_when)都会触发rescue,跳过block中剩余未执行的模块转而顺序执行rescue中的每一个模块,而不论block和rescue中的模块执行成功与否,always中的模块是必然要执行的。
注意1:rescue关键字必须与block关键字对齐,不能与单个模块名对齐。
注意2:当对block使用rescue时,如果block中的模块又使用了ignore_errors: yes,则rescue会失效。
图示详解:

(执行过程:先执行block中的shell模块,执行失败,跳过block中的debug模块,开始执行rescue的第一个debug模块,然后执行rescue的shell模块,执行失败,跳过rescue的第二个debug模块,执行always中的debug模块。)

18、playbook语法点之:过滤器

18.1、简介

过滤器能够按照一定规则把对象数据进行筛选、格式化等处理,以输出我们想要的结果。使用格式:{{数据对象 | 过滤器1 | 过滤器2 ...}}
注意1:{{}}、|都不能省略。(|的作用类似于管道)
注意2:数据对象如果是变量、数字,直接写变量名、数字即可;数据对象如果是字符串,则要加引号,如 {{“abc” | upper}}

18.2、字符串过滤器

{{“abc” | upper}} → “ABC”   #把字符串全部转换成大写
{{“aBc” | lower}} → “abc”   #把字符串全部转换成小写
{{“abc” | capitalize}} → “Abc”   #仅把字符串首字母大写
{{“abc” | replace(‘b’,’T’)}} → “aTc”   #把b替换成T
{{'string' | replace('tr','')}} → “sing”   #把tr替换成空,即删掉tr
{{'string' | regex_replace('(.*)tr(.*)$','\\2-\\1')}} → "ing-s"   #使用正则表达式进行替换。解释:第一个(.*)表示tr前面的所有字符,第二个(.*)表示tr后面的所有字符,\\1和\\2分别引用了第一个(.*)和第二个(.*)的内容(两个\是因为\本身需要做转义)。
{{“abc” | reverse}} → “cba”   #把字符串反转
{{“abc” | first}} → “a”   #返回字符串的第一个字符
{{“abc” | last}} → “c”   #返回字符串的最后一个字符
{{“abc” | length} → 3   #返回字符串的长度。同:count
{{“  abc  ” | trim}} → “abc”   #把字符串首尾连续的空格都删掉
{{“abc” | list}} → [a,b,b]   #把字符串转换成数组,一个字符是一个元素
{{“abc” | shuffle}} → [b,a,c] ...   #把字符串转换成数组,一个字符是一个元素,顺序随机

18.3、数字过滤器

{{“a” | int}} → 0   #转换成整数,如不能转换则默认返回0
{{“a” | int(6)}} → 6   #转换成整数,如不能转换则返回6
{{“a” | float}} → 0.0   #转换成浮点数,如不能转换则返回0.0
{{“a” | float(2.5)}} → 2.5   #转换成整数,如不能转换则返回2.5
{{-5 | abs}} → 5   #返回其绝对值
{{1.2345 | round}} → 1.0   #四舍五入成整数,但保留小数点后1位固定为0
{{1.2345 | round(2)}} → 1.23   #四舍五入到小数点后2位
{{100 | random}} → 6 ...   #从0至100中随机产生一个整数
{{15 | random(start=5,step=3)}} → 8...   #从5开始,步长为3,至15的整数中随机选择一个,同:{{15 | random(5, 3)}}。(如不指定,则start默认为0,step默认为1)

18.4、数组过滤器

{{ [1,2,3] | length }} → 3   #返回数组长度,即元素个数。同count
{{ [1,2,3] | first }} → 1   #返回数组第一个元素
{{ [1,2,3] | last }} → 3   #返回数组最后一个元素
{{ [1,2,3] | min }} → 1   #返回数组中的最小值
{{ [1,2,3] | max }} → 3   #返回数组中的最大值
{{ [1,2,3] | sort }} → [1,2,3]   #将数组升序排序
{{ [1,2,3] | sort(reverse=true) }} → [3,2,1]   #将数组降序排序
{{ [1,2,3] | sum }} → 6   #返回纯数字非嵌套数组各元素之和
{{ [1,2,[‘a’,’b’,[3,4]]] | flatten }} → [1,2,‘a’,’b’,3,4]   #将所有嵌套的数组都拉平
{{ [1,2,[‘a’,’b’,[3,4]]] | flatten(levels=1) }} → [1,2,‘a’,’b’,[3,4]]   #只拉平第一层嵌套的数组
{{ [1,2,3] | join }} → ‘123’   #将数组的各元素合并成一个字符串
{{ [1,2,3] | join(‘-’) }} → ‘1-2-3’   #将数组各元素以指定字符连接成一个字符串
{{ [1,2,3] | random }} → 3 ...   #随机返回数组中的一个元素。对数组使用random过滤器的时候不可以使用start、step参数。
{{ [1,2,3] | shuffle }} → [2,1,3] ...   #随机打乱数组中的各元素
{{ [1,2,3,2,1] | unique }} → [1,2,3]   #数组去重
{{ ‘a’,’b’,3 | upper }} → [‘A’,’B’,3]   #把数组各元素都大写
{{ ‘A’,’B’,3 | lower }} → [‘a’,’b’,3]   #把数组各元素都小写

18.5、未定义变量过滤器

{{ var1 | d(‘ww’) }}   #var1变量如果有值(包括空值),就返回var1的值;var1变量如果未定义,则返回’ww’。同:default
{{ var1 | d(‘ww’,boolean=true) }}   #var1变量如果有非空值,就返回var1的值;var1变量如果未定义或者值为空,则返回’ww’。 同:default

18.6、注册变量过滤器

需求场景:当task执行失败时,默认playbook会自动退出。如果我们使用了ignore_errors=yes,将会强制忽略失败而继续执行后面的task。但是,如果我们想要实现这样一种效果:“当task执行失败时,不立即退出playbook,而是继续完成一些操作,然后再退出”(“苟延残喘”一会儿再去死?哈哈),这又怎样才能实现呢?没错,注册变量过滤器可以实现这种“苟延残喘”的效果~

解释:由于设置了gather_facts=no,所以第一个task必然失败,之后我们使用了register、ignore_errors两个关键字再配合failed_when: ‘result|failed’才实现了任务失败后显示8秒钟“mission failed ...”再退出的效果。
注意:其实,前面在总结条件判断语法的时候,我们就知道,使用条件判断中的is关键字也完全可以实现上述效果,即最后一行也可以写成:failed_when: ‘result is failed’。而且,在ansible 2.9之后的版本中(现在使用的版本是2.6.2),注册变量过滤器的语法将被取消(这里就姑且纪念一下吧)。因此,在ansible 2.9之后的版本中,上图的最后一行应该写成:failed_when: ‘result is failed’
总结:注册变量过滤器常配合register和条件判断来实现上述“苟延残喘”的效果(难怪叫它注册变量过滤器呢)。同理,除了failed以外,它支持状态检查的关键字还有success、changed、skipped

18.7、文件路径过滤器

    {{ “/usr/local/nginx/conf/nginx.conf” | basename }} → “nginx.conf”   #返回路径中最终的文件名
{{ “/usr/local/nginx/conf/nginx.conf” | dirname }} → "/usr/local/nginx/conf"   #返回文件名前面的路径
    {{ “~/.ssh/id_rsa” | expanduser }} → "/root/.ssh/id_rsa "   #将~替换成用户目录(假设当前用户是root)
    {{ "/etc/ansible/test/www.link" | realpath }} → “/etc/fstab”   #返回软连接文件/etc/ansible/test/www.link的真实文件路径(假设提前执行了ln -s /ets/fstab /etc/ansible/test/www.link)

八、roles初步学习

1、简介

roles其实是一种基于角色的剧本组织方式。当项目规模比较大的时候,我们的playbook文件势必会写的很臃肿,阅读和维护将变得越来越艰难。include关键字的出现使得局面得到一丝缓解,但是随着项目复杂性的增加,使用include建立的相互依赖关系也变得错综复杂,局面依然很难维护。最终,roles的出现使得这一切得到了根本解决。

2、举例详解roles用法

2.1、案例介绍

这里以我自己写的一个playbook为例,详细梳理roles在playbook中的作用。案例是用ansible搭建lvs+keepalived的DR模式双主架构,此案例一共使用了7台虚拟机:1台client,2台lvs,4台nginx。其中,4台nginx两两分组,2台lvs互为主备。

2.2、roles目录结构展示(下图只显示目录)

roles目录:ansible自带的目录,用来存放角色文件
lvs1目录:用来存放第一台lvs的角色
lvs2目录:用来存放第二台lvs的角色
nginx_servers目录:用来存放nginx服务器的角色
common目录:用来存放共用角色,即lvs1、lvs2、nginx_servers这3个角色都需要用到的功能。

2.3、目录文件详解

lvs1、lvs2、nginx_servers这3个角色目录下的文件组织方式是相同的,这里先以nginx_servers为例,详细解释nginx_servers目录下各文件的作用。
files目录:存放的是需要复制到nginx服务器上且不需要进行变量替换的文件(即文件内容是写死的),playbook中的copy模块、unarchive模块会自动访问该目录下的文件,也就是说,copy模块、unarchive模块中的src参数直接写文件名即可,无需写绝对路径。ifcfg-lo:0和ifcfg-lo:0是两个网卡配置文件,用来给nginx服务器的lo网卡绑定两个vip用的。nginx-1.14.0.tar.gz是nginx的安装包,需要复制到nginx服务器上解压缩,然后编译安装nginx。
handlers目录:存放nginx_servers这个角色所需执行的handlers任务,playbook中使用notify调用handlers任务时,会自动访问该目录下的main文件。该目录下必须要有一个main.yaml文件,如果需要的handlers任务比较多,也可以再多建立一些handlers文件,然后通过在main.yaml文件中使用include关键字来调用其他的handlers任务。main.yaml文件内容如下图:

tasks目录:存放nginx_servers这个角色所需执行的tasks任务,playbook中调用roles时会自动访问该目录下的main文件。该目录下必须要有一个main.yaml文件,可以用include来包含该目录下的其他tasks文件。该目录下各文件内容如下图:

templates目录:存放需要先经过变量替换后再复制给nginx服务器的文件(一般是程序的配置文件或网页文件),这些文件的文件名必须要以.j2结尾。playbook中的template模块会自动访问该目录下的.j2文件。.j2文件中需要替换的变量的值,都需要在vars目录下定义好。该目录下各文件内容如下图:

(nginx.conf.j2是需要变量替换后复制给nginx服务器的nginx的配置文件,里面内容较多,这里只显示了需要进行变量替换的内容)
vars目录:存放nginx_servers角色所需的所有自定义变量。template模块进行变量替换时会自动访问该目录下的main文件,可以用include包含其他的变量文件。mian.yaml文件内容如图:

最后,特别注意:files、templates、tasks、vars、handlers目录的名称必须这样写,不能随意更改。

2.4、案例的主playbook文件

前面介绍了/etc/ansible/roles目录,下面介绍主playbook文件。

如图,案例的主playbook文件是lvs_keepalived_nginx_doublemasters_DR.yaml,文件内容如下图:

该文件中,一共有4个play:
第一个play,对所有主机操作,通过调用common角色,完成了对所有主机的yum源配置、ssh秘钥认证配置;
第二个play,对4台nginx服务器操作,通过调用nginx_servers角色,完成了4台nginx服务器的两个vip绑定、nginx软件安装配置和启动;
第三个play和第四个play,分别对两台lvs服务器操作,通过调用lvs角色,完成了lvs和keepalived的软件安装配置和启动。
3、roles基本用法总结
先创建一个项目的主playbook文件,然后在roles目录下创建一些不同的角色目录,目录名随意。每个角色目录下都有各自的files、templates、tasks、vars、handlers等功能子目录,子目录名不可随意更改。在主playbook文件中通过调用不同的roles角色,来完成对不同主机的操作,从而最终完成项目任务。

九、扩展提高内容:自定义模块、自定义过滤器、自定义插件、性能优化

1、自定义模块

1.1、最简单的自定义模块举例

第1步:配置自定义模块的存放路径(编辑ansible.cfg文件)

第2步:在存放路径下,以模块名作为文件名新建文件。并用shell、python或其他语言编写文件内容:导入模块、定义参数、定义命令、定义回显信息、定义执行状态。

第3步:调用模块

..
(待续)

结语:
我花了大约一周的业余时间整理了上述内容,里面的一字一句都是经过我实际测试后记录的。上述内容只是ansible的基本知识,感觉这些应该是能够应付日常的运维工作了。关于自动化开发的相关内容,待以后需要的时候再继续深入研究吧(其实是因为我的python需要先补补课~)。
我费力气整理这个笔记的原因有两个:
1、我的脑子不是电脑,我不可能永远记住ansible的每一个技术细节。因此,我这个笔记中才会尽可能详细的梳理好每一个细节,以备需要的时候查询。(小技巧:我会把这个笔记做成pdf文件,存放到我的手机中,需要查询某个细节时,随时随地都可以用手机查看~ 是不是很爽~)
2、我把ansible的知识按照体系进行梳理,结构清晰,图文并茂,更便于分享给需要帮助的朋友们。赠与金钱,我的金钱会减少;但是赠与知识,我的知识并不会减少。因为金钱只能剪切,而知识可以复制。

posted @ 2019-05-17 20:54  michaelni  阅读(442)  评论(0编辑  收藏  举报