Ansible 错误处理block和rescue示例
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