Ansible 错误处理block和rescue示例

时间:2020-01-09 10:34:02  来源:igfitidea点击:

Ansible中的块允许我们在逻辑上将一组任务分组在一起,主要用于以下两个目的之一。一种可能是将条件逻辑应用于整个任务集。我们可以使用ansible block、rescue和always来执行不同类型的错误处理,其中使用rescue block可以执行清除操作。我们将通过本节中的示例详细了解这一点。

例1:为什么我们要用block

我已经写了一个小剧本"ansible-blocks-1.yml",它你理解ansible-blocks是如何有用以及为什么应该使用它。在这个剧本中,我们有4个任务,其中3个任务需要根级权限,而其中一个任务可以在没有根级权限的情况下执行。

现在我用了3次"become:yes",这只是增加了剧本中的行数。

[ansible@controller ~]$ vim ansible-blocks-1.yml
--
 - name: Ansible Blocks
   hosts: server1
   gather_facts: false
   tasks:
     - name: List usr directory content
       command: "ls -l /usr/"
       become: yes
     - name: List root partition content
       command: "ls -l /root/"
       become: yes
     - name: List ansible user's home directory content
       command: "ls -l ~/"
     - name: List bin diretcory content
       command: "ls -l /bin/"
       become: yes

我们可以在play开始时加上become:yes,但是这个变量将应用到整个play中,我们不需要任务3的这个权限。为了克服这个问题,我们有"块级解决方案",其中我们可以将所有常见任务放在单个块下。

这就是剧本中"块"的样子。其中:我们将所有需要根级别权限的任务组合到一个块中,并且不再使用been 3次,而是将其应用到整个块中。

--
 - name: Ansible Blocks
   hosts: server1
   gather_facts: false
   tasks:
     - block:
        - name: List usr directory content
          command: "ls -l /usr/"
        - name: List root partition content
          command: "ls -l /root/"
        - name: List bin directory content
          command: "ls -l /bin/"
       become: yes
     - name: List ansible user's home directory content
       command: "ls -l ~/"

说明:

注意对齐,这一点很重要。与"block"相比,"been"键的空格数相同,因此"been"值适用于整个块。

现在我们可以执行剧本:

[ansible@controller ~]$ ansible-playbook ansible-blocks-1.yml
PLAY [Ansible Blocks] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [List usr directory content] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [server1]
TASK [List root partition content] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
changed: [server1]
TASK [List bin directory content] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [server1]
TASK [List ansible user's home directory content] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [server1]
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server1                    : ok=4    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

示例2:使用rescue block执行错误恢复

在使用ansible块执行恢复操作时,我们还有其他好处,即如果块中的任何任务失败,则在理想情况下,playbook将退出(除非我们使用"ignore_errors:yes")。但是对于块,我们可以分配一个"rescue"块,它可以再次包含一系列任务。因此,如果块中的任何任务失败,那么将自动执行恢复块中的任务,恢复块中的任务可以执行我们在失败时希望执行的任何清理活动。

最后,我们还有一个'always'块,它将独立于任务执行状态进行调用,可以根据要求提供类似摘要或者任何内容。

在这个示例playbook ansible-blocks-2.yml中,我们在"block"中创建了两个任务,其中一个任务将通过,而另一个任务预计将失败。在这种情况下,"rescue"块将在"always"块之后执行。

--
 - name: Ansible Blocks
   hosts: server1
   gather_facts: false
   tasks:
     - block:
        - name: List home directory content
          command: "ls -l ~/"
        - name: Failing intentionally
          command: "ls -l /tmp/does-not-exist"
       rescue:
        - name: Rescue block (perform recovery)
          debug:
            msg: "Something went wrong, cleaning up.."
       always:
        - name: This will execute always
          debug:
            msg: "I will execute even in failure scenario"

让我们来执行这个剧本:

[ansible@controller ~]$ ansible-playbook ansible-blocks-2.yml
PLAY [Ansible Blocks] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [List home directory content] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
changed: [server1]
TASK [Failing intentionally] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
fatal: [server1]: FAILED! => {"changed": true, "cmd": ["ls", "-l", "/tmp/does-not-exist"], "delta": "0:00:00.003050", "end": "2017-09-26 10:29:13.864820", "msg": "non-zero return code", "rc": 2, "start": "2017-09-26 10:29:13.861770", "stderr": "ls: cannot access '/tmp/does-not-exist': No such file or directory", "stderr_lines": ["ls: cannot access '/tmp/does-not-exist': No such file or directory"], "stdout": "", "stdout_lines": []}
TASK [Rescue block (perform recovery)] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [server1] => {
    "msg": "Something went wrong, cleaning up.."
}
TASK [This will execute always] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
ok: [server1] => {
    "msg": "I will execute even in failure scenario"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server1                    : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=1    ignored=0

如我们所见,我们的task1成功了,但是task2失败了,之后执行恢复块,然后执行always块。

现在假设"block"部分中没有任何任务失败,在这种情况下,"rescue"块将不会执行,但"always"块将独立于任务状态执行。

我已经更新了playbook,列出了/tmp目录内容,这样第二个任务就不会失败,让我们在执行后观察输出:

[ansible@controller ~]$ ansible-playbook ansible-blocks-2.yml
PLAY [Ansible Blocks] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [List home directory content] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
changed: [server1]
TASK [List tmp directory content] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [server1]
TASK [This will execute always] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
ok: [server1] => {
    "msg": "I will execute even in failure scenario"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server1                    : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

因此,这次没有调用'rescue'块,但按预期执行了'always'块。

示例3:错误和块恢复的实际示例

既然我们知道了阻塞和救援的概念,让我们在实际用例中使用它。我将创建一个剧本,它将在我们的区块下执行以下任务

  • 安装vsftpd

  • 备份/etc/vsftpd/vsftpd.conf文件在同一托管节点上

  • 将"vsftpd.j2"jinja模板从控制器节点复制到托管节点并替换"/etc/vsftpd"/vsftpd.conf文件`

  • 通过尝试访问错误的位置故意使块失败

下一步,我将有一个'救援'块,这样如果出了问题,我们将执行一些清理/回退。在这方面,我有两个任务:

  • 在控制台上打印恢复消息

  • 还原vsftpd.conf文件使用备份文件vsftpd.conf.bkp文件在托管节点上

最后,我们将始终阻止独立于任务执行状态重新启动"vsftpd"服务

这是我们的剧本"ansible-blocks-3.yml":

--
 - name: Install vsftpd
   hosts: server1
   become: yes
   vars:
     anonymous_enable: yes
     local_enable: yes
     write_enable: yes
     anon_upload_enable: yes
   tasks:
     - block:
        - name: install vsftp
          yum:
            name: vsftpd
        - name: take backup of existing config
          copy:
            src: /etc/vsftpd/vsftpd.conf
            dest: /etc/vsftpd/vsftpd.conf.bkp
            remote_src: yes
        - name: use Jinja2 template to configure vsftpd
          template:
            src: vsftpd.j2
            dest: /etc/vsftpd/vsftpd.conf
        - name: This will fail
          command: "ls -l /tmp/does-not-exist"
       rescue:
        - name: Recovery block
          debug:
            msg: "something failed, restoring vsftpd.conf from backup"
        - name: Restoring vsftpd.conf
          copy:
            src: /etc/vsftpd/vsftpd.conf.bkp
            dest: /etc/vsftpd/vsftpd.conf
            remote_src: yes
       always:
        - name: Restarting vsftpd
          service:
            name: vsftpd
            state: restarted

这是我们的示例模板。

我们在前面使用Jinja2模板时使用了相同的模板。

[ansible@controller ~]$ cat vsftpd.j2
anonymous_enable={{ anonymous_enable }}
local_enable={{ local_enable }}
write_enable={{ write_enable }}
anon_upload_enable={{ anon_upload_enable }}
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
pam_service_name=vsftpd
userlist_enable=YES
# MY IP Address={{ ansible_facts['default_ipv4']['address'] }}

让我们执行这个剧本并验证以下步骤:

[ansible@controller ~]$ ansible-playbook ansible-blocks-3.yml
PLAY [Install vsftpd] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [Gathering Facts] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [server1]
TASK [install vsftp] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
ok: [server1]
TASK [take backup of existing config] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [server1]
TASK [use Jinja2 template to configure vsftpd] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
changed: [server1]
TASK [This will fail] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
fatal: [server1]: FAILED! => {"changed": true, "cmd": ["ls", "-l", "/tmp/does-not-exist"], "delta": "0:00:00.003701",                                                                                                                        "end": "2017-09-26 10:56:49.076834", "msg": "non-zero return code", "rc": 2, "start": "2017-09-26 10:56:49.073133", "s                                                                                                                       tderr": "ls: cannot access '/tmp/does-not-exist': No such file or directory", "stderr_lines": ["ls: cannot access '/tm                                                                                                                       p/does-not-exist': No such file or directory"], "stdout": "", "stdout_lines": []}
TASK [Recovery block] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
ok: [server1] => {
    "msg": "something failed, restoring vsftpd.conf from backup"
}
TASK [Restoring vsftpd.conf] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
changed: [server1]
TASK [Restarting vsftpd] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
changed: [server1]
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
server1                    : ok=7    changed=4    unreachable=0    failed=0    skipped=0    rescued=1    ignored=0

如预期,任务1和任务2已成功执行,而任务3已失败。由于我们块中的一个任务失败,因此执行"rescue"块,它将恢复我们的任务vsftpd.conf文件文件。最后,执行always块以重新启动"vsftpd"服务。

我们还可以连接到server1并检查vsftpd.conf.bkp文件创建时间:

[ansible@server-1 ~]$ sudo ls -l /etc/vsftpd/vsftpd.conf*
-rw------- 1 root root 5098 Jan 14  2019 /etc/vsftpd/vsftpd.conf
-rw-r--r-- 1 root root 5098 Jan 14  2019 /etc/vsftpd/vsftpd.conf.bkp