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

火山引擎A-B测试私有化实践

时间:2023-03-22 15:15:22 科技观察

作为ToB市场的产品——火山引擎A/B测试(DataTester)为了满足客户对数据安全和合规问题的需求,探索私有化的产品部署是一条绕不过去的路。在针对ToB客户私有化的实际实施中,火山引擎A/B测试(DataTester)也遇到了字节内部服务和企业SaaS服务不容易遇到的问题。在解决这些问题的实践中,火山引擎A/B测试团队积累了一定的流程管理和性能优化经验。本文主要从业务角度分享目前VolcanoEngineA/B测试的私有化架构,遇到的主要问题,以及解决方案。火山引擎A/B测试私有化架构架构图整个系统采用Ansible+Bash的方式搭建。为了适应小型私有集群的部署,它让每个实例点对点部署,资源复用,达到最小三节点交付的目标。它还可以隔离在线和离线资源以提高集群稳定性。集群可分为三部分:业务服务:主要直接面向用户提供接口或功能服务,如实验管理、实验报告、OpenAPI、数据访问等上层服务的运行,例如支持实验报告的计算引擎,以及为指标创建提供元信息的元信息服务;基础服务也作为基础设施的适配层,用来屏蔽SaaS和基础设施私有化的区别,比如SaaS采用的实时+离线的Lambda架构。为了减少资源开销,适应中小型集群部署,私有化只保留实时部分。计算引擎服务将此与上层屏蔽。不同之处。基础设施:内部团队提供统一的私有化基础设施基础minibase,采用host+k8s相结合的部署方式。minibase适配底层操作系统和硬件,上层业务直接对接minibase。私有化带来的挑战挑战一:版本管理传统SaaS服务只需要部署和维护一套产品供所有客户使用,因此产品只需要针对单个或少数服务进行更新,一个版本特性即可快速推出,无需考虑从零开始。开始构建一组产品。SaaS服务的版本发布周期往往以周为单位,保持每周1-2个版本的更新频率。但是在私有化交付中,我们需要确定一个基线版本,绑定每个服务的次要版本号,保证同版本下各个环境的交付物是等价的,从而降低后续升级运维的成本。通常,基线版本的发布周期倾向于双月一次。版本发布周期由于私有化和SaaS服务在架构、实现、基础等方面存在差异,上述发布节奏会带来一个明显的问题:团队要投入大量的开发和测试人力来集中精力创造历史发布周期中Feature的私有化适配、私有化特性的开发、版本发布的集成测试挤占了其他需求的人力调度。为了将周期内完成的工作分散到特性开发阶段,重新规范了分支使用逻辑,改进了私有化流水线和上线流程,并提前了研发和测试的介入时间。解决方案:1.分支逻辑分支管理SaaS和私有化都是基于master分支发布的,非私有化版本周期中SaaS和私有化没有特殊区分。在私有化发布周期,单独创建一个对应版本的私有化分支,发布完成后合并到master分支。这样可以保证master分支在任何情况下都应该在SaaS环境和私有环境中工作。2.发布流水线功能上线流程在发布流水线内部搭建一个私有化的预发布环境,搭建一套流水线。master分支的mr会触发pipeline,更新SaaS预发布环境和私有化预发布环境中最新的master分支代码,并执行自动和手动回归测试。这样做的好处是促进了特定特性的研发,从技术方案设计层面考虑了不同环境的Diff问题,减少了后期返工的成本。同学的上下文切换成本,SaaS和私有化,都是在Feature开发周期内完成的。挑战二:性能优化VolcanoEngineA/B测试工具的报表计算是基于ClickHouse实现的实时分析。SaaS采用多租户共享多个大集群的架构,资源弹性高,可以在不同租户之间合理复用计算资源。对于私有化,它们大多是小规模的、独立的集群。不同客户同时运行的实验数量从几个到数百个不等。报表观察时间与用户习惯和公司工作密切相关,存在明显的波峰波谷。因此,实验报告延迟输出、实时分析慢等现象更容易在私有化中暴露出来。解决方案:实验报告系统首先介绍一下火山引擎A/B测试产品的实验报告系统。下图的实验报告为例:从上到下,产生实验报告的必要输入包括:分析的日期范围和筛选条件,以选择合适的指标来评估实验的收益。实验版和对照版报表类型,例如:如何定义多日累计分析、单日趋势分析等指标?指标的核心要素包括:1.用户行为产生的事件和属性2.预设的算子3.四种算子是针对某个用户行为,根据算子的规则计算出来的,用四种算术运算组合成一个指标。由此,我们可以大致想象,一个常规的A/B实验报告查询,就是通过实验命中圈出实验组或对照组的人群,分析这类人群在实验期间的指标值。由于A/B的特定置信度计算要求,需要在统计结果中体现方差等其他特殊统计值。所有的聚合计算如求和、PV数等都需要聚合到单独的粒度计算中。模型优化如何区分用户命中了哪一组?集成SDK在调用A/B拆分方法时,会上报一个实验曝光事件,记录用户入组信息。随后的指数计算认为,入组后发生的事件受到了实验版的影响。影响。例如:进入实验版本1的事件A的PV数为2,UV数为1,转化为查询模型:虽然上面的模型最直观,但是存在大量的资源浪费:曝光事件和普通事件存储在一个事件表中,一个大规模曝光事件需要查找第一条记录,扫描的分区数会随着实验时间的增加而增加。暴露事件可能会被重复报告。只有计算口径中的第一次曝光才是有效事件。针对以上问题对计算模型进行一些优化,将曝光事件转化为属性记录到用户表中。新模型的变化是:由此带来的好处是:用户表没有时间概念,数据增长=新增用户增长率,规模可控用户表本身会作为维表引入到原有模型中。在这种情况下,一个lessjoin操作模型在实验指标优??化后已经测试了14天以上。多天的累计报表查询时间减少了50%以上,并且会随着实验时间的增加而增加。.在实施预聚合私有化部署之前,将进行早期资源估算。对于当前资源估算,选择“日活跃用户”和“日事件量”作为主要输入参数。不增加同时运行的实验数量的原因是:首先,我们想简化资源计算的模型。其次,在大多数情况下无法提前预测同时运行的实验数量。但是,这个公式引入了一个问题:资源相同的集群在承载不同数量级的实验时,计算量差异较大。在实验量较少的场景下,目前的数据处理架构是轻量级的,将计算逻辑放在查询端,按需使用指标计算,大大减轻了数据流任务的压力。但是假设集群中同时运行100个实验,每个实验平均关注3个指标加上进入实验的人数统计。在当前的查询模型下,事件表每天至少被扫描100*(3+1)次。对于自定义过滤模板等预计算条件,计算量会成倍增加,直到导致查询任务积累数据和输出延迟。重新观察实验报告的核心元素和指标的构成可以发现:指标、报告类型、实验版本都是可枚举的、可预知的。基于假设检验的指标比较置信度计算需要按人粒度计算方差现有指标算子可以先按人粒度计算(去重除外)。所有指标和实验版本?答案是肯定的:扫描当天的事件数据,根据实验和指标配置计算出一个人级别的指标表user_agg。通过user_agg表可以计算出指标计算所需的UV个数、指标的统计值、指标的方差。如果进一步扩展user_agg表的能力,几乎可以替代原来的表完成实验报告中80%以上的指标计算,同时还支持day级别的时间选择切换、用户属性标签过滤等.修改后的指数计算模型使用经验数据。用户每天平均产生的事件数在100-500之间。聚合模型通过多次扫描当天数据的全表获得1/100-1/500的大小。中间表,后续的索引计算和用户维度过滤可以使用聚合表代替原表参与运算。当然,考虑到聚合本身的资源开销,收益会随着运行实验数量的增加而增加,但当实验数量过少时,可能会造成资源浪费。是否启用它需要在两者之间取得平衡。挑战三:稳定性私有化服务的运维渠道复杂,运维压力大,对服务可用性的要求更加严格。A/B测试中要求最高的部分是分发服务,它直接决定在线用户的版本命中情况。卸载服务本身是为故障而设计的,采用降级策略避免调用链路上的故障影响所有实验结果,牺牲部分实时性,使用多级缓存保证卸载结果仍然稳定单个基础设施离线的极端情况。卸载服务整体架构我们把卸载服务作为一个整体,一共使用了三层存储,分别是服务内存、Redis缓存、关系数据库。实验变更时同时将变更消息写入消息队列,分发服务消费消息队列修改内存和Redis缓存中的实验配置,保证多节点间的一致性和实时性.同时,分发服务额外开启一个协程,以自下而上的策略定期全量更新实验配置数据,防止由于消息队列故障导致配置不更新;把Redis当作Mysql的一个备份组件,任意让其中一个失效,这样即使重启了分发服务依然会继续。可以恢复最新版本的offload配置,保证客户端offload结果的稳定性。总结火山引擎A/B测试(DataTester)脱胎于字节跳动内部工具,融合了字节内部丰富业务场景的A/B测试经验;同时,立足于B端市场,持续通过ToB市场的实践经验沉淀和打磨产品,更好地为内外部客户创造价值。本文是火山引擎A/B测试(DataTester)团队在目前ToB客户私有化实践中的实践分享。本文遇到的破解私有化问题的过程,也是这个产品不断打磨和成熟,从0-1阶段走向1-N阶段的过程。