如何打造支撑百万级构建量的服务系统

程序源代码

共 4527字,需浏览 10分钟

 ·

2020-10-14 18:45



正文如下

本文是第十三届 - 前端早早聊构建专场,也是早早聊第 89 场,来自 淘系前端团队 - 冬鸫 的分享

引言 & 背景

大家好,欢迎大家今天来参与本次“前端搞构建”专场,我是来自淘系前端的冬鸫,我所属的团队负责淘系及集团前端工程研发链路的建设。

今天给大家分享的内容是我们在云端构建服务这个主题上的一些经验和探索,也就是我们内部所称的“云构建系统”。

构建,是前端同学日常开发中的老朋友了,我敢打赌今天的听众朋友中一定有不少是 Webpack 高级配置专家的,当我们提到 “构建” 这个词时,他可以代表多重意义:

  • 构建过程:输入源码,输入产物的过程
  • 构建工具和生态:webpack,rollup,以及 babel, ts 等
  • 构建工具的“集成”:create-react-app,vue-cli,团队共用配置等
  • 构建服务环境:jenkins,travis ci,gitlab ci 等

构建概念的变化,基本上是随着项目和公司的规模扩大而变化的,当项目很小,一个人,本地构建就能完成目标,使用的也是社区现成产物,随着项目变多,构建会很快成为一个团队做前端工程化的切入点,随着团队规范和统一构建配置的产出,构建工程化也算完成了它的历史使命。

然后有一周末,后端突然打来电话,线上出问题了!说要一个两周前的版本回滚,你得回公司,开电脑, git checkout, npm run build,构建完成估计还有几分钟,赶紧内网冲浪朋友圈分享,好不容易敬业一回。发完回来一看,构建失败,node 版本不匹配,一拍脑门,上周升了个 node 版本,赶紧降回去,再 npm run build,泡杯茶压压惊,回来一看,less loader not found,node_modules/a/node_modules/b/node_modules/c minify error 全出来了。

我们会进一步思考,有没有一个平台能解决这一类的问题?云构建来了。

系统设计

这时候,构建的概念终于变成了“前端构建的服务化系统”,也就是我们今天的主角,前端云构建系统。那么云构建到底是什么?

本质上非常简单,就是把本地的构建过程搬到云上,这样做有什么好处呢?

  • 环境纯净,不会有 node 版本的问题
  • 构建逻辑标准化、中心化,构建的逻辑与代码逻辑是拆分的,整体构建流程更可控
  • 回滚产物溯源,以前所有的版本都能快速获取
  • 立足前端生态,构建是前端的构建,专注于前端才能伺候的更好。
  • 满足不同团队的需求,无论是 webpack ts js 还是 c++ 自己撸了一个构建器,都能兼容
  • 支撑本地&云研发,线上线下用同一套系统

我们已经累计运行了上百万次构建,活跃的构建器有上百个,除了传统的前端仓库构建外,还服务了搭建、低代码链路的构建需求,工作日的日构建量在一万五左右。

回到标题,那我们是如何支撑这么大量的构建任务,并同时满足不同团队间的需求呢?

先来看一下整体的系统设计:

  • 左侧是接口,SDK,负责将系统能能力开放给三方平台
  • 上层部分,一部分是平台的基础能力,主要服务于构建的发起用户,同时承接构建服务能力的相关工作,如产物下载,日志分析等,另一部份则是服务构建器的开发者,为其提供开发、测试、上线、灰度等服务,我们后面会详细解释构建器的整体设计。
  • 系统底层是基于容器调度的任务运行系统,负责构建任务的调度与资源分配,可以看到我们还有一个构建运行器,它运行在底层的 docker 容器中,是运行构建过程的核心,运行器在执行构建逻辑的同时也负责构建相关数据的记录与上报,比如结果上传,依赖数据扫描,阶段计时等等。

一个构建任务的运行逻辑非常清晰,从三方调用至构建服务,构建服务进行一些准备逻辑,比如构建器版本的确认,配置读取等,再调用任务调度平台进行运行,他们之间通过实时的 Websocket 或 HTTP 请求、回调进行沟通。

构建系统支持的两种方式的构建,一个是命令构建,感觉就和运行一段 Shell 命令一样,自定义程度高,接入成本极低,比如可以在仓库中配置中写一个 tnpm run build,接入就基本完成了。这方便一些小项目,小团队快速从本地构建转移到云端构建上去,直接享受平台提供的一些内能。除此之外就是通过构建器进行构建。

接下来先给大家讲讲构建器的设计。

构建器

构建器逻辑

构建器干了什么事呢?本质上就是完成了构建逻辑与代码逻辑的解耦,也就是把项目间构建的依赖、配置和自定义构建逻辑从项目中抽离出来。

我们基于构建的场景,定义了构建器的接口,一个是本地的开发态下的构建,别一个是生产态下的构建逻辑。

开发态逻辑在本地调试时执行,生产态逻辑主要在云端执行,通过这样的设计,我们完成了构建器的标准化。继而可以进一步基于构建器这个概念来提供更多能力。

有一些同学会觉得,我直接把构建逻辑抽出来发个 npm 包,项目中依赖一下不就可以了么?

但如果今天你是 rax 的构建器负责人,你肯定不敢随便发布,发个 minor 吧还怕出 bug 影响项目的构建,发个 breaking change 也免不了那些把版本标记成星号的狂野用户,更可怕的是,你没有他们代码的 git 权限,想拿几个实际项目测都测不了。为了让你发 npm 包没有顾虑,我们为构建器提供了两个不同的更新通道:正式通道和灰度通道。

构建器版本控制与灰度

构建器开发者可以在平台上更新不同的通道,同时可以指定用户或者指定用户比例来进行灰度,系统会通知到用户他被开启了构建灰度,用户也可以主动体验新版本的构建器。

构建平台对版本的指定将成为唯一的版本确定来源,无论是本地的调试构建还是线上的云构建,都会受到平台的控制,出了问题一键回滚即时生效,这回 rax 开发者终于能安心喝茶了。

构建器持久化

有一个调侃前端工程师的图大家都见过,其实这里最重的真不是 antd,人家顶头了也就几十兆,最重的明明是 ts babel,还有一安装就要编译的 Sass。

但是有了构建器就不一样了,当构建逻辑本身被抽离之后,构建器的依赖也同时自然的从项目中剥离(估计一大部分的 devDependence 都被从项目中拿走了),所以我们将构建器及其依赖在构建机上做了持久化,更新的时机与开发者更新的时机保持一致,这样做还可以保证构建器的依赖不会频繁变动,社区包更新也不怕出问题了。

跟据我们的观察,这样做能平均减少 10% - 50% 的构建总时长。

构建通用服务

我们说云构建是为前端开发者服务的,光有一个构建器是可不够,我们在平台上可以为构建器以及构建命令的使用用户提供一些通用能力,来辅助他们进行问题的定位以及构建效率的提升,这里给大家介绍两个用户不用配置就直接可用的平台功能。

Sourcemap 提取

先来看 Sourcemap,这是一个让人又爱又恨的功能,开了之后线上调试那叫一个爽,但是就怕哪天有人在 Sourcemap 解析代码中发现我骂老板,这是很大的安全隐患对不对。

Sourcemap 要想正确生效,构建的时候就需要一并产出,差一个字母都不行,因为安全问题又不能发上线,这个时候云构建就可以帮项目做这个事了。

构建后,我们会在产物中搜索 map 文件,并记录到系统中,用户或平台可以选择在最终产物中删除这些 map,来保证在发布的时候这些 map 不会泄露到外部造成一些安全问题。被删除的那些 sourcemap 文件会在内网中提供入口,给三方平台和用户使用。淘系内部的一些面向前端的混沌攻击系统就使用了这个能力来进行模拟攻击。

node_modules 扫描记录

大家都知道 npm 的 semver 原则,同样的代码今天构建和明天构建,他实际的依赖可能发生变化,这个变化有可能会带来一些问题,比如我们刚才讲过的构建失败,甚至产物受到影响,或者出现一些逻辑问题和安全问题,类似前一段时间 is-promise 更新事件,所以产出构建产物的这次构建的实际依赖是有必要记录下来的,其一可以帮助定位问题,其二可以在发生一些安全风险时快速提醒相关的项目。

大体逻辑是在构建后进行扫描,记录,再通过数据清洗的方式得出相关的依赖关系,扫描逻辑可以看一下业界的一些实现,比如 npm 7 使用的 arborist。之后用户就可以查看每次构建的依赖情况,出现了不知道原因的逻辑问题时,也可以通过比较依赖树来定位问题。同时当一些包出现安全隐患或暴露已知问题时,我们也可以快速得到相应的构建记录,来提醒同学注意修复问题。

数据相关的逻辑我们是和蚂蚁合作产出的,有兴趣大家可以关注下 陆沉 老师带来的关于构建数据相关的分享,一定非常精彩。

任务系统

任务系统是我们支撑 2000 每小时的构建任务并发的核心,它负责容器的调度、基础镜像的管理以及管理日志、结果。这套系统现在已经云构建中剥离出来了,除了负责构建相关的任务外,还同时会承载一些其他工程相关系统任务的运行,比如代码质量的检查,仓库分支的合并等等。在物理机或 ECS 上会有负责控制、发现机器的 Client,他同时也管理了我们上文提到的构建器的缓存逻辑

架构上,我们主要使用 docker 及其提供的相关 API,在之上标准化了一些日志、结果获取的能力,通过与 docker server 直接进行加密链接,就可以拿到实时的日志和容器中产出的文件了。

团队介绍

  1. 归属的团队是淘系工程团队,实际上不止服务着淘系近 300 左右的前端同学,也服务着集团每周近 3000 位的前端及非前端研发的同学
  2. 整体的平台体系,底层依赖了非常多的底层服务,有内部体系的,云上资源的
  3. 团队小伙伴每天支撑的应用体系内容大致分为 基础服务能力工程应用产品
    1. 基础服务,依托于前面提到的底层任务调度系统,以及上层的云构建,依然存在着非常多的产品体系,以资源检测为代表的门神系统、以脚手架仓库初始配置为代表的云脚手架系统,以及面向工程研发流程定制能力的流程引擎系统;
    2. 除此之外,我们也维护着整个阿里经济体体系内,最大的源站回源体系,作为经济体内主要业务及双促业务的资源、页面基础服务重要一环
    3. 在基础服务能力之上,从用户产品的维度提供了支撑前端研发应用持续部署的工程研发平台,本地 CLI 研发工具解决方案工程套件,以及给予以及支撑面向上层体系内平台定制、自建场景的开放平台
    4. 以及去年在阿里举办的 D2 前端分享会中提到的 IDE 体系也正在紧锣密鼓的建设推进中
  4. 作为集团前端研发的基础设施,在整个体系内也参与、主导了非常多的横向体系,例如面向前端研发的安全生产治理、IDE 底层体系等等
  5. 平台在几年的建设下来,以及支撑起集团体系内的绝大多数 BU 的核心业务体系
  6. 欢迎敢想敢做的同学联系我们交流

书:人类起源的故事

加微信 codingdreamer ,备注进群,加入大会专属内推群,及讲师团队钉钉群

扫码关注公众号,订阅更多精彩内容。



你点的每个赞,我都认真当成了喜欢


浏览 31
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报