当前位置: 首页 > 科技观察

简化后端服务的A-B-n测试

时间:2023-03-20 13:40:11 科技观察

了解如何使用Iter8SDK在Kubernetes中轻松运行A/B/n实验。Iter8使您可以轻松地为您的应用程序/ML模型收集业务指标。A/B/n测试或拆分测试是一种测试过程,通过该过程将用户流量随机分布在应用程序(或应用程序组件)的两个或多个版本之间。评估业务指标以确定获胜版本——产生更大利润或业务价值的版本。例如,购物应用程序可能使用收入和用户参与度作为业务指标。我们专注于部署在Kubernetes中的后端服务的A/B/n测试。例如,在下图中,前端可能是基于Node.js的在线商店。它依靠后端推荐服务向用户提供产品建议。我们有兴趣对推荐服务的多个版本进行A/B/n测试。在图中,我们有两个版本,v1(当前或默认版本)和v2(候选版本)。计算业务指标A/B/n测试依赖于对业务指标的评估。这些指标衡量应用程序特定版本的好处或价值。例如,在在线商店应用程序中,相关指标可能是销售收入或用户参与度。业务指标是特定于应用程序的;它们不能由基础设施计算,而必须由应用程序本身计算。当销售收入等指标由应用程序组件(如上面的前端在线商店)计算时,间接包括了后端推荐引擎的贡献。但是,前端组件无法将指标与特定版本的后端相关联,因为它通常不知道使用的是哪个版本的后端服务。为了让Iter8SDK正确地将指标归因于后端版本,前端有必要知道每个用户会话正在使用哪个后端版本。辅助前端服务,可以使用Iter8SDK。Iter8是一个开源的Kubernetes发布优化器,可帮助您在几秒钟内开始测试Kubernetes应用程序。使用Iter8,您可以进行SLO验证、金丝雀测试、混沌注入测试以及现在的A/B/n测试等各种实验。Iter8SDK提供了两个接口:Lookup(component,user_session),它标识了调用者应该用来向给定用户会话发送请求的组件的版本。WriteMetric(metric_value,component,user_session),它将度量与组件的推荐版本相关联。下面的序列图显示了我们的购物应用程序对这些接口的使用:为响应用户请求,前端组件调用Lookup()以确定要使用哪个版本的后端组件。Lookup()返回固定数量的用户定义跟踪标签之一。Lookup()保证为同一个用户session推荐相同的tracktags,保证后续对后端的调用都会发送到同一个版本。然后,前端服务将其请求发送到推荐的后端,使用轨道作为路由键。当前端稍后计算用户会话的业务指标时,它可以安全地与推荐的后端版本相关联。WriteMetric()可用于执行此操作,无需前端跟踪到后端版本的映射。由于Lookup()返回一组固定的轨道标签中的一个,因此前端服务必须配置为将流量路由到一组固定的服务-每个轨道一个。版本映射以跟踪随时间的变化,并使用部署的Kubernetes对象上的标签作为候选版本部署的一部分来完成。很明显,SDK的引入对应用前端组件的开发者提出了要求。此外,该方法依赖于前端应用程序可以根据一组轨道标识符路由后端请求的假设。这意味着对应用程序配置的要求。我们通过示例演示使用Iter8SDK是多么容易。Iter8SDK使用gRPC实现。ProtocolBuffers文档中描述了这些接口。从中,可以为各种语言生成特定于语言的代码。接口本身由A/B/n服务实现。A/B/n测试的应用程序开发我们将考虑一个简单的两层应用程序。用Node.js编写的前端代表一个在线商店。用Go编写的后端表示前端调用的推荐引擎,用于向用户展示替代产品。可以在此处找到此应用程序的完整源代码。前端组件支持两个接口:/getRecommendation请求产品推荐。/buy完成购买。作为购买(请求)的副作用,/buy计算用户会话的业务指标。/getRecommendation接口的实现依赖于后端推荐服务。我们想在此后端推荐服务上运行A/B/n测试。Iter8SDK使用gRPC实现。ProtocolBuffers文档中描述了这些接口。从中,可以为各种语言生成特定于语言的代码。我们生成的代码在示例应用程序中,可以直接复制以用于您自己的应用程序。要使用Iter8SDK,需要gRPC和生成的库:vargrpc=require('@grpc/grpc-js');varmessages=require('./abn_pb.js');varservices=require('./abn_grpc_pb.js');并实例化一个客户端:varclient=newservices.ABNClient(abnEndpoint,gprc.credentials.createInsecure());此客户端用于两个用例:调用后端服务之前和编写指标小时。调用后端服务在调用后端推荐服务之前,需要先调用Lookup()方法。返回的trackidentifier应该作为一个索引来选择发送请求的路由。在我们的前端示例中,这可以实现如下://track映射到路由到后端服务consttrackToRoute={"default":"http://backend:8091","candidate":"http://backend-candidate:8091",}...//识别默认routeroute=trackToRoute['default'];...varapplication=newmessages.Application();application.setName('default/backend');application.setUser(req.header('X-User'));client.lookup(application,function(err,session){if(!err){//使用由推荐轨迹决定的路由route=trackToRoute[session.getTrack()];}//使用session.getTrack()作为端点列表的索引调用后端服务http.get(route+'/recommend',...)}在此实现中,用户会话从请求标头X中提取-用户请注意,如果与Iter8SDK交互有问题,会选择默认路由,完整的示例代码可以在这里找到编写指标当/buy调用接口时,表示销售完成,计算业务指标。在我们的示例应用程序中,一个随机值被分配给指标sample_metric://exportmetricvarmv=newmessages.MetricValue();mv.setName('sample_metric');mv.setValue(random({min:0,max:100,integer:true}).toString());mv.setApplication('default/backend');mv.setUser(user);client.writeMetric(mv,function(err,session){});!无需进一步更改,无论运行了多少次A/B/n测试。可以在此处(Node.js)找到完整的示例前端代码。Python和Go中提供了替代实现。与节点示例一样,生成的代码可以直接复制到您自己的应用程序中。部署应用程序进行A/B/n测试在部署要进行A/B/n测试的应用程序组件时,唯一的要求是添加Iter8A/B/n服务用于标识组件版本的标签。在我们的例子中,我们计划只测试后端推荐组件。Iter8SDK要求每个版本部署的资源实例中至少有一个包含如下标签:app.kubernetes.io/name:应用(组件)名称。app.kubernetes.io/version:版本名称。iter8-tools/track:用于此版本的轨道标签。iter8-tools/abn:指示版本是否准备好接收流量的标志。作为说明,可以使用以下命令手动部署示例应用程序。首先部署前端网店组件:kubectlcreatedeploymentfrontend--image=iter8/abn-sample-frontend-node:latestkubectlexposedeploymentfrontend--name=frontend--port=8090接下来部署示例后端推荐component当前或默认版本。我们将所需的标签添加到部署对象中,因为我们想在此组件上运行A/B/n测试。kubectl创建部署后端--image=iter8/abn-sample-backend:latestkubectlexpose部署后端--name=backend--port=8091kubectl标签部署后端app.kubernetes.io/name=backendkubectl标签部署后端app.kubernetes.io/versinotallow=v1kubectllabeldeploymentbackenditer8.tools/track=defaultkubectllabeldeploymentbackenditer8.tools/abn=true最后部署Iter8服务(如果没有部署的话):helminstall--repohttps://iter8-tools.github.io/hubiter8-abniter8-abn\--set"resources={deployments,services}"\--set"namespaces={default}"运行A/B/n测试我们现在准备运行A/B测试,比较后端推荐组件的当前部署的默认和新发布候选。运行测试有两个步骤。第一步是部署组件的一个或多个发布候选版本。使用我们的示例应用程序,我们展示了部署后端推荐引擎的候选版本所需的步骤。待候选版本部署完毕后,Iter8Service会开始将分配给它的tracklabel发送给前端服务。作为响应,前端商店将开始向新版本的推荐引擎发送请求。第二步是启动Iter8实验来评估收集到的指标。我们演示了一个多循环实验-一个定期执行直到被删除的实验。在每次执行时,可以以任何方式部署候选部署-手动(如我们所示)或使用CI工作流。如上所述,必须将所需的标签添加到至少一个Kubernetes资源对象中。在我们的示例应用程序中为后端推荐服务部署候选版本:kubectlcreatedeploymentbackend-candidate--image=iter8/abn-sample-backend:latestkubectlexposedeploymentbackend-candidate--name=backend-candidate--port=8091kubectllabeldeploymentbackend-candidateapp.kubernetes.io/name=backendkubectllabeldeploymentbackend-candidateapp.kubernetes.io/versinotallow=v2kubectllabeldeploymentbackend-candidateiter8.tools/track=candidate部署候选版本时一定要注意确保在前端向它发送任何请求之前完全初始化候选版本。这可以通过仅在版本候选完全初始化后设置iter8-tools/abn标志来确保。初始化后,Iter8A/B/n服务将开始提供响应Lookup()请求的版本。kubectllabeldeploymentbackend-candidateiter8.tools/abn=true这些步骤如下图所示。最初,仅部署默认版本v1并接收来自前端的所有流量。在部署候选版本v2时,前端继续将所有请求发送到默认版本。一旦发布候选准备好接收流量,例如,当pod就绪时,设置标签iter8.tools/abn。这会触发Iter8服务开始向前端推荐它,而前端又开始向两个版本发送请求。在我们的示例应用程序中:kubectllabeldeploymentbackend-candidateiter8.tools/abn=true实际上,测试取决于应用到前端服务的用户负载。在本教程中,我们使用将请求发送到存储端点/getRecommendation和/buy的脚本来应用负载。用于将本地请求转发到集群:kubectlport-forwardsvc/frontend8090:8090并为不同用户产生负载;例如,对于用户foo和foobar:curl-shttps://raw.githubusercontent.com/iter8-tools/docs/main/samples/abn-sample/generate_load.sh|sh-s---ufoocurl-shttps://raw.githubusercontent.com/iter8-tools/docs/main/samples/abn-sample/generate_load.sh|sh-s---ufoobarstartIter8experiment启动Iter8experiment,周期性读取MetricsWriteMetric()写入的业务。可以使用预定义的abnmetrics任务:iter8klaunch\--set"tasks={abnmetrics}"\--setabnmetrics.applicatinotallow=default/backend\--setrunner=cronjob\--setcrnotallow="*/1****"此命令启动Iter8实验,该实验运行预定义的abnmetrics任务以读取在默认命名空间中运行的后端应用程序组件的记录指标。使用cronjobrunner意味着实验将根据cronjobSchedule定期运行(在本例中为每分钟)。实验结果将随时间更新。检查实验结果并决定是否推广候选版本。第一份报告将在实验任务首次运行后(大约一分钟)可用。示例报告如下:iter8kreport实验总结:******************实验完成:false无任务失败:true任务总数:1完成数tasks:18指标的最新观察值:************************************Metric|候选人|默认-------|-----|-----abn/sample_metric/count|765.00|733.00abn/sample_metric/最大值|100.00|100.00abn/sample_metric/平均值|50.11|49.64abn/样本指标/分钟|0.00abn/sample_metric/stddev|28.63|29.25推广赢家在推广候选版本时,必须注意确保没有用户流量意外发送到正在升级或删除的版本。任何可用于执行促销的方法都应包括以下步骤。还显示了示例应用程序的手动步骤。最初,默认版本和候选版本都从前端接收请求。首先,iter8.tools/abn从与默认版本关联的资源中取消设置标签。这会在过渡期间禁用到默认版本的流量-Iter8SDK接口Lookup()将从其推荐后端列表中删除默认轨道:kubectllabeldeploymentbackenditer8.tools/abn-接下来,使用新版本重新部署对象到默认曲目是关联的。当更新的对象就绪时,添加iter8.tools/abn标签,表明它已准备好接收流量。kubectllabeldeploymentbackenditer8.tools/abn=true此时默认和候选轨道标签都与相同的后端版本相关联。现在可以删除候选发布版。为此,取消设置iter8.tools/abn标签以终止到候选资源的流量:kubectllabeldeploymentbackend-candidateiter8.tools/abn-最后,删除候选资源。kubectldeletedeploymentbackend-candidatekubectldeleteservicebackend-candidate最后的想法我们探讨了进行A/B/n测试的一些挑战,尤其是应用程序的后端服务。关键挑战涉及前端组件计算业务指标,但没有正确地将它们与有助于其计算的后端版本相关联。Iter8SDK使前端能够正确建立这种关联。它通过提供一个查找接口来实现这一点,该接口允许前端服务识别在处理用户请求时要使用的后端服务的版本。这样,它可以可靠地将业务指标分配给后端版本。我们展示了使用Iter8SDK修改前端服务和运行A/B/n测试是多么容易。对前端的一次性更改只需要几行额外的代码。启用候选版本进行测试只需要添加一些标签。试用完本教程后,请使用您自己的应用程序进行试用。