RHCE9学习指南 第29章 playbook的使用
ansible的许多模块使都是在命令行中执行的,每次只能执行一个模块。如果需要执行多个模块,且要写判断语句,判断模块是否执行成功了,如果没成功则会怎么处理等。这时就需要写脚本了,ansible中的脚本咱们叫作playbook,每个playbook中可以包括多个play。
29.1 playbook的写法
playbook是以yaml或yml作为后缀,每个play都可以使用两种格式来写。
(1)参数写在模块后面,命令如下。
- name: play的名称
hosts: 主机组1,主机组2,...#--列出主机组
tasks:
- name: 提示信息1
模块1:argx1=vx1 argx2=vx2 #这种写法,=号两边不要空格
- name: 提示信息x
模块x:rgx1=vx1 argx2=vx2
一个play中可以包含多个task,每个task调用一个模块。
(2)参数分行写,一行后一个参数,命令如下。
- hosts: 主机组1,主机组2,... #--列出主机组
tasks:
- name: 描述语句1
模块1:
argx1: vx1 #这里指定模块的参数,注意冒号后面的空格
argx2: vx2
- name: 描述语句2
模块x:
argx1: vx1
argx2: vx2
需要注意的是,yaml文件对缩进有极严格的要求,每个缩进都是2个空格,不要按【Tab】键。
一个完整的playbook中至少要包括一个play,下面是一个包含2个play的playbook,命令如下。
---
- name: 第一个play的名称
hosts: 主机组1,主机组2,...#--列出主机组
tasks:
- name: 提示信息1
模块1:argx1=vx1 argx2=vx2
- name: 提示信息x
模块x:rgx1=vx1 argx2=vx2
- name: 第2个play的名称
hosts: 主机组3,主机组4,...#--列出主机组
gather_facts: false
tasks:
- name: 提示信息1
模块1:argx1=vx1 argx2=vx2
- name: 提示信息x
模块x:rgx1=vx1 argx2=vx2
在写playbook时,一定要先写好框架,然后往框架中写内容。如果在多个主机组上做的操作是相同的,可以把他们放在同一个play中。如果在不同的主机组中执行不同的操作,可以通过不同的play分别来实现。
这里第二个play中加了一句gather_facts: false,意思是在执行此play时不需要通过setup获取主机组的信息。所以,如果在tasks中如果没有使用到fact变量,建议能加上这句,可以提升执行的速度。
写好之后运行playbook的方法是
ansible-playbook 文件
也可以通过
ansible-navigator run 文件 -m stdout
这种方式来运行,后面的命令里,我们基本都使用此命令来运行
本章实验均放在/home/lduan/demo1目录中做,先创建demo1目录,并把ansible.cfg和hosts拷贝进去,命令如下。
[lduan@server ~]$ mkdir demo1
[lduan@server ~]$ cp ansible.cfg hosts demo1/
[lduan@server ~]$ cd demo1/
[lduan@server demo1]$
练习1:
写一个playbook文件 test1.yaml,在server2和server3上打印主机名和IP地址。
分析:因为在server2和server3上做相同的操作,所以只要一个play即可。这个play中包括两个task:一个用于打印主机名,一个用于打印IP,命令如下。
[lduan@server demo1]$ cat test1.yaml
---
- hosts: server2,server3
tasks:
- name: 打印主机名
debug: msg={{ansible_fqdn}}
- name: 打印IP
debug: msg={{ansible_default_ipv4.address}}
[lduan@server demo1]$
运行此文件,命令如下。
[lduan@server demo1]$ ansible-navigator run ./test1.yaml -m stdout
...输出...
TASK [打印主机名]
ok: [server2] => {
"msg": "server2.rhce.cc"
}
ok: [server3] => {
"msg": "server3.rhce.cc"
}
...输出...
TASK [打印IP]
ok: [server2] => {
"msg": "192.168.26.102"
}
ok: [server3] => {
"msg": "192.168.26.103"
}
...输出...
[lduan@server demo1]$
练习2:
写一个playbook文件 test2.yaml,在server2上打印主机名,在server3上打印IP地址。
分析:因为在server2和在server3上做的是不同的操作,所以这里写两个play,一个play在server2上执行,一个play在server3上执行。每个play上只要包括一个task即可,命令如下。
[lduan@server demo1]$ cat test2.yaml
---
- name: 在server2上的操作
hosts: server2
tasks:
- name: 这是第一个操作, 打印主机名
debug: msg={{ansible_fqdn}}
- name: 在server3上的操作
hosts: server3
tasks:
- name: 打印IP
debug: msg={{ansible_default_ipv4.address}}
[lduan@server demo1]$
执行此playbook,命令如下。
[lduan@server demo1]$ ansible-navigator run ./test2.yaml -m stdout
PLAY [在server2上的操作]
...输出...
TASK [这是第一个操作, 打印主机名]
ok: [server2] => {
"msg": "server2.rhce.cc"
}
...输出...
PLAY [在server3上的操作]
...输出...
ok: [server3]
TASK [打印IP]
ok: [server3] => {
"msg": "192.168.26.103"
}
[lduan@server demo1]$
练习3:
写一个playbook文件 test3.yaml,要求如下。
(1)在server2上安装vsftpd,启动并开机自动启动vsftpd,设置防火墙开放ftp服务。
(2)在server3上安装httpd,启动并开机自动启动httpd,设置防火墙开放http服务。
分析:因为在server2和在server3上做的是不同的操作,所以这里写两个play。
第一个play在server2上执行,包括3个task,分别用于安装、服务管理、防火墙设置。
第二个play在server3上执行,包括3个task,分别用于安装、服务管理、防火墙设置。
文件内容如下。
[lduan@server demo1]$ cat test3.yaml
---
- name: 第一个play在server2上要做的操作---安装vsftp,并启动,开启防火墙
hosts: server2
tasks:
- name: 第一个操作安装ftp
yum: name=vsftpd state=installed
- name: 第二个操作启动服务
service: name=vsftpd state=started enabled=yes
- name: 开启防火墙
firewalld: service=ftp state=enabled immediate=yes permanent=yes
- name: 第二个play在server3上要做的操作--安装httpd,启动,开启防火墙
hosts: server3
tasks:
- name: 第一个操作安装httpd
yum: name=httpd state=installed
- name: 第二个操作启动服务
service: name=httpd state=started enabled=yes
- name: 第三个操作 开启防火墙
firewalld: service=http state=enabled immediate=yes permanent=yes
[lduan@server demo1]$
执行此playbook,命令如下。
[lduan@server demo1]$ ansible-navigator -m stdout run ./test3.yaml
PLAY [第一个play在server2上要做的操作---安装vsftp,并启动,开启防火墙] ****************************************************************
ok: [server2]
TASK [第一个操作安装ftp] ****************************************************************
ok: [server2]
TASK [第二个操作启动服务] ****************************************************************
ok: [server2]
TASK [开启防火墙] ****************************************************************
changed: [server2]
PLAY [第二个play在server3上要做的操作--安装httpd,启动,开启防火墙] ****************************************************************
ok: [server3]
TASK [第一个操作安装httpd] ****************************************************************
ok: [server3]
TASK [第二个操作启动服务] *****************************************************************
ok: [server3]
TASK [第三个操作 开启防火墙] ****************************************************************
ok: [server3]
[lduan@server demo1]$
29.2 错误处理
在写playbook时,会遇到各种各样的问题,例如,命令出错了,或者引用的变量不存在等等诸多可能出现的错误。在playbook中具备一定的错误处理能力。
29.2.1 ignore_errors
执行一个playbook时,如果其中某个task有错误,则后续的play就不再继续运行下去了。看下面的例子,编写test4.yaml内容如下。
[lduan@server demo1]$ cat test4.yaml
---
- hosts: server2
gather_facts: false
tasks:
- name: aa
debug: msg={{default_xxx}}
- name: bb
debug: msg="22222"
[lduan@server demo1]$
这里写了两个task,一个是aa另一个是bb,在aa这个task中引用了一个不存在的变量default_xxx,所以会导致aa这个task报错。如果某个task报错,则后续的task就不再执行了,所以bb这个task也不会继续执行了。
[lduan@server demo1]$ ansible-navigator run ./test4.yaml -m stdout
PLAY [server2] ***********************
TASK [aa] **************************
fatal: [server2]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'default_xxx' is undefined\n\nThe error appears to be in '/home/lduan/demo1/test4.yaml': line 5, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: aa\n ^ here\n"}
PLAY RECAP ***********************
server2 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
[lduan@server demo1]$
如果想task aa出错时不影响后续task的执行,那么可以在task aa中添加ignore_errors: true来忽略这个报错继续往下执行,命令如下。
[lduan@server demo1]$ cat test4.yaml
---
- hosts: server2
gather_facts: false
tasks:
- name: aa
debug: msg={{default_xxx}}
ignore_errors: true
- name: bb
debug: msg="22222"
[lduan@server demo1]$
这里添加了ignore_errors: true忽略报错信息,下面运行test4.yaml看结果。
[lduan@server demo1]$ ansible-navigator run ./test4.yaml -m stdout
PLAY [server2] **********************************
TASK [aa] **************************************
fatal: [server2]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'default_xxx' is undefined\n\nThe error appears to be in '/home/lduan/demo1/test4.yaml': line 5, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: aa\n ^ here\n"}
...ignoring
TASK [bb] **************************************
ok: [server2] => {
"msg": "22222"
}
PLAY RECAP ************************************
server2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1
[lduan@server demo1]$
这里可以看到,即使task aa出错了但是后续的task bb仍然继续运行了。
29.2.2 fail语句
fail模块和debug模块一样,都是用来打印信息的,区别在于debug执行完成之后会继续进行后续模块的操作,而fail打印完报错信息之后,然后会退出整个playbook。
[lduan@server demo1]$ cat test5.yaml
---
- hosts: server2
gather_facts: false
tasks:
- name: aa
debug: msg="111"
- name: bb
fail: msg="222"
- name: cc
debug: msg="333"
[lduan@server demo1]$
这里写了3个task,其中task aa和task cc使用debug打印信息,task bb用fail打印信息。下面运行这个playbook看结果。
[lduan@server demo1]$ ansible-navigator run ./test5.yaml -m stdout
PLAY [server2] *******************************
TASK [aa] ***********************************
ok: [server2] => {
"msg": "111"
}
TASK [bb] ************************************
fatal: [server2]: FAILED! => {"changed": false, "msg": "222"}
PLAY RECAP **********************************
server2 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
[lduan@server demo1]$
这里可以看到,task aa正确执行之后,继续执行task bb。因为task bb用的是fail来打印信息,所以执行完之后,就退出playbook了,所以task cc并没有执行。
作业
1.写一个名称为chap29-1.yaml的playbook,获取db主机组中主机的IP地址。
2.写一个名称为chap29-2.yaml的playbook,完成下面的。
(1)要求在server2上安装nfs-utils包,启动nfs-server服务并设置开启自动启动。
(2)在server2上配置firewalld,要求开放nfs、rpc-bind、mountd三个服务,要求重启系统也生效。
(3)在server2上创建目录/share。
(4)在server2的文件/etc/exports中写入/share *(rw,no_root_squash)。
(5)在server2执行系统命令"exportfs -arv"。
(6)在server3上挂载192.168.26.102:/share 到/nfs目录,文件系统为nfs。