测试策略
将测试与 Ansible Playbook 集成
很多时候,人们会问,“如何最好地将测试与 Ansible Playbook 集成?” 有很多选择。 Ansible 实际上被设计为一个“快速失败”和有序的系统,因此可以很容易地将测试直接嵌入到 Ansible Playbook 中。在本章中,我们将深入探讨一些集成基础设施测试的模式,并讨论可能合适的测试级别。
注意
本章是关于测试您正在部署的应用程序,而不是关于如何在开发过程中测试 Ansible 模块的章节。有关该内容,请跳转到开发部分。
通过将一定程度的测试纳入您的部署工作流程中,当代码进入生产环境时,意外情况会更少,并且在许多情况下,可以使用测试来防止失败的更新迁移到整个安装。由于它是基于推送的,因此也很容易在 localhost 或测试服务器上运行这些步骤。Ansible 允许您在升级工作流程中插入任意数量的检查和平衡。
合适的测试级别
Ansible 资源是期望状态的模型。因此,不应该需要测试服务是否已启动、软件包是否已安装或其他类似的事情。Ansible 是确保这些事情以声明方式为真的系统。相反,在您的 Playbook 中声明这些事情。
tasks:
- ansible.builtin.service:
name: foo
state: started
enabled: true
如果您认为服务可能未启动,最好的做法是请求启动它。如果服务启动失败,Ansible 将会适当地发出警告。(这不应与服务是否正在执行某些功能混淆,稍后我们将展示更多关于如何执行此操作的信息)。
检查模式作为漂移测试
在上面的设置中,Ansible 中的 --check
模式也可以用作测试层。如果针对现有系统运行部署 Playbook,则使用 ansible 命令的 --check
标志将报告 Ansible 是否认为它必须进行任何更改才能使系统进入期望状态。
这可以让你提前知道是否需要在给定系统上进行部署。通常,脚本和命令不会在检查模式下运行,因此,如果您希望在正常模式下执行某些步骤,即使使用 --check
标志,例如调用 script 模块,请禁用这些任务的检查模式
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
如果使用角色(您应该使用角色,角色很棒!),则 script 模块推送的脚本可以位于角色的 '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 中编写一些基本应用程序验证,它们将在您每次部署时运行。
因此,部署到本地开发 VM 和暂存环境都将验证事情是否按照计划进行,然后再进行生产部署。
您的工作流程可能如下所示
- 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 通信指南