干货 | 从零打造携程无线持续交付平台 MCD 实践
刘李丰,携程无线基础工程团队高级经理,负责无线交付平台和基础服务研发。十多年的互联网从业经验,曾供职于 eBay 等互联网公司,研发经验丰富。
目前携程大部分订单已来自移动端,App 几乎承载了整个集团的所有业务形态。在内部研发中,携程的 App 已经发展成为拥有 90+ Native Bundle、100+ Hybrid Bundle、30+ React Native Bundle,几百名研发人员,每个版本(1 个月)交付 4000+个 App 包,Hybrid/ReactNative/HotFix/Bundle 发布次数 500+。
如果没有一个有效的无线持续交付平台,很难实现大版本的集成发布在 3 天内完成。而对比市场上开源的无线持续集成工具 Fastlane、TestFlight、Jenkins 都存在各种定制化需求的问题。因此我们从零开始,逐步打造适合携程研发流程的无线交付平台,系统化地解决研发支撑痛点。
下面将从集成、测试、发布、运营四个子平台来展开,具体分享我们是如何一步步打造无线持续交付平台的。
一、集成平台
从最初到现在,携程无线持续交付模型经历了从 1.0 到 2.0 的迭代演进。
在 1.0 之前,App 集成和发布还主要依靠人工操作 Jenkins,需要由特定的打包人员负责打包,再将包通过 IM/邮件等方式传递给其他测试人员,测试结果需要专人手工回收,以把控 App 质量。此时最大的问题就是 App 管理混乱,人工介入过多,每次发布都需要花费很长的时间。
1、1.0 阶段
在 1.0 阶段,我们引入 MCD(MobileContinues Delivery)平台思路,将打包人员的工作交给平台来完成,提高了发布工作效率。这时不需要专职人员负责出包,平台会定时自动打包,测试人员可以到平台上自由取包(通过下载链接或扫描二维码的方式)进行测试。
与此同时,测试人员也可以在平台上进行单独的打包和测试。测试结果会统一反馈到平台上来,协调人员在平台根据各家的反馈结果决定需要重新出包还是继续下一步发布操作。
在这个阶段,App 的打包还完全依赖于源代码进行,由平台生成打包参数(主要包含 App 运行环境、与 iOS 签名相关的参数以及代码仓库相关的参数)提交给后台的打包系统。它会根据仓库地址和commit ID 从代码仓库中拉取全量代码,然后打包系统再调用代码中预先准备好的 Build 脚本构建 App 产物,构建完成后将结果保存至临时的文件服务中,最后由平台的回收进程将构建结果回收并处理之后放在云存储上供用户下载使用。
2、2.0 阶段
1.0 阶段虽然已经基本实现了集成打包的自动化,但是还存在以下几点不足:
源码打包方式效率低下,每次都要从代码仓库中下载全量代码,再通过源代码生成 App 产物。这样做使得每次 Build 时间都很长,而打包次数的增加会导致某些打包任务积压,系统不能及时出包。
编译容易失败,任何一个 Check-in 导致的编译失败,就会致使系统不能正常出包。
系统之间采用轮询模式,Job 任务扩展性差。
MCD 系统发起 Build 请求之后会有另一个定时的 Job 任务去轮询 Build Server 查看 Build 结果。在初期阶段还能满足业务需求,但是后来由于打包数量的增加以及打包频率的提高,系统的处理效率变得越来越低。
一方面打包资源不够(Android 打包使用 Linux 虚拟机,iOS 打包使用 Mac),另一方面轮询Job 的处理效率达到了瓶颈。打包机器采用 Jenkins 方式进行管理,因此很容易进行横向扩展,但是 Job 却很难扩容。
针对以上问题,我们对平台进行了升级改造,主要为:
引入消息机制,提高系统吞吐量;
将工程进行拆分,按照 Bundle 的方式进行打包。
消息机制的改造:
首先基本打包架构和流程保持不变,在 MCD 系统和 Build Server 之间增加消息系统,MCD 发起 Build 之后不再轮询Build Server,而是消费由 Build Server 产生的 Build 完成消息,如图 1 所示。使用这样的生产消费模型 MCD 可以轻易地进行水平扩展,系统执行效率得到极大改善。
工程拆分:
App 工程拆分成多个不同的 Bundle 模块,Bundle 之间存在依赖关系。在这个情况下 App 的编译和打包也可以按照 Bundle 的方式进行组织。
在开发阶段,开发人员提交完代码之后就能直接将自己负责的 Bundle 编译成可执行文件,测试可以自由选择 Bundle 进行打包。此时打包工作只需将多个 Bundle 文件组装在一起就行,极大地加快了打包速度。通过测试的 Bundle 会被发布到指定地点,在 App 集成阶段只需把所有发布的 Bundle 组装成最终 App 供大家测试即可。
在开发自测阶段,开发人员提交完代码后在 MCD 平台上 Build 出所开发的Bundle(标记为 L,表示 Latest)。相关测试人员(或开发人员自己)可以打测试包,测试包会使用所有 Bunde 的 L 版本进行构建。构建完成之后获取测试包进行功能测试,测试通过后就可以将该 Bundle 进行发布操作,即标记为 RC 版本(表示 Release Candidate)。
图 2 测试打包发布
如图 2 所示,在集成测试阶段,MCD 平台会自动选取已发布(RC 版本)的 Bundle 打出集成包,测试完成后测试人员可以将测试结果反馈到 MCD 平台中,待测试全部通过之后就可以安排 App 进入发布定版阶段,再进行一些后续市场包操作就可提交给 App 应用市场供用户下载。
由于 App 的开发是个连续过程,测试包和集成包都可以很方便地配置是需要 Hourlybuild 还是 Daily build,并且可以和测试平台的相关功能联动,从而实现持续集成。
我们也会将不同版本(不同功能)的 App 按照项目进行划分,主版 App 为标准项目,非主版(渠道/HotFix/Bundle)App 为非标准项目,同一项目内的 Bundle 可以自由组合,项目之间互相独立,不受影响。一个项目在创建时可以选择从另一个项目继承其各模块的最新版和发布版。项目在开发过程中也可以从其父项目中实时同步对应模块的最新版和发布版,这样就能基本满足各种开发和测试的需求。
二、测试平台
随着携程持续交付的发展,原有的测试模式以及流程渐渐显现出不足,主要体现在以下三点:
快速验证与手工验证的冲突;
自动化回归测试与手动测试效率的矛盾;
设备兼容测试与设备不足的矛盾。
1、快速验证与白屏检测
在集成测试阶段,每一次出包首先会由基础测试人员负责验证此次出包的质量,主要是验证各个 BU 入口页面 Hybrid 或直连页面是否正常,如果大量 BU 入口页面异常则会要求重新出包。这个过程看似很简单,但是“(沟通成本+验证成本)*出包次数”所耗费的时间也是不容忽视的,且这种冒烟测试对于测试人员也很枯燥无趣。
图 3 集成包
针对以上情况,最初引入了白屏测试等基础性测试功能,并与集成平台打通(见图 3,可以直接从集成包列表选择相关测试功能)。
白屏测试主要是检测 App 首页各个入口页面是否显示正常,对于每一个页面会进行图像比对以及页面长度来验证页面是否正常,白屏会在多个设备进行测试,只要其中一台设备通过,则认为这个入口是正常的。
2、自动化回归测试
通过回归测试自动化,可以提高回归测试效率,有效缓解测试人员的压力。我们逐步开发了 MCD 测试平台用于自动化测试项目的管理、执行、报告和展示,支持Android/iOS App、HTML5/Hybrid Testing。
测试平台架构简化图如图 4 所示,各个模块主要功能如下:
Portal 负责与用户交互,查看报表,保持后台系统对用户透明;
Invoker 是调度通道,用户基于 Portal 提交的测试任务通过此通道来调度,包括创建项目、设备占用、测试项目执行等;
Lab Center 负责所有与设备相关的业务,Lab Center 与二次开发的开源软件 STF 交互,所有设备挂载在 STF 上;
当具体的测试项目在 Jenkins 上执行时,会将预先占用的设备远程连接到 Slave 上等待使用。
MCD 测试平台提供了自动化测试(白屏检测、录制回放、回归测试)、系统测试(兼容性、性能、稳定性)、手动测试(远程租用、远程调试)三大类功能,基本达到了我们对于提高测试效率的需求,如图 5、图 6 所示。
图 5 Mobile 项目列表
图 6 自动化测试 Task Case 页面
另外,日常测试中还经常遇到这些情况:需要快速验证 App 的一个功能,有些问题只能在特定机型上重现,测试目前却没有这款手机。因此我们基于STF 二次开发出设备共享平台,将空闲设备收集起来在平台上共享,用户只需在平台上搜索需要的设备就可以立刻通过STF 进行使用了,如图 7 所示。
图 7 设备租用
同时,我们还建立了代码质量的自动化,集成 Sonar、Facebook Infer 和 Uint Test 功能,方便每个版本对代码质量进行跟踪,如图 8 所示。
图 8 代码质量自动化追踪
三、发布平台
无线发布系统一直以来都是无线应用能快速迭代最关键的一环,一个好的发布系统能帮助开发快速地修复线上问题,并能快速将新功能投送至用户,帮助业务抢占市场,而发布系统的设计也随着 CD(Continuous Delivery)概念的深入人心而日新月异。
无线发布的特点是静态资源包(不管是 Hybrid、React Native 还是 HotFix、Bundle),发布技术中涉及到的主要问题如下:
如何最大限度地把静态资源的下载流量降到最低,以减少用户对应用更新的感知度,提高应用升级的成功率和用户体验。
灰度发布,保证发布的质量。
1、资源包发布
针对静态资源包发布,经历了如下几个历程:
全量包发布: 携程最初使用的都是全量发布的方式,这种方式实现简单,但也简单暴力,而问题也比较明显,导致用户升级时流量巨大。
文件字符串差分: 这种方法避免了整包差分的问题,能够比较方便地实现快速迭代要求下的小量更新需求,并且文本差分工具简单,实现容易,出错较少,但缺点是差分效果差。
文件二进制差分: 对每个文件进行二进制级别的差分能够具备小量快跑的特点,同时进一步优化差分文件 Size。这也是携程目前使用的方式,它仍然使用了 bsdiff 作为差分工具,当文件没有更新时,不输出差分文件,而更新时也只产生 Size 非常小的 diff 文件。
按需差分: 在按需差分前,我们采取的是预差分方案。就是在发布时,把所有的版本间差分准备好,有多少个历史版本就将产生多少个差分包。按照这种做法,随着发布次数的增多,发布差分包数量也将急剧增长,对存储的要求越来越高,也造成了浪费。
在此前提下,引入了按需差分,就是终端用户通过将自己的版本号与线上最新版本号做比较,发现落后时,触发差分过程,从而获取增量更新。
这里会有一个问题——终端用户第一次访问时没有差分包,我们的做法是:第一次只返回全量包,用户不用等待,但是后续用户会拿到差分包。
另外一个问题是,避免重复打差分包。在计算时发现无差分包,会触发差分包过程,同时会把这个信息缓存(过期时间半个小时)。当其他客户端同样的版本请求时,会先判断缓存,没有的话进行差分操作,有的话直接下发全量包。
2、灰度发布
网站发布通常是有灰度发布的过程,无线的发布跟网站发布流程是类似的,但是做法有差别。二者的区别是:网站是 centralize 的,可以统一控制,而无线的发布是 decentralized。无线发布包,像 Hybrid、React Native、HotFix 等静态资源,是需要客户端一个个来拉取的。那么,如何确保整个发布的流畅,确保发布质量呢?我们的解决方案如下:
Offline 灰度发布流程
灰度第一步是冒烟,网站的冒烟是导一部分流量到某台 Server 上,而我们则是通过配置一个白名单,只有客户端在白名单中的才会获取到最新发布。当测试没有通过,可以选择终止,然后再决定是否需要回滚,测试通过决定是否需要扩大比例。
监控发布数据
当客户端收到下载物后,会把当前的下载信息上报上来,供发布人员进行监控查看。
灰度发布设计中需要注意:
避免一直小白鼠:为了避免一个用户一直当小白鼠,每次灰度需要利用灰度 ID+客户端信息进行取模运算。
灰度回滚:灰度回滚时只需要回滚已经更新这次灰度的客户端,对于没有更新到的,则无需回滚。
四、运营平台
运营是研发流程的最后一环,携程的 MCD 平台思路是能够针对发布结果、运营情况以及配置等功能有一个集中的管理。目前包含了运营看板、性能看板、发布看板、无线配置、崩溃管理、App Size 统计等模块。
其中无线配置经历了多次迭代,对于各种配置项,从版本、平台、渠道、A/B 多个角度进行了定义,做到灵活动态配置,需改就改,粒度大小自由配置。下发时根据App 的版本、平台、渠道等信息挑选符合条件的配置进行发布。需要修改的时候也是先修改测试环境配置,测试通过后再同步到生产。配置分为专用版和通用版两种,专用版只会下发给特定的渠道/版本,通用版以版本号为基准,在不发布新版本配置的情况下系统默认给 App 端下发最近的通用版配置。当同时存在符合条件的专用版和通用版的时候,只下发专用版配置。
崩溃管理也是无线运营的重要一环,尽管目前行业内已经有崩溃管理功能强大的平台,但由于定制性需求我们也搭建了自有崩溃管理功能,方便快速发现线上问题,从而能够通过 HotFix/Bundle 方式及时修复,如图 9 所示。
图 9 崩溃管理页面
五、结束语
以上是携程无线基础工程团队在无线持续交付方面的一些工程实践,MCD 的核心目标是能够对集成、测试、发布和运营的完整生命周期提供更好的平台支撑和管理。虽然取得了一些成果,但还有许多提升空间,例如多应用支持、支持条件发布等,无线持续发布之路依然有很多工作要做。
推荐阅读: