测试策略
将测试与Ansible Playbook集成
许多人都会问:“如何才能最好地将测试与Ansible playbook集成?”有很多方法。Ansible实际上设计为一个“快速失败”且有序的系统,因此可以轻松地将测试直接嵌入Ansible playbook中。在本章中,我们将介绍一些集成基础设施测试的模式,并讨论可能合适的正确测试级别。
注意
本章介绍的是测试您正在部署的应用程序,而不是介绍如何在开发过程中测试Ansible模块。有关该内容,请转到“开发”部分。
通过将一定程度的测试纳入您的部署工作流程,当代码进入生产环境时,将减少意外情况,并且在许多情况下,测试可用于生产环境中,以防止失败的更新迁移到整个安装过程中。由于它是基于推送的,因此也很容易在本地主机或测试服务器上运行这些步骤。Ansible允许您根据需要在升级工作流程中插入任意数量的检查和平衡。
正确的测试级别
Ansible资源是期望状态的模型。因此,无需测试服务是否已启动,软件包是否已安装或其他此类事情。Ansible是确保这些内容声明性地为真的系统。相反,在您的playbook中声明这些内容。
tasks:
- ansible.builtin.service:
name: foo
state: started
enabled: true
如果您认为服务可能未启动,最好的方法是请求启动它。如果服务启动失败,Ansible将发出相应的警告。(这不要与服务是否正在执行某些功能相混淆,稍后我们将详细介绍如何执行此操作)。
使用检查模式进行漂移测试
在上述设置中,Ansible中的--check
模式也可以用作测试层。如果针对现有系统运行部署playbook,则对ansible命令使用--check
标志将报告Ansible是否认为它需要进行任何更改才能将系统置于期望状态。
这可以提前让您知道是否需要部署到给定系统。通常情况下,脚本和命令不会在检查模式下运行,因此如果您希望某些步骤即使在使用--check
标志时也以普通模式执行(例如对脚本模块的调用),请为这些任务禁用检查模式。
roles:
- webserver
tasks:
- ansible.builtin.script: verify.sh
check_mode: false
用于测试的有用模块
某些playbook模块特别适合于测试。以下是一个确保端口打开的示例。
tasks:
- ansible.builtin.wait_for:
host: "{{ inventory_hostname }}"
port: 22
delegate_to: localhost
以下是如何使用URI模块来确保Web服务返回的示例。
tasks:
- action: uri url=https://www.example.com return_content=yes
register: webpage
- fail:
msg: 'service is not happy'
when: "'AWESOME' not in webpage.content"
很容易在远程主机上推送任意脚本(使用任何语言),如果脚本的返回码非零,则脚本将自动失败。
tasks:
- ansible.builtin.script: test_script1
- ansible.builtin.script: test_script2 --parameter value --parameter2 value
如果使用角色(您应该使用,角色很棒!),则由脚本模块推送的脚本可以位于角色的“files/”目录中。
assert模块使验证各种类型的真值变得非常容易。
tasks:
- ansible.builtin.shell: /usr/bin/some-command --parameter value
register: cmd_result
- ansible.builtin.assert:
that:
- "'not ready' not in cmd_result.stderr"
- "'gizmo enabled' in cmd_result.stdout"
如果您需要测试Ansible配置未声明性设置的文件是否存在,“stat”模块是一个不错的选择。
tasks:
- ansible.builtin.stat:
path: /path/to/something
register: p
- ansible.builtin.assert:
that:
- p.stat.exists and p.stat.isdir
如上所述,无需检查命令的返回码。Ansible会自动检查它们。与其检查用户是否存在,不如考虑使用user模块来创建用户。
Ansible是一个快速失败的系统,因此当创建用户时出现错误时,它将停止playbook运行。您无需检查其结果。
测试生命周期
如果将一定程度的基本应用程序验证写入您的playbook中,它们将在每次部署时运行。
因此,部署到本地开发虚拟机和登台环境都将验证部署前的事情是否符合计划。
您的工作流程可能如下所示:
- Use the same playbook all the time with embedded tests in development
- Use the playbook to deploy to a staging environment (with the same playbooks) that simulates production
- Run an integration test battery written by your QA team against staging
- Deploy to production, with the same integrated tests.
如果您是生产Web服务,则您的QA团队应该编写类似集成测试套件。这将包括诸如Selenium测试或自动化API测试之类的测试,通常不会嵌入到您的Ansible playbook中。
但是,将一些基本运行状况检查纳入您的playbook是有意义的,在某些情况下,可以对远程节点运行QA测试套件的子集。这就是下一节介绍的内容。
将测试与滚动更新集成
如果您已阅读控制任务运行位置:委托和本地操作,您可能会很快发现滚动更新模式可以扩展,并且您可以使用playbook运行的成功或失败来决定是否将机器添加到负载均衡器中。
这是嵌入式测试的极好总结。
---
- hosts: webservers
serial: 5
pre_tasks:
- name: take out of load balancer pool
ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
tasks:
- ansible.builtin.include_role:
name: "{{ item }}"
loop:
- common
- webserver
- name: run any notified handlers
ansible.builtin.meta: flush_handlers
- name: test the configuration
ansible.builtin.include_role:
name: apply_testing_checks
post_tasks:
- name: add back to load balancer pool
ansible.builtin.command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
当然,在上面,“从池中取出”和“添加回”步骤将被对Ansible负载均衡器模块或适当的shell命令的调用所取代。您可能还有一些步骤使用监控模块来启动和结束机器的中断窗口。
但是,您可以从上面看到,测试用作一个关卡——如果未执行“apply_testing_checks”步骤,则机器将不会返回池中。
阅读关于“max_fail_percentage”的委托章节,您还可以控制多少次测试失败会阻止滚动更新继续进行。
上述方法也可以修改为从测试机器远程对机器运行步骤。
---
- hosts: webservers
serial: 5
pre_tasks:
- name: take out of load balancer pool
ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
roles:
- common
- webserver
tasks:
- ansible.builtin.script: /srv/qa_team/app_testing_script.sh --server {{ inventory_hostname }}
delegate_to: testing_server
post_tasks:
- name: add back to load balancer pool
ansible.builtin.command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
在上面的示例中,在将脚本放回池中之前,会从测试服务器对远程节点运行脚本。
如果出现问题,请使用Ansible自动生成的重试文件修复失败的服务器,以便仅对这些服务器重复部署。
实现持续部署
如果需要,可以扩展上述技术以启用持续部署实践。
工作流程可能如下所示:
- Write and use automation to deploy local development VMs
- Have a CI system like Jenkins deploy to a staging environment on every code change
- The deploy job calls testing scripts to pass/fail a build on every deploy
- If the deploy job succeeds, it runs the same deploy playbook against production inventory
一些Ansible用户使用上述方法每小时部署六到十二次,而无需使所有基础设施脱机。如果您希望达到这个级别,那么自动化QA文化至关重要。
如果您仍然进行大量的QA手动测试,您仍然应该决定是否手动部署,但这仍然有助于使用上一节中的滚动更新模式并使用“script”、“stat”、“uri”和“assert”等模块合并一些基本运行状况检查。
结论
Ansible认为您不需要另一个框架来验证基础设施的基本要素是否正确。这是因为Ansible是一个基于顺序的系统,它会在主机上出现未处理的错误时立即失败,并防止进一步配置该主机。这会将错误强制显示在顶部,并在Ansible运行结束时在摘要中显示它们。
但是,由于Ansible被设计为一个多层编排系统,因此它可以轻松地将测试合并到playbook运行的末尾,无论是使用松散的任务还是角色。与滚动更新一起使用时,测试步骤可以决定是否将机器放回负载均衡池中。
最后,因为Ansible错误会一直传播到Ansible程序本身的返回码,并且Ansible默认情况下以简单的基于推送的模式运行,所以如果您希望将其用作构建环境的一部分来推出系统,那么Ansible是一个很好的步骤持续集成/持续交付管道,如上文所述。
重点不应放在基础设施测试上,而应放在应用程序测试上,因此我们强烈建议您与您的QA团队一起讨论哪些测试可以在每次部署开发虚拟机时运行,以及他们希望在每次部署中对登台环境运行哪些测试。显然,在开发阶段,单元测试也很棒。但是不要对您的playbook进行单元测试。Ansible以声明的方式描述资源的状态,因此您不必这样做。但是,如果有一些情况您想确保某些事情,那就太好了,像stat/assert这样的模块非常适合此目的。
总而言之,测试是一件非常组织性和特定于站点的任务。每个人都应该这样做,但是对您的环境最有效的方法会因您正在部署的内容和使用它的人而异——但是每个人都会从更强大、更可靠的部署系统中受益。
另请参见
- 集合索引
浏览现有的集合、模块和插件
- 使用playbook
playbook简介
- 控制任务运行位置:委托和本地操作
委托,可用于处理负载均衡器、云和本地执行的步骤。
- 沟通
有问题?需要帮助?想分享您的想法?请访问Ansible沟通指南