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。

相关新闻

发表回复

Please Login to Comment

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

                                                                                                                                    RHCE9学习指南全部更新完成,点击阅读