A/B/n 测试,或拆分测试,是一种测试过程,用户流量通过该过程随机分布在应用程序(或应用程序组件)的两个或多个版本之间。评估业务指标以确定获胜版本——产生更大利润或业务价值的版本。例如,购物应用程序可能使用收入和用户参与度作为业务指标。
我们专注于部署在 Kubernetes 中的后端服务的 A/B/n 测试。例如,在下图中,前端可能是一个基于Node.js的在线商店。它依靠后端推荐服务向用户提供产品建议。我们有兴趣对推荐服务的多个版本进行 A/B/n 测试。在图中,我们有两个版本,v1(当前或默认版本)和v2 (候选版本)。
计算业务指标
A/B/n 测试依赖于对业务指标的评估。这些指标衡量应用程序特定版本的好处或价值。例如,在在线商店应用程序中,相关指标可能是销售收入或用户参与度。业务指标是特定于应用程序的;它们不能由基础设施计算,而必须由应用程序本身计算。当销售收入等指标由应用程序组件(如上面的前端在线商店)计算时,间接包括了后端推荐引擎的贡献。但是,前端组件无法将度量值与特定版本的后端相关联,因为它通常不知道使用了哪个版本的后端服务。
Iter8 SDK
为了正确地将指标归因于后端版本,前端有必要知道每个用户会话正在使用哪个后端版本。为了协助前端服务,可以使用 Iter8 SDK。Iter8是一个开源的Kubernetes发布优化器,可以帮助您在几秒钟内开始测试 Kubernetes 应用程序。使用 Iter8,您可以执行各种实验,例如 SLO 验证、金丝雀测试、混沌注入测试,以及现在的 A/B/n 测试。Iter8 SDK 提供了两个接口:
- Lookup(component, user_session),它标识一个组件的版本,调用者应该使用该版本向给定的用户会话发送请求。
- WriteMetric(metric_value, component, user_session),它将度量与组件的推荐版本相关联。
下面的序列图显示了我们的购物应用程序对这些接口的使用:
为响应用户请求,前端组件调用Lookup()以确定要使用哪个版本的后端组件。Lookup()返回固定数量的用户定义曲目标签之一。Lookup()保证为相同的用户会话推荐相同的曲目标签,确保后续对后端的调用将发送到相同的版本。然后,前端服务将其请求发送到推荐的后端,使用轨道作为路由的关键。当前端稍后为用户会话计算业务指标时,它可以安全地与推荐的后端版本相关联。WriteMetric()可用于执行此操作,从而无需前端跟踪到后端版本的映射。
由于Lookup()返回一组固定的轨道标签中的一个,前端服务必须配置为将流量路由到一组固定的服务——每个轨道一个。要跟踪的版本的映射随时间变化,并且作为候选版本部署的一部分使用已部署的 Kubernetes 对象上的标签完成。
显然,SDK 的引入对应用程序前端组件的开发人员提出了要求。此外,该方法依赖于前端应用程序可以根据一组轨道标识符路由后端请求的假设。这意味着对应用程序配置的要求。我们通过示例演示使用 Iter8 SDK 是多么容易。
Iter8 SDK 使用 gRPC 实现。该协议缓冲区文档中描述了这些接口。可以从中为各种语言生成特定于语言的代码。接口本身由 A/B/n 服务实现。
A/B/n 测试的应用程序开发
我们将考虑一个简单的两层应用程序。用 Node.js 编写的前端代表一个在线商店。 用Go编写的后端表示由前端调用的推荐引擎,用于向用户展示替代产品。可以在此处找到此应用程序的完整源代码。
前端组件支持两个接口:
- /getRecommendation要求产品推荐。
- /buy完成购买。
作为购买(请求)的副作用,/buy计算用户会话的业务指标。接口的实现/getRecommendation依赖于后端推荐服务。我们想在此后端推荐服务上运行 A/B/n 测试。
Iter8 SDK 使用 gRPC 实现。这些接口在协议缓冲区文档中进行了描述。可以从中为各种语言生成特定于语言的代码。我们生成的代码在示例应用程序中,可以直接复制以用于您自己的应用程序。
要使用 Iter8 SDK,需要 gRPC 和生成的库:
并实例化一个客户端:
此客户端用于两种用例:调用后端服务之前和写入指标值时。
调用后端服务
在调用后端推荐服务之前,需要Lookup()先调用该方法。返回的轨道标识符应该用作索引来选择发送请求的路线。在我们的示例前端中,这可以按如下方式实现:
在此实现中,用户会话是从请求标头中提取的X-User。请注意,如果与 Iter8 SDK 交互出现任何问题,将选择默认路由。可以在此处找到完整的示例代码。
编写指标
当/buy调用接口时,表示销售完成,计算业务指标。在我们的示例应用程序中,一个随机值被分配给指标sample_metric:
而已!不需要进一步的更改,无论运行了多少 A/B/n 测试——或者没有。
可以在此处 (Node.js)找到完整的示例前端代码。Python和Go中提供了替代实现。与节点示例一样,生成的代码可以直接复制到您自己的应用程序中。
为 A/B/n 测试部署应用程序
在部署将要进行 A/B/n 测试的应用程序组件时,唯一的要求是添加 Iter8 A/B/n 服务用于标识组件版本的标签。在我们的例子中,我们计划只测试后端推荐组件。
Iter8 SDK要求每个版本部署的资源实例中至少有一个包含以下标签:
- app.kubernetes.io/name:应用程序(组件)名称。
- app.kubernetes.io/version: 版本名称。
- iter8-tools/track:要用于此版本的曲目标签。
- iter8-tools/abn: 指示版本是否准备好接收流量的标志。
作为说明,可以使用以下命令手动部署示例应用程序。首先,部署前端在线商店组件:
接下来,部署示例后端推荐组件的当前或默认版本。我们将所需的标签添加到部署对象中,因为我们希望在此组件上运行 A/B/n 测试。
最后,部署 Iter8 服务(如果尚未部署):
运行 A/B/n 测试
我们现在准备运行 A/B 测试,比较后端推荐组件的当前部署的默认版本和新的候选版本。运行测试有两个步骤。第一步是部署组件的一个或多个候选版本。使用我们的示例应用程序,我们展示了部署后端推荐引擎的候选版本所需的步骤。待候选版本完全部署后,Iter8 Service会开始将分配给它的track label发送给前端服务。作为响应,前端商店将开始向新版本的推荐引擎发送请求。第二步是启动 Iter8 实验来评估收集到的指标。我们展示了一个多循环实验——一个定期执行直到被删除的实验。在每次执行时,
部署候选版本
可以以任何方式部署候选版本——手动(如我们所示)或使用 CI 工作流。如上所述,必须将所需标签添加到至少一个 Kubernetes 资源对象。
在我们的示例应用程序中部署后端推荐服务的候选版本:
在部署候选版本时,必须注意确保候选版本在前端向其发送任何请求之前已完全初始化。这可以通过仅在候选版本完全初始化后设置 iter8-tools/abn 标签来确保。一旦初始化,Iter8 A/B/n 服务将开始提供响应Lookup()请求的版本。
这些步骤如下图所示。最初,仅部署默认版本 v1 并接收来自前端的所有流量。
当部署候选版本 v2 时,前端继续将所有请求发送到默认版本。
一旦候选版本准备好接收流量,例如,当 pod 为 时Ready,设置标签iter8.tools/abn。这会触发 Iter8 服务开始向前端推荐它,而前端又开始向两个版本发送请求。在我们的示例应用程序中:kubectl label deployment backend-candidate iter8.tools/abn=true
实际上,测试取决于应用到前端服务的用户负载。在本教程中,我们使用将请求发送到存储端点的脚本来应用负载,/getRecommendation并/buy.使用将本地请求转发到集群:kubectl port-forward svc/frontend 8090:8090
并为不同的用户生成负载;例如,对于用户foo和foobar:
启动 Iter8 实验
启动 Iter8 实验以定期读取通过 编写的业务指标WriteMetric()。可以使用预定义的 abnmetrics 任务:
此命令启动 Iter8 实验,该实验运行预定义的 abnmetrics 任务以读取在默认命名空间中运行的后端应用程序组件的记录指标。使用 cronjob runner 表示实验将根据 cronjobSchedule 定期运行(在本例中为每分钟一次)。实验结果将随时间更新。
检查实验结果并决定是否推广候选版本。第一份报告将在实验任务第一次运行后(大约一分钟)可用。示例报告如下:
提升赢家
在推广候选版本时,必须注意确保没有用户流量意外发送到正在升级或删除的版本。任何可用于执行促销的方法都应包括以下步骤。还显示了示例应用程序的手动步骤。
最初,默认版本和候选版本都从前端接收请求。
首先,iter8.tools/abn从与默认版本关联的资源中取消设置标签。这会在转换期间禁用到默认版本的流量——Iter8 SDK 接口Lookup()将从其推荐后端列表中删除默认轨道:
接下来,使用新版本重新部署与默认轨道关联的对象。
更新的对象准备就绪后,添加iter8.tools/abn指示它已准备好接收流量的标签。
此时,默认和候选轨道标签都与相同的后端版本相关联。现在可以删除候选版本。为此,取消设置iter8.tools/abn标签以终止到候选资源的流量:
最后,删除候选资源。
最后的想法
我们探讨了进行 A/B/n 测试的一些挑战,尤其是应用程序的后端服务。关键挑战涉及前端组件计算业务指标无法正确地将它们与有助于其计算的后端版本相关联。Iter8 SDK 使前端能够正确地建立这种关联。它通过提供一个查找接口来实现这一点,该接口允许前端服务识别后端服务的版本以在处理用户请求时使用。这样,它可以可靠地将业务指标分配给后端版本。我们展示了使用 Iter8 SDK 修改前端服务和运行 A/B/n 测试是多么容易。
只需要几行额外的代码就可以对前端进行一次性更改。启用候选版本进行测试只需要添加一些标签。
试用本教程后,使用您自己的应用程序进行试用。